fix(calm-suite): round-trip multi-child, interface, and options relationships in Studio#2692
Draft
eddie-knight wants to merge 2 commits into
Draft
Conversation
…ionships in Studio
Studio's CALM<->canvas projection lost graph fidelity on a load->edit->save
round-trip: connects endpoint interfaces were dropped, multi-child
composed-of/deployed-in/interacts relationships fragmented into N single-child
relationships with mangled `#i` ids (destroying the original unique-id that
flows and options decisions reference), and options relationships vanished or
were emitted as schema-invalid `{ options: [] }`.
- calmToFlow now carries connects endpoint interfaces on the edge; flowToCalm
re-aggregates edges by their source-relationship id (`calmRelId`), restoring
the original unique-id and merging multi-child targets into one relationship.
buildRelationshipType restores interfaces and throws on `options` (it has no
edge form) instead of emitting invalid CALM.
- applyFromCanvas preserves loaded options relationships from the prior model
with referential GC (drops decisions whose node/relationship refs no longer
resolve), since options have no edge representation.
- Remove the non-functional "Options" edge-type affordance (EdgeProperties
dropdown + CalmCanvas context menu): Studio has no decision-authoring UI, so
it could only ever produce invalid CALM. Loaded options still round-trip.
- EdgeProperties edits resolve the relationship via `calmRelId` so editing a
multi-child edge updates its (aggregated) relationship instead of no-opping.
Tests: connects-interface round-trip; multi-child re-aggregation for
composed-of/deployed-in/interacts (set-equality, original id); new-edge
handling; options preservation + decision GC on node deletion; flowToCalm never
emitting options; and multi-child edit-by-calmRelId.
Signed-off-by: Eddie Knight <knight@linux.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes CALM Studio’s canvas ↔ CALM projection round-trip fidelity so that multi-child relationships, connects interface bindings, and file-loaded options decisions aren’t structurally degraded when users edit node positions and save.
Changes:
- Re-aggregate multi-child
composed-of/deployed-in/interactsedges back into a single CALM relationship keyed by the originalunique-id(calmRelId), and preserveconnectsendpoint interface bindings. - Preserve file-loaded
optionsrelationships across canvas edits viaapplyFromCanvas, with referential GC of invalid decision references; remove “Options” as an authorable edge type in the UI. - Add/extend integration and component tests covering interface round-trip, multi-child re-aggregation, options preservation/GC, and editing by
calmRelId.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| calm-suite/calm-studio/apps/studio/src/tests/integration/sync-integration.test.ts | Adds integration coverage for lossless round-trips (interfaces, multi-child re-aggregation, options preservation/GC). |
| calm-suite/calm-studio/apps/studio/src/tests/components/EdgeProperties.test.ts | Updates EdgeProperties tests for “no options edge type” and calmRelId-based editing behavior. |
| calm-suite/calm-studio/apps/studio/src/lib/stores/projection.ts | Updates CALM↔Flow projection to carry interface bindings and re-aggregate edges by calmRelId. |
| calm-suite/calm-studio/apps/studio/src/lib/stores/calmModel.svelte.ts | Preserves options relationships across canvas edits and performs referential GC. |
| calm-suite/calm-studio/apps/studio/src/lib/properties/EdgeProperties.svelte | Removes options from authorable relationship types; targets edits via calmRelId and displays relationship id. |
| calm-suite/calm-studio/apps/studio/src/lib/canvas/CalmCanvas.svelte | Removes “Options” from the edge-type context menu options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…lity Three fixes from PR #2692 review: - EdgeProperties: changing the relationship type of a multi-child relationship rebuilt it from only the edited edge's endpoints, silently dropping the other children. Rebuild from the relationship's full membership (read from the model by calmRelId) so composed-of/deployed-in/interacts conversions preserve every child; connects (1:1) reduces to the first member. - applyFromCanvas: stop dropping an options relationship whose decisions array ends up empty. `option-type` has no minItems, so `options: []` is valid CALM; dropping it was lossy for a legitimately-empty input. Referential GC of dangling decisions is kept, but the relationship itself is preserved. - projection.ts: update the stale flowToCalm docstring that still described multi-child round-trips as a lossy split into single-child relationships. Tests: editing a multi-child edge's type now asserts all children are preserved; a legitimately-empty `options: []` survives a round-trip; the GC test asserts the relationship is kept (decisions GC'd) rather than dropped. Signed-off-by: Eddie Knight <knight@linux.com>
Contributor
Author
|
My currently open PRs are stacked to avoid any merge conflicts or rebasing overhead. This can be taken out of draft when the preceding PR is merged: #2691 |
16 tasks
Open
28 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The journey this fixes: You open a CALM architecture in CALM Studio — one with a container that holds several children (a
composed-of/deployed-inwith many nodes), someconnectsrelationships that bind specific interfaces, and maybe anoptionsdecision. You rearrange the nodes on the canvas and save. The saved file should be structurally identical to what you loaded — only positions changed.Today it isn't. Studio's CALM↔canvas projection (
projection.ts) silently degrades the graph on that round-trip:composed-ofwith 3 children comes back as three separate single-child relationships with mangled ids (box-children#0,#1,#2) — destroying the originalunique-idthat flows andoptionsdecisions reference by id.connectsinterface bindings are dropped.{ node, interfaces }→{ node }, so endpoint interface references are lost.optionsrelationships vanish or go invalid. Loaded decisions are dropped entirely on a canvas edit, and the "Options" edge-type could only ever emit schema-invalid{ options: [] }(Studio has no UI to author a decision'sdescription/nodes/relationships).The net effect: any tool downstream of Studio (the CLI validator, the Hub, flow overlays, generators) sees a structurally degraded document. This is the graph-payload counterpart to the document-level-key loss fixed in the parent PR.
After this PR, the round-trip is lossless:
composed-of/deployed-in/interactsrelationships re-aggregate into one relationship with the originalunique-idand all children intact.connectsendpoint interfaces survive the round-trip.optionsrelationships loaded from a file are preserved, with referential GC (a decision whose referenced node/relationship was deleted on the canvas is dropped rather than left dangling).What changed
calmToFlownow carriesconnectsendpoint interfaces on the edge;flowToCalmre-aggregates edges by their source-relationship id (calmRelId), restoring the originalunique-idand merging multi-child targets into one relationship.buildRelationshipTyperestores interfaces and throws onoptions(it has no edge form) instead of emitting invalid CALM.applyFromCanvaspreserves loadedoptionsrelationships from the prior model with referential GC, sinceoptionshave no edge representation.EdgePropertiesedits resolve the relationship viacalmRelId, so editing a multi-child edge updates its (aggregated) relationship instead of silently no-opping; the read-only Unique ID field shows the relationship id.Type of Change
Affected Components
cli/)calm/)calm-ai/)calm-hub/)calm-hub-ui/)calm-server/)calm-widgets/)docs/)shared/)calm-plugins/vscode/)Commit Message Format ✅
Conventional Commits, DCO signed-off:
fix(calm-suite): round-trip multi-child, interface, and options relationships in StudioTesting
Verified on Node 22:
npm run test --workspace=@calmstudio/studio(457 pass, incl. new fidelity/options coverage),npm run typecheck --workspace=@calmstudio/studio(no new errors vs the pre-existing baseline), andnpm run build --workspace=@calmstudio/studioall pass. New tests cover: connects-interface round-trip; multi-child re-aggregation forcomposed-of/deployed-in/interacts(set-equality ofnodes, originalunique-id,container/actorpreserved); the fullapplyFromJson → calmToFlow → applyFromCanvaswiring path; a drawn edge staying its own relationship (not absorbed into a group);optionspreservation; decision GC on a deleted reference (including partial-survival and empty-ref-kept);flowToCalmnever emittingoptions; and editing a multi-child edge bycalmRelId. Each fidelity guard was checked to fail on pre-fix code.Checklist