Skip to content

feat(shared): instantiate items in calm generate - #3103

Merged
markscott-ms merged 7 commits into
finos:mainfrom
YoofiTT96:feat/2859-generation-items
Sep 17, 2026
Merged

markscott-ms merged 7 commits into
finos:mainfrom
YoofiTT96:feat/2859-generation-items

Conversation

@YoofiTT96

@YoofiTT96 YoofiTT96 commented Sep 11, 2026

Copy link
Copy Markdown
Member

Description

calm generate now honours an answer that selects an items member. Before this, the member was silently dropped — the architect asked for it and the architecture came back without it, with no error.

Part of #2859.

PR sequence

Three stacked PRs, each reviewable alone, in this order:

PR Scope
1 #3059 prefixItems alternatives — the pre-existing gaps that had to be fixed first
2 #3060 items in pattern validation
3 this items in generation

GitHub will not let a cross-fork PR target a branch in my fork, so all three sit on main and each diff carries the ones below it. The commits to review here are the last two.

#3060 must not merge without this PR. On its own it teaches calm validate to accept an items node that calm generate then drops, and feat(shared) cuts a release on merge.

The decision worth knowing

Narrowing promotes. A selected items member is appended to prefixItems and items is deleted, so the instantiator only ever reads positions and never learns what items is. The alternative — teaching the instantiator a second shape — spreads the change across two stages for no gain.

Gotcha

A pattern that pins maxItems to the number of prefixItems entries can never build an items member, and the architecture calm generate produces then fails its own pattern on maxItems. That is an authoring error, and #3060 adds pattern-items-must-fit-within-max-items to refuse it up front rather than have generation quietly rewrite the author's bound.

Tidying

options.ts and the decision validation rule asked the same question of the same shape — does this relationship carry relationship-type.properties.options — one with optional chaining and one with a JSONPath query. Both now call declaresOptions, which #3060 puts beside the helper that reads a unique-id. That adds the first commands/ → spectral/ import in shared; declaration-paths.ts is pattern-shape knowledge that happens to live under spectral/, and the shared reader in calm-models is where both will eventually point.

Also fixed

Three faults reachable without items at all: an array with no prefixItems produced {} where an array belongs, an empty choice list threw on flatMap, and a pattern with no relationships property threw on prefixItems.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Code style/formatting changes
  • ♻️ Refactoring (no functional changes)
  • ⚡ Performance improvements
  • ✅ Test additions or updates
  • 🔧 Chore (maintenance, dependencies, CI, etc.)

Additive. No pattern in the repository changes result, because none of them uses items yet.

Affected Components

  • CLI (cli/)
  • Schema (calm/)
  • CALM AI (calm-ai/)
  • CALM Hub (calm-hub/)
  • CALM Hub UI (calm-hub-ui/)
  • CALM Server (calm-server/)
  • CALM Widgets (calm-widgets/)
  • Documentation (docs/)
  • Shared (shared/)
  • VS Code Extension (calm-plugins/vscode/)
  • Dependencies
  • CI/CD

Commit Message Format ✅

feat(shared): instantiate items in calm generate
refactor(shared): share the decision predicate with validation

Testing

  • I have tested my changes locally
  • I have added/updated unit tests
  • All existing tests pass

Ten new tests across options.spec.ts and instantiate.spec.ts. Each new guarantee was mutation-tested: disabling the promotion, forcing the choice check true, and restoring the old array condition each turn the matching tests red and leave the rest green.

options.ts and instantiate.ts are at 100% statements. shared passes 107 files and 1246 tests, and root npm test is green.

Rebased onto #3060 after the review round on #3059. The shared predicate above replaced two call sites in options.ts, not the one the review named — the second was caught by sixteen failing tests, and forcing the predicate true still turns the decision rule's negative case red.

Checklist

  • My commits follow the conventional commit format
  • I have updated documentation if necessary
  • I have added tests for my changes (if applicable)
  • My changes follow the project's coding standards

An answer that selects an items member now reaches the architecture.
Narrowing promotes the selected members into prefixItems and deletes
items, so the instantiator only ever reads positions and needs no
knowledge of items.

Fixes three faults reachable without items. A pattern whose array has no
prefixItems produced {} where an array belongs. Passing an empty choice
list threw on flatMap. A pattern with no relationships property threw on
prefixItems.

flattenCalmItems is renamed selectDeclarations. Its "items" meant CALM
elements, which reads as JSON Schema items now that both are in play.
generate and the decision rule asked the same question of the same
shape, one with optional chaining and one with a JSONPath query.

@markscott-ms markscott-ms left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (effort: high).

One additional finding that falls outside the diff's changed lines, so it's noted here instead of inline:

consistency (shared/src/commands/generate/components/instantiate.ts, instantiateObject): The "array with no prefixItems should yield [] not {}" fix was applied only to instantiateFromProperties (top-level pattern properties); the structurally parallel array-handling loop in instantiateObject, one call frame deeper, still gates on valueDef.type === 'array' && valueDef.prefixItems with no ?? [] fallback and was left untouched. Not a regression from this diff, but the two near-duplicate array-instantiation loops have now drifted further apart in behaviour, making the file easier to under-fix again next time.

Comment thread shared/src/commands/generate/components/options.ts Outdated
Comment thread shared/src/commands/generate/components/instantiate.ts Outdated
Comment thread shared/src/commands/generate/components/options.ts Outdated
Comment thread shared/src/commands/generate/components/options.ts Outdated
Comment thread shared/src/commands/generate/components/options.ts Outdated
Comment thread shared/src/commands/generate/components/options.ts Outdated
selectChoices is shared by generation and by validation, which has called
it since before items support existed. Promoting a chosen items member
into prefixItems also deleted the items block, so the pattern that
validation compiles lost the only thing constraining what an architecture
may add beyond its fixed positions. calm validate accepted architectures
it should reject.

Generation never read items, so deleting it bought nothing. The block now
stays. A promoted member sits at a fixed position and items governs the
positions after it, so the two do not collide.

instantiateFromProperties checked const before the array branch, which
would return a stale const for a resolved schema carrying both and
discard the prefixItems content. The original precedence is restored,
keeping the empty-array fallback beside it.

alternativesOf replaces the duplicated oneOf/anyOf test and reads the
keyword list from declaration-paths, so the two cannot drift.

Raised in review on finos#3103.
@markscott-ms
markscott-ms merged commit 6cdefeb into finos:main Sep 17, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Affects `cli` code shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants