Skip to content

fix(calm-suite): adopt canonical nested relationship-type in CALMGuard for cross-tool interop#2683

Open
eddie-knight wants to merge 4 commits into
mainfrom
fix/calmguard-canonical-relationship-type
Open

fix(calm-suite): adopt canonical nested relationship-type in CALMGuard for cross-tool interop#2683
eddie-knight wants to merge 4 commits into
mainfrom
fix/calmguard-canonical-relationship-type

Conversation

@eddie-knight

@eddie-knight eddie-knight commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Description

The journey this fixes: You model an architecture in CALM Studio, export it (or grab any CALM document the CLI / Hub produces), then open CALMGuard to run compliance analysis on it. Today that fails — the document is rejected the moment you upload it, before any analysis runs.

Concretely: take Studio's own ecommerce demo export and drop it into CALMGuard's upload zone → it's refused at the CALM-schema step.

The cause is a dialect mismatch. Canonical CALM 1.2 nests the relationship variant inside an object:

"relationship-type": { "connects": { "source": { "node": "a" }, "destination": { "node": "b" } } }

…but CALMGuard hand-rolled its own Zod schema using a flat string discriminant with a sibling key ("relationship-type": "connects" + a sibling "connects": {…}). So CALMGuard was the only tool in the suite speaking a private dialect: it couldn't read what Studio/CLI/Hub/Visualizer produce, and the remediation PRs it wrote back were schema-invalid to those same tools. It also enforced a closed 9-value node-type enum, so Studio extension node-types (e.g. aws:lambda) were rejected too.

After this PR, the round trip works:

  • ✅ A canonical CALM document from Studio / CLI / Hub uploads and analyzes in CALMGuard.
  • ✅ CALMGuard's remediation PR output is canonical CALM that calm validate, the Hub, and the Visualizer accept.
  • ✅ Extension node-type values from Studio packs are accepted.

What changed

  • Rewrote CALMGuard's relationship schema to the canonical nested relationship-type object (exactly-one-variant enforced via superRefine) and relaxed node-type to an open string, matching the CALM core spec.
  • Added getRelationshipVariant() and routed every reader (CALM extractor, the React-Flow graph mapping, the learning extractor) through the nested shape.
  • Made the v1.0 normalizer emit the nested form.
  • Added a compile-time conformance check against @finos/calm-models types (gated by tsc, so drift fails the typecheck) plus interop tests that parse real canonical fixtures authored by other CALM tools and assert the legacy flat form is now rejected.
  • Converted CALMGuard's demo examples and inline test fixtures to nested; updated calm-guard/AGENTS.md.

This PR also includes a small unrelated convenience commit, chore(calm-suite): add Makefile to launch suite apps on demand — happy to drop it if you'd prefer the PR scoped purely to the fix.

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.)

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 ✅

Conventional Commits, DCO signed-off:

  • fix(calm-suite): adopt canonical nested relationship-type in CALMGuard for cross-tool interop
  • chore(calm-suite): add Makefile to launch suite apps on demand

Testing

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

Verified on Node 22: npm run typecheck, npm run lint, npm run test:run (117 tests, including new interop + conformance coverage), and npm run build for calmguard all pass. Manually confirmed against a real Studio export: pre-fix CALMGuard rejects it at the upload zone; post-fix it parses and proceeds to analysis.

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

…d for cross-tool interop

CALMGuard modeled `relationship-type` as a flat string discriminant with a
sibling variant key, which is not canonical CALM 1.2. As a result it rejected
nested documents authored by CALM Studio / the CLI / Hub at parse time, and its
own PR write-backs were schema-invalid to those tools.

- Rewrite the Zod schema to the canonical nested `relationship-type` object,
  enforcing exactly-one-variant via superRefine, and relax `node-type` to an
  open string so extension node-types (e.g. aws:lambda) interoperate.
- Add getRelationshipVariant() and route all readers (calm extractor,
  calm-to-flow graph mapping, learning extractor) through the nested shape.
- Emit the nested form from the v1.0 normalizer.
- Add a compile-time conformance check against @finos/calm-models types
  (gated by tsc, not vitest), plus interop tests that parse real canonical
  fixtures authored by other CALM tools and reject the legacy flat form.
- Convert demo examples and inline test fixtures to nested; update AGENTS.md.

Adds @finos/calm-models as a direct dependency of calmguard.

Signed-off-by: Eddie Knight <knight@linux.com>
Slim launcher wrapping the existing npm workspace scripts: guard / guard-docs /
studio / studio-desktop dev servers, builds, and tests. Runs from the repo root
and builds @calmstudio/calm-core before starting Studio (it ships from dist).

Signed-off-by: Eddie Knight <knight@linux.com>
…nd build

CALMGuard's compile-time conformance check (src/lib/calm/conformance.ts) imports
types from @finos/calm-models. That package ships from dist/ and has no prepare
script, so a clean `npm ci` does not build it — the Lint/Typecheck and Build
jobs then fail to resolve `@finos/calm-models/types` (TS2307). Build it first in
both jobs, matching how the calm-studio workflow already sequences it.

Signed-off-by: Eddie Knight <knight@linux.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants