finos / finos/architecture-as-code
CALMGuard rejects canonical CALM documents: relationship schema uses a non-canonical flat shape
- Dominant language
- TypeScript
- Stars
- 399
- Forks
- 138
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 38
Description
## Bug Report
**Component:** `calm-suite/calm-guard` (CALMGuard)
CALMGuard's in-app CALM parser/validator models relationships with a **non-canonical flat shape** and therefore **rejects canonical CALM documents**. Every published CALM release (1.0, 1.1, 1.2) defines `relationship-type` as a nested **object** of keyed variants (`interacts`/`connects`/`deployed-in`/`composed-of`/`options`); CALMGuard's schema instead expects `relationship-type` to be a **string** with a sibling object, so real CALM architectures fail validation.
### Steps to Reproduce:
1. Take any canonical CALM document — its relationships use the nested `relationship-type` object form, e.g.:
```json
{
"nodes": [
{ "unique-id": "a", "node-type": "service", "name": "A", "description": "" },
{ "unique-id": "b", "node-type": "service", "name": "B", "description": "" }
],
"relationships": [
{
"unique-id": "a-to-b",
"relationship-type": {
"connects": { "source": { "node": "a" }, "destination": { "node": "b" } }
}
}
]
}
```
2. Load/validate it through CALMGuard (`parseCalm` in `src/lib/calm/parser.ts`, schema in `src/lib/calm/types.ts`) — e.g. via the upload zone or the analysis flow.
### Expected Result:
The document validates, including its relationships, because this nested form is the canonical CALM relationship shape used by the in-repo meta-schemas (`calm/release/*/meta/core.json`) and `@finos/calm-models`.
### Actual Result:
Zod validation fails. `calmRelationshipSchema` (`src/lib/calm/types.ts:195`) is a `z.discriminatedUnion('relationship-type', [...])` whose members declare `'relationship-type': z.literal('connects')` (a **string**) plus a sibling `connects` object. A canonical document supplies `'relationship-type'` as an **object** (`{ connects: { ... } }`), which matches no union member, so parsing errors out.
Supporting evidence that canonical relationship support is genuinely absent (not merely untested):
- The "v1.2 document" unit fixture has **`relationships: []`** (`src/__tests__/calm/parser.test.ts` `v12Doc`), so no nested relationship is ever exercised. Version detection (`detectCalmVersion`, `src/lib/calm/normalizer.ts`) only keys off top-level `adrs`/`decorators`/`timelines`.
- `src/lib/calm/normalizer.ts` normalises documents toward this flat schema before Zod parsing, and defaults unknown input to `'1.1'`.
- User-facing copy hard-codes a version string: `src/components/calm/calm-upload-zone.tsx:59` → `"CALM schema validation failed — file does not match CALM v1.1 structure"`. The relevant structure is the canonical CALM schema.
### Environment:
- `finos/architecture-as-code` `main`.
- `calm-suite/calm-guard` depends on `@finos/calm-cli ^1.33.0` but uses its own hand-rolled Zod schema for the in-app parse path rather than the shared CALM model.
### Additional Context:
- Canonical relationship shape, identical in 1.1 and 1.2: `calm/release/1.1/meta/core.json` / `calm/release/1.2/meta/core.json` (`defs.relationship.properties.relationship-type` — an object of keyed variants with `oneOf`).
- #2553 (#2550) brought CalmStudio onto the canonical nested model after the same class of bespoke-schema divergence; CALMGuard has it in its own Zod schema.
- Surfaced during the AGENTS.md documentation audit (#2587 / PR #2588).
- Suggested direction: replace CALMGuard's flat `calmRelationshipSchema` with the canonical nested form — ideally by adopting `@finos/calm-models` / `@finos/calm-cli` validation rather than maintaining a bespoke Zod schema (as CalmStudio did in #2553); failing that, rewrite the union to discriminate on the single present key of the `relationship-type` object. Replace the hard-coded "v1.1" user message, and add a fixture with a non-empty nested relationship to lock in the behaviour.
Contributor guide
Assessment
This issue has not been assessed yet.