Skip to content

feat: add support for inheriting properties from workspace groups#346

Draft
vytautas-astrauskas-sensmetry wants to merge 5 commits into
mainfrom
feat/workspace-groups
Draft

feat: add support for inheriting properties from workspace groups#346
vytautas-astrauskas-sensmetry wants to merge 5 commits into
mainfrom
feat/workspace-groups

Conversation

@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator

@vytautas-astrauskas-sensmetry vytautas-astrauskas-sensmetry commented May 21, 2026

Adds groups to .workspace.json:

{
  "projects": [
    {
      "path": "project1",
      "iris": ["urn:kpar:project1"]
    }
  ],
  "groups": {
    "kerml": {
      "project": {
        "version": "1.0.0"
      },
      "meta": {
        "metamodel": "https://www.omg.org/spec/KerML/20250201"
      }
    }
  }
}

that enable inheriting the specified properties in .project.json and .meta.json files:

{
  "index": {
    "A": "a.sysml"
  },
  "created": "2025-10-31T17:01:00.414506000Z",
  "metamodel": { "workspace": "kerml" },
  "checksum": {
    "a.sysml": {
      "value": "",
      "algorithm": "NONE"
    }
  }
}

Signed-off-by: Vytautas Astrauskas <vytautas.astrauskas@sensmetry.com>
@consideRatio
Copy link
Copy Markdown
Collaborator

I havn't heard discussion leading up to this about this feature, but is this worth the complexity and priority to add now and maintain long run?

@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator Author

I havn't heard discussion leading up to this about this feature, but is this worth the complexity and priority to add now and maintain long run?

It is necessary for publishing the standard library, which soon will have to be updated regularly (likely almost every month). Also, I expect other users to use workspaces extensively and benefit from being able to declare things in a single place.

@consideRatio
Copy link
Copy Markdown
Collaborator

Okay! Im squeezing in this review input from mobile, but these kinds of considerations are key to me:

  • UX intuitivity (prompt / response below)
  • Feature scope: is there something we must not influence to avoid spiraling conplexity etc? Related to Provide explicit scope of felevance for sysand.toml and workspace.json #347
  • Docs are essential for PR review and long term maintenance, but of course also for users. There is a docs entry about workspaces already that should be updated to onboard people to this functionality
UX prompt / response

#346

I'm thinking the UX of this config option is questionable, i want intuitive naming and such, can you consider the what you think is a great UX with regards to structure, naming, behvaior etc (not implementation).


I'll take a look at the PR to understand what config option we're talking about.

Here's my read on what the PR is actually doing, then where I'd push the UX.

What it does: a "group" is a named bundle of default properties that member projects inherit. The workspace declares groups.kerml, and a project opts in by replacing a field's value with a pointer: "metamodel": { "workspace": "kerml" }.

The two things I'd change:

1. The per-field pointer is the weak point. Opting in by overwriting each field's value with { "workspace": "kerml" } is verbose, overloads the value position, and means inheritance is decided field-by-field (you could pull version from one group and metamodel from another — flexible, but a muddy mental model). I'd replace it with one project-level key:

{ "extends": "kerml", "index": { "A": "a.sysml" }, ... }

extends has strong precedent (tsconfig, eslint) and reads as exactly what it does. The project inherits everything from the named preset; anything it declares locally overrides.

2. Naming is inconsistent and misleading. It's defined as groups but referenced as workspace — two words for one concept. And "group" implies a set of projects bundled together, not a set of shared properties. I'd call the bundle a preset (or profile), and use that same word on both sides. The project/meta split inside it is defensible since it mirrors the .project.json / .meta.json files a user already deals with — keep that.

So the shape becomes: workspace defines presets.kerml = { project: {...}, meta: {...} }, project says "extends": "kerml".

Behavior to pin down explicitly (intuitive defaults in brackets): precedence between local and inherited values [local wins]; whether extends takes an array for layering [yes, last wins]; and merge depth for nested objects [shallow per top-level key, so a project's checksum map doesn't get half-merged with a preset's].

Want me to sketch the full before/after for both files, or dig into the multiple-inheritance edge cases?

Sketch out

Current design

.workspace.json

{
  "projects": [
    { "path": "project1", "iris": ["urn:kpar:project1"] }
  ],
  "groups": {
    "kerml": {
      "project": { "version": "1.0.0" },
      "meta": { "metamodel": "https://www.omg.org/spec/KerML/20250201" }
    }
  }
}

project1/.meta.json — inherits via per-field pointer

{
  "metamodel": { "workspace": "kerml" }
}

project1/.project.json — separate pointer again

{
  "version": { "workspace": "kerml" }
}

Proposed design

.workspace.jsongroupspresets

{
  "projects": [
    { "path": "project1", "iris": ["urn:kpar:project1"] }
  ],
  "presets": {
    "kerml": {
      "project": { "version": "1.0.0" },
      "meta": { "metamodel": "https://www.omg.org/spec/KerML/20250201" }
    }
  }
}

project1/.project.json — one opt-in, local field overrides

{
  "extends": "kerml",
  "version": "1.2.0"
}

project1/.meta.json — same opt-in, metamodel inherited untouched

{
  "extends": "kerml",
  "index": { "A": "a.sysml" },
  "checksum": { "a.sysml": { "value": "", "algorithm": "NONE" } }
}

Net effect for project1: version = 1.2.0 (local wins), metamodel = the KerML URL (inherited).

Notable shifts

The biggest change is that extends is declared once per file instead of per inherited field — the project says "I'm a kerml project," not "this specific field comes from kerml." Inheritance becomes a property of the project, not of each value.

One open question this surfaces: extends now repeats in both .project.json and .meta.json. Options — (a) accept the small repetition since the two files are independently consumed, or (b) move extends to the workspace's projects entry so it's declared once:

"projects": [
  { "path": "project1", "iris": ["urn:kpar:project1"], "extends": "kerml" }
]

Option (b) is cleaner (single source, the workspace already lists members) but means a project file alone no longer tells you what it inherits — you have to read the workspace. I'd lean (b) for the std-library case where the workspace is the control point, but (a) keeps projects self-describing. Which matters more to you?

@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator Author

  • Feature scope: is there something we must not influence to avoid spiraling conplexity etc?

I will leave this for @andrius-puksta-sensmetry to answer since he has a more in-depth knowledge of Sysand.

By the way, the workspaces are an experimental feature (meaning that they can have breaking changes in any release) exactly to be able for us to figure out such things by testing Sysand in realistic usage scenarios.

@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator Author

  • UX intuitivity (prompt / response below)

The majority of users would take properties not from groups, but from the workspace directly in the same way as we do with Cargo. For example:

{
  "projects": [
    {
      "path": "project1",
      "iris": ["urn:kpar:project1"]
    }
  ],
  "project": {
    "version": "2.0.0"
  }
}

{
  "name": "project1",
  "version": { "workspace": true },
  "usage": []
}

The groups are needed only to support the case where the same workspace contains both KerML and SysML projects, which is likely to be rare (that is my guess, but we need actual usage data).

For this case where there are no groups and all properties are inherited directly, using explicit extends looks a bit awkward and implicitly inheriting things is error prone. Therefore, to me using explicit extends looks like optimizing the edge case at the cost of the main usage case.

What are your thoughts?

@consideRatio
Copy link
Copy Markdown
Collaborator

consideRatio commented May 22, 2026

I suggest:

  1. Rename groups to presets - a more self-explanatory name i think
  2. Document this functionality and that the default preset is based on top level project / meta, while you can define other presets under presets., and the default preset is accessed via true
    EDIT: also, the presets, including the default, are independent - right? A non-default preset isnt building on top of the default etc, right? I think this is worth being explicit about also.
  3. Consider breaking change and requiring "default" instead of true when referencing the default preset (can also be done later, but open an issue if its postponed rather than rejected perhaps). That could improve readability and make things more consistent to implement and explain/document.

I considered suggesting putting the default preset under presets.default as well, but i figure its better to retain it at top level project/meta to avoid significant nesting for the most common situation.

Copy link
Copy Markdown
Collaborator

@consideRatio consideRatio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I havnt reviewed the code, but I also think we shouldn't allocate much time towards it now for this feature. I also dont want the lack of approval stop from merge when you think its fine to merge. 👍 On self evaluating when to merge this work!

@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator Author

1. Rename groups to presets - a more self-explanatory name i think

Done.

2. Document this functionality and that the default preset is based on top level project / meta, while you can define other presets under presets., and the default preset is accessed via true

Done.

   EDIT: also, the presets, including the default, are independent - right? A non-default preset isnt building on top of the default etc, right? I think this is worth being explicit about also.

Currently, the user needs to choose to define either in default or in an explicit preset. Defining a value in both is an error. (We could relax this in the future if we see that this design is annoying.)

3. Consider breaking change and requiring "default" instead of true when referencing the default preset (can also be done later, but open an issue if its postponed rather than rejected perhaps). That could improve readability and make things more consistent to implement and explain/document.

Done.

Signed-off-by: Vytautas Astrauskas <vytautas.astrauskas@sensmetry.com>
@vytautas-astrauskas-sensmetry
Copy link
Copy Markdown
Collaborator Author

Proper implementation is blocked by #281. Freezing this PR until it is completed.

@consideRatio
Copy link
Copy Markdown
Collaborator

consideRatio commented May 24, 2026

It is necessary for publishing the standard library

We can't get stdlib on index before #281 and this PR thrn, right? Did we require the stdlib on the index?

EDIT: Answer in meet, we don't need the official index, but our own, and this is only blocking the official.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants