@sdd-method/author-cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/NOTICE +1 -1
  2. package/dist/author-program.js +1 -1
  3. package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +4 -4
  4. package/dist/lib/api-surface/analyzers/serverless.js +1 -1
  5. package/dist/lib/api-surface/analyzers/spring.js +1 -1
  6. package/dist/lib/briefing/render.js +1 -2
  7. package/dist/lib/briefing/variables.js +1 -2
  8. package/dist/lib/catalogue/archetype/wireframe.js +1 -1
  9. package/dist/lib/catalogue/build.js +17 -23
  10. package/dist/lib/catalogue/builders/application-conformance-coverage.js +1 -1
  11. package/dist/lib/catalogue/builders/c4-components.js +4 -5
  12. package/dist/lib/catalogue/builders/c4-containers.js +4 -4
  13. package/dist/lib/catalogue/builders/c4-external-systems.js +1 -1
  14. package/dist/lib/catalogue/builders/c4-l1-candidates.js +2 -2
  15. package/dist/lib/catalogue/builders/capabilities.js +2 -2
  16. package/dist/lib/catalogue/builders/capability-edges.js +4 -4
  17. package/dist/lib/catalogue/builders/capability-spec-walker.js +3 -3
  18. package/dist/lib/catalogue/builders/container-dependency.js +19 -20
  19. package/dist/lib/catalogue/builders/contracts.js +5 -6
  20. package/dist/lib/catalogue/builders/data-store-mapping.js +4 -4
  21. package/dist/lib/catalogue/builders/edge-label-merge.js +2 -2
  22. package/dist/lib/catalogue/builders/entities-manifest.js +1 -1
  23. package/dist/lib/catalogue/builders/entities-mermaid.js +3 -3
  24. package/dist/lib/catalogue/builders/entity-slug.js +11 -12
  25. package/dist/lib/catalogue/builders/event-catalog-walker.js +3 -3
  26. package/dist/lib/catalogue/builders/evidence-class.js +2 -2
  27. package/dist/lib/catalogue/builders/external-system-exemption.js +1 -1
  28. package/dist/lib/catalogue/builders/feature-contract.js +1 -3
  29. package/dist/lib/catalogue/builders/feature-grounding.js +1 -2
  30. package/dist/lib/catalogue/builders/feature-groups.js +0 -1
  31. package/dist/lib/catalogue/builders/feature-subscription-channels.js +2 -3
  32. package/dist/lib/catalogue/builders/feature-tree-walker.js +1 -1
  33. package/dist/lib/catalogue/builders/features.js +3 -4
  34. package/dist/lib/catalogue/builders/glossary-xref.js +1 -1
  35. package/dist/lib/catalogue/builders/persona-c4system.js +7 -7
  36. package/dist/lib/catalogue/builders/persona-container.js +14 -14
  37. package/dist/lib/catalogue/builders/personas.js +1 -2
  38. package/dist/lib/catalogue/builders/product-platform-dependencies.js +2 -2
  39. package/dist/lib/catalogue/builders/quality-scores.js +1 -1
  40. package/dist/lib/catalogue/builders/repositories.js +2 -2
  41. package/dist/lib/catalogue/builders/screen-tree.js +2 -2
  42. package/dist/lib/catalogue/builders/screens.js +3 -4
  43. package/dist/lib/catalogue/builders/system-external-dependency.js +7 -7
  44. package/dist/lib/catalogue/canonical-schema.yaml +23 -23
  45. package/dist/lib/catalogue/coverage-predicates.js +4 -4
  46. package/dist/lib/catalogue/coverage-run.js +1 -1
  47. package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
  48. package/dist/lib/catalogue/ext-id.js +4 -4
  49. package/dist/lib/catalogue/feature-grounding-aliases-loader.js +1 -1
  50. package/dist/lib/catalogue/finding-identity.js +3 -3
  51. package/dist/lib/catalogue/freshness.js +1 -1
  52. package/dist/lib/catalogue/manifest-loader.js +2 -2
  53. package/dist/lib/catalogue/scaffold-delta.js +1 -1
  54. package/dist/lib/catalogue/schema-compat.js +1 -2
  55. package/dist/lib/catalogue/scores-coverage.js +1 -1
  56. package/dist/lib/catalogue/screen-validation-loader.js +1 -1
  57. package/dist/lib/catalogue/service-contract-aliases-loader.js +1 -1
  58. package/dist/lib/catalogue/staleness-verdicts.js +2 -2
  59. package/dist/lib/catalogue/traceability-loader.js +2 -3
  60. package/dist/lib/conformance/index.js +29 -8
  61. package/dist/lib/lock-file.js +11 -1
  62. package/dist/lib/package-version.js +1 -1
  63. package/dist/lib/snapshot/manifest.js +1 -1
  64. package/dist/lib/snapshot/targets.js +2 -2
  65. package/dist/lib/validate/index.js +1 -1
  66. package/dist/lib/validate/plan.js +7 -4
  67. package/dist/lib/validate/yaml-parse.js +1 -1
  68. package/dist/maintain-version.json +2 -2
  69. package/dist/verbs/plan.js +3 -3
  70. package/dist/verbs/validate.js +6 -1
  71. package/dist/verify-program.js +1 -1
  72. package/dist/verify-version.json +2 -2
  73. package/package.json +2 -6
@@ -134,7 +134,7 @@ enums:
134
134
  runtime: true
135
135
  emits_service_row: false
136
136
  description: |
137
- Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation); firmware runs, but on a device, so it is runtime without a service row (M-ADR 0215) — service emission follows emits_service_row, not runtime. Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape (per resolution of adopt-canonical-catalogue-builder.md §7.1).
137
+ Type of repository, declared in `orchestration/repo-config/*.yaml` as `service_type` and surfaced in `repositories.component_type` and `services.component_type`. The canonical builder uses this to decide which repos are runtime services (api_service, worker, gateway, frontend, mobile) and which are non-runtime (library, infrastructure, documentation); firmware runs, but on a device, so it is runtime without a service row (M-ADR 0215) — service emission follows emits_service_row, not runtime. Whether a repo is a vertical-domain application is conveyed by its SDD manifest's `profile: application-product`, not by a service_type tag — this enum is strictly about runtime artefact shape.
138
138
  capability_exemption_reason:
139
139
  values:
140
140
  - value: infrastructure
@@ -174,11 +174,11 @@ enums:
174
174
  - value: public-infrastructure
175
175
  description: Public networks, registries, or services no party in the estate operates.
176
176
  - value: estate-owned-unmodelled
177
- description: "Operated by this estate but with no SDD or container home yet — the row is an accounted-for stand-in, and this population is the estate's modelling backlog, countable (F16, 2.32.0). Not an assertion of externality: an assertion of accountability for a boundary row."
177
+ description: "Operated by this estate but with no SDD or container home yet — the row is an accounted-for stand-in, and this population is the estate's modelling backlog, countable (2.32.0). Not an assertion of externality: an assertion of accountability for a boundary row."
178
178
  - value: organisation-internal
179
- description: Owned elsewhere in the same organisation, outside this estate's modelling boundary (F16, 2.32.0).
179
+ description: Owned elsewhere in the same organisation, outside this estate's modelling boundary (2.32.0).
180
180
  description: |
181
- Why an external-system candidate is ACCOUNTED FOR at the estate boundary, per M-ADR 0189 §4.6 (WS15; wording corrected by F16, 2.32.0): five values assert another party owns the system, and two assert the estate/organisation owns it but it has no model home yet — forcing the latter through the former would demand a false reason, and a false reason is worse than silence. The adjudicator can prove a candidate internal; it cannot prove one accounted-for — that is an assertion the author is accountable for, in the shape M-ADR 0188 ratified for repositories. Closed vocabulary, method-owned, extended only by ADR amendment, so adopters cannot locally widen the exemption surface to silence the unverified-candidate coverage finding.
181
+ Why an external-system candidate is ACCOUNTED FOR at the estate boundary, per M-ADR 0189 §4.6 (wording corrected in 2.32.0): five values assert another party owns the system, and two assert the estate/organisation owns it but it has no model home yet — forcing the latter through the former would demand a false reason, and a false reason is worse than silence. The adjudicator can prove a candidate internal; it cannot prove one accounted-for — that is an assertion the author is accountable for, in the shape M-ADR 0188 ratified for repositories. Closed vocabulary, method-owned, extended only by ADR amendment, so adopters cannot locally widen the exemption surface to silence the unverified-candidate coverage finding.
182
182
  ENFORCED, not advisory: the builder drops a row whose reason is outside this vocabulary (convention finding) and the candidate stays unverified — which is the point. An unrecognised reason must not succeed at the one thing an exemption does.
183
183
  scaffold_status:
184
184
  values:
@@ -1353,7 +1353,7 @@ catalogues:
1353
1353
  nullable: true
1354
1354
  description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
1355
1355
  description: |
1356
- Hierarchical grouping for features. First-class entity per resolution of adopt-canonical-catalogue-builder.md §7.2 — replaces the previous reverse-only synthetic `feature-tree-group` rows in features.csv. Both forward and reverse can use feature_groups: forward application-product SDDs whose `feature-tree.md` has H2/H3 structure, reverse SDDs whose extracted feature trees are inherently hierarchical, and any SDD that promotes groups to first-class with authored metadata. Groups can nest via `parent_group_id`.
1356
+ Hierarchical grouping for features. First-class entity — replaces the previous reverse-only synthetic `feature-tree-group` rows in features.csv. Both forward and reverse can use feature_groups: forward application-product SDDs whose `feature-tree.md` has H2/H3 structure, reverse SDDs whose extracted feature trees are inherently hierarchical, and any SDD that promotes groups to first-class with authored metadata. Groups can nest via `parent_group_id`.
1357
1357
  foreign_keys:
1358
1358
  - column: product_id
1359
1359
  references: products.id
@@ -2062,8 +2062,8 @@ catalogues:
2062
2062
  nullable: false
2063
2063
  description: Mandatory, non-empty. The reason makes exemptions countable; the note makes them reviewable (M-ADR 0188 §4.2 pattern).
2064
2064
  description: |
2065
- A declared, reasoned assertion that an external-system candidate is GENUINELY external (M-ADR 0189 §4.6 / WS15). Annotation catalogue (M-ADR 0133 §4.2b): one FK, declared never derived, closed reason enum. Sourced from `orchestration/external-system-exemptions.yaml`.
2066
- The invariant it completes: c4_external_systems was the one catalogue family with no coverage predicate — a candidate producing no adjudication outcome was silent, and that silence hid a 28% dependency -graph distortion (ledger L4). Every candidate now either resolves at adjudication, or carries an exemption here, or is reported unverified. An exemption retires only the unverified finding — never an adjudication outcome (a converted candidate's exemption is a contradiction, reported and dropped). Shared entities may be exempted by multiple SDDs: identical rows dedup at aggregation (same exception class as c4_external_systems); conflicting rows keep first-seen with a convention finding.
2065
+ A declared, reasoned assertion that an external-system candidate is GENUINELY external (M-ADR 0189 §4.6). Annotation catalogue (M-ADR 0133 §4.2b): one FK, declared never derived, closed reason enum. Sourced from `orchestration/external-system-exemptions.yaml`.
2066
+ The invariant it completes: c4_external_systems was the one catalogue family with no coverage predicate — a candidate producing no adjudication outcome was silent, and that silence hid a 28% dependency -graph distortion. Every candidate now either resolves at adjudication, or carries an exemption here, or is reported unverified. An exemption retires only the unverified finding — never an adjudication outcome (a converted candidate's exemption is a contradiction, reported and dropped). Shared entities may be exempted by multiple SDDs: identical rows dedup at aggregation (same exception class as c4_external_systems); conflicting rows keep first-seen with a convention finding.
2067
2067
  foreign_keys:
2068
2068
  - column: external_system_id
2069
2069
  references: c4_external_systems.id
@@ -2172,7 +2172,7 @@ catalogues:
2172
2172
  type: string
2173
2173
  nullable: false
2174
2174
  description: exposes | consumes.
2175
- description: "Edge: service exposes or consumes contract. Both FKs are external (per M-ADR 0147): federated SDDs that walk per-repo service-architecture tables emit consumes rows where service_id resolves to the platform-root catalogue's services.csv (post-aggregation) and contract_id resolves to the platform-root contracts.csv. Internal builds (e.g. connect-mono) still optimistically validate locally. Schema 2.8.0."
2175
+ description: "Edge: service exposes or consumes contract. Both FKs are external (per M-ADR 0147): federated SDDs that walk per-repo service-architecture tables emit consumes rows where service_id resolves to the platform-root catalogue's services.csv (post-aggregation) and contract_id resolves to the platform-root contracts.csv. Internal builds (a single-SDD platform) still optimistically validate locally. Schema 2.8.0."
2176
2176
  foreign_keys:
2177
2177
  - column: service_id
2178
2178
  references: services.id
@@ -2303,7 +2303,7 @@ catalogues:
2303
2303
  nullable: true
2304
2304
  description: Where the coupling is evidenced — a path or anchor in the SDD, from `evidence:` on the services[] entry. Prose about the coupling stays in dependency_notes; this is the pointer a reviewer follows.
2305
2305
  description: |
2306
- Edge: capability is realised by services. Sourced from the capability traceability manifest services[] block. service_id is an external FK (schema 2.29.0, F4): a traceability entry marked `owner_sdd:` declares a cross-SDD reference (e.g. the service moved to a sibling SDD at an M-ADR 0170 §4.2 carve) — the per-SDD builder emits the edge, logs it `[finding] external`, and aggregation validates it hard against the owning sibling's rows. An UNMARKED id that does not resolve locally is NOT emitted — it stays a member-tier orphan finding (or convention, when it names a known external system).
2306
+ Edge: capability is realised by services. Sourced from the capability traceability manifest services[] block. service_id is an external FK (schema 2.29.0): a traceability entry marked `owner_sdd:` declares a cross-SDD reference (e.g. the service moved to a sibling SDD at an M-ADR 0170 §4.2 carve) — the per-SDD builder emits the edge, logs it `[finding] external`, and aggregation validates it hard against the owning sibling's rows. An UNMARKED id that does not resolve locally is NOT emitted — it stays a member-tier orphan finding (or convention, when it names a known external system).
2307
2307
  foreign_keys:
2308
2308
  - column: capability_id
2309
2309
  references: capabilities.id
@@ -2365,8 +2365,8 @@ catalogues:
2365
2365
  - name: source
2366
2366
  type: string
2367
2367
  nullable: false
2368
- description: "declared — the only emitted value. `derived` is reserved vocabulary: the reverse derivation it anticipated was never built (0 rows across 105 measured units) and the claim was corrected at 3.0.0 (M-ADR 0203 §4.5 / F2); the value re-enters if a decision builds a reverse source."
2369
- description: "Edge: capability is bound to a contract. Whether the capability exposes or depends on it is not stated by the source (traceability.contracts), so this description asserts no direction (M-ADR 0211 §1). Forward-declared only. Forward-declared only (traceability.contracts). The reverse derivation this description long claimed (service_contract ⋈ capability_service) was measured nonexistent — 0 `source: derived` rows across 105 units, builder references neither catalogue — and the CLAIM was corrected at 3.0.0 (M-ADR 0203 §4.5 / F2, gap class absent-derivation per M-ADR 0196 §4.1). The edge is forward-one-directional until a decision builds a reverse source."
2368
+ description: "declared — the only emitted value. `derived` is reserved vocabulary: the reverse derivation it anticipated was never built (0 rows across 105 measured units) and the claim was corrected at 3.0.0 (M-ADR 0203 §4.5); the value re-enters if a decision builds a reverse source."
2369
+ description: "Edge: capability is bound to a contract. Whether the capability exposes or depends on it is not stated by the source (traceability.contracts), so this description asserts no direction (M-ADR 0211 §1). Forward-declared only. Forward-declared only (traceability.contracts). The reverse derivation this description long claimed (service_contract ⋈ capability_service) was measured nonexistent — 0 `source: derived` rows across 105 units, builder references neither catalogue — and the CLAIM was corrected at 3.0.0 (M-ADR 0203 §4.5, gap class absent-derivation per M-ADR 0196 §4.1). The edge is forward-one-directional until a decision builds a reverse source."
2370
2370
  foreign_keys:
2371
2371
  - column: capability_id
2372
2372
  references: capabilities.id
@@ -2546,7 +2546,7 @@ catalogues:
2546
2546
  type: string
2547
2547
  nullable: true
2548
2548
  description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
2549
- description: "Edge: feature delivers async push via a named channel/topic. Peer of `graphql_operations:` in the per-feature evidence shape — `graphql_operations:` carries the GraphQL operation names declared in the schema (Queries / Mutations / Subscriptions); `feature_subscription_channels` rows carry the channel/topic layer that physically delivers the push events (AppSync WebSocket endpoints, Pekko WS channels, MQTT topics, server-sent-event streams). Per resolved decision §1 of `content/plans/v0-9-0-cohort-sweep-derived-bundle.md`: a single GraphQL Subscription operation may listen on multiple channels; one channel may be consumed by multiple operations — modelling as a separate edge catalogue (not as a feature column) accommodates the many-to-many. Built per-SDD from `reverse-evidence/feature-grounding-evidence.yaml` `subscription_channels:` entries on each feature. Added in schema 2.9.0."
2549
+ description: "Edge: feature delivers async push via a named channel/topic. Peer of `graphql_operations:` in the per-feature evidence shape — `graphql_operations:` carries the GraphQL operation names declared in the schema (Queries / Mutations / Subscriptions); `feature_subscription_channels` rows carry the channel/topic layer that physically delivers the push events (AppSync WebSocket endpoints, Pekko WS channels, MQTT topics, server-sent-event streams). A single GraphQL Subscription operation may listen on multiple channels; one channel may be consumed by multiple operations — modelling as a separate edge catalogue (not as a feature column) accommodates the many-to-many. Built per-SDD from `reverse-evidence/feature-grounding-evidence.yaml` `subscription_channels:` entries on each feature. Added in schema 2.9.0."
2550
2550
  foreign_keys:
2551
2551
  - column: feature_id
2552
2552
  references: features.id
@@ -2672,9 +2672,9 @@ catalogues:
2672
2672
  - name: interaction
2673
2673
  type: string
2674
2674
  nullable: true
2675
- description: Optional interaction label from the Rel(...) line. May hold multiple '; '-joined labels (F25).
2675
+ description: Optional interaction label from the Rel(...) line. May hold multiple '; '-joined labels.
2676
2676
  description: |
2677
- Edge: a persona uses a C4 container — the L2-granularity companion to persona_c4system (M-ADR 0189 F26 §3, ratified 2026-07-10). Eleven of one estate's L2 diagrams and the bulk of another's 81 unroutable Rels drew Person→Container relationships that had no catalogue and, until F26, no finding. Harvested at member tier from Rel(...) lines whose endpoints resolve to a file-local Person and a local container (either direction; the row is normalised persona→container). A Person that matches no persona row is a coverage finding, never a silent skip. Both authoring directions feed from the same diagram surface (M-ADR 0157 parity). Added in schema 2.34.0.
2677
+ Edge: a persona uses a C4 container — the L2-granularity companion to persona_c4system (M-ADR 0189, ratified 2026-07-10). Eleven of one estate's L2 diagrams and the bulk of another's 81 unroutable Rels drew Person→Container relationships that had no catalogue and raised no finding. Harvested at member tier from Rel(...) lines whose endpoints resolve to a file-local Person and a local container (either direction; the row is normalised persona→container). A Person that matches no persona row is a coverage finding, never a silent skip. Both authoring directions feed from the same diagram surface (M-ADR 0157 parity). Added in schema 2.34.0.
2678
2678
  foreign_keys:
2679
2679
  - column: persona_id
2680
2680
  references: personas.id
@@ -2698,11 +2698,11 @@ catalogues:
2698
2698
  - name: interaction
2699
2699
  type: string
2700
2700
  nullable: true
2701
- description: Optional integration label/protocol from the Rel(...) line. May hold multiple '; '-joined labels when one pair is declared with distinct labels (F25, 2.33.2).
2701
+ description: Optional integration label/protocol from the Rel(...) line. May hold multiple '; '-joined labels when one pair is declared with distinct labels (2.33.2).
2702
2702
  - name: from_container_id
2703
2703
  type: string
2704
2704
  nullable: true
2705
- description: The Rel's non-external endpoint resolved to a local container at member tier, when it is one (F17, 2.33.0) — the datum that lets adjudication rewrite this edge onto container_dependency instead of destroying it. Empty when the endpoint is the system itself or unresolvable.
2705
+ description: The Rel's non-external endpoint resolved to a local container at member tier, when it is one (2.33.0) — the datum that lets adjudication rewrite this edge onto container_dependency instead of destroying it. Empty when the endpoint is the system itself or unresolvable.
2706
2706
  description: "Edge: a C4 system integrates with an external software system. Captures the L1 context 'System integrates with External System' relationship, extracted from System_Ext(...) + Rel(...) on L1/L2 diagrams. Per M-ADR 0155. Container-level external dependencies are out of scope (system-level only). Added in schema 2.10.0. From 2.30.0 (M-ADR 0189 §4.4): edges whose external endpoint the aggregator adjudicates as an INTERNAL system are rewritten onto system_system_dependency and removed from this catalogue."
2707
2707
  foreign_keys:
2708
2708
  - column: c4_system_id
@@ -2734,13 +2734,13 @@ catalogues:
2734
2734
  - name: source_sdd_id
2735
2735
  type: string
2736
2736
  nullable: true
2737
- description: SDD whose diagram declared the originating reference. Member-emitted rows set it directly (F19); adjudication-derived rows are stamped by the aggregator.
2737
+ description: SDD whose diagram declared the originating reference. Member-emitted rows set it directly; adjudication-derived rows are stamped by the aggregator.
2738
2738
  - name: platform_sdd_id
2739
2739
  type: string
2740
2740
  nullable: true
2741
2741
  description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
2742
2742
  description: |
2743
- Edge: a C4 container depends on another INTERNAL container (adopter finding F17 — F6 exactly one tier down: "no edge for internal" at L2). Derived by the aggregator at adjudication: an external-system candidate that resolves to an internal container was never external — each of its dependency edges whose member-resolved `from_container_id` is known is rewritten here, and a candidate with no remaining edges is dropped (it was a phantom duplicate of the container). A candidate with unconvertible edges is kept with the tier-contradiction finding, so no edge is ever destroyed to make a build quieter — the measured cost that stopped an adopter remediating 22 contradictions was eight real edges. ALSO emitted at member tier (F19, 2.33.1): the C4-correct authoring — Rel(container_a, container_b) between two Container(...) nodes — emits its edge directly; adjudication merges into a catalogue that already has content rather than being its only source, so the metamodel no longer rewards mis-declaration over correct authoring. Added in schema 2.33.0.
2743
+ Edge: a C4 container depends on another INTERNAL container (the system-tier gap exactly one tier down: "no edge for internal" at L2). Derived by the aggregator at adjudication: an external-system candidate that resolves to an internal container was never external — each of its dependency edges whose member-resolved `from_container_id` is known is rewritten here, and a candidate with no remaining edges is dropped (it was a phantom duplicate of the container). A candidate with unconvertible edges is kept with the tier-contradiction finding, so no edge is ever destroyed to make a build quieter — the measured cost that stopped an adopter remediating 22 contradictions was eight real edges. ALSO emitted at member tier (2.33.1): the C4-correct authoring — Rel(container_a, container_b) between two Container(...) nodes — emits its edge directly; adjudication merges into a catalogue that already has content rather than being its only source, so the metamodel no longer rewards mis-declaration over correct authoring. Added in schema 2.33.0.
2744
2744
  foreign_keys:
2745
2745
  - column: from_container_id
2746
2746
  references: c4_containers.id
@@ -2910,7 +2910,7 @@ catalogues:
2910
2910
  - name: target_locus
2911
2911
  type: string
2912
2912
  nullable: true
2913
- description: platform | external | legacy (M-ADR 0204; closed enum, absent/empty = platform). A statement about the TARGET only — never about the consuming product, and never a stability, lifecycle or quality claim. platform rows behave exactly as before this column existed. external/legacy rows carry empty target_capability_id and target_service_id and name their target in target_external_system_id; they skip platform resolution and the F18 realiser-join, with every skip named.
2913
+ description: platform | external | legacy (M-ADR 0204; closed enum, absent/empty = platform). A statement about the TARGET only — never about the consuming product, and never a stability, lifecycle or quality claim. platform rows behave exactly as before this column existed. external/legacy rows carry empty target_capability_id and target_service_id and name their target in target_external_system_id; they skip platform resolution and the synchronous realiser-join, with every skip named.
2914
2914
  - name: target_external_system_id
2915
2915
  type: string
2916
2916
  nullable: true
@@ -2926,7 +2926,7 @@ catalogues:
2926
2926
  - name: contract_type
2927
2927
  type: string
2928
2928
  nullable: true
2929
- description: rest | graphql | openapi | grpc | event | event-stream | stream | other. Optional; reflects what the consuming SDD declares. The async set (`event`/`event-stream`/`stream`) marks a shared-stream subscription that binds to a stream contract rather than a realiser service (M-ADR 0192) — such rows skip the F18 synchronous realiser-join at aggregation.
2929
+ description: rest | graphql | openapi | grpc | event | event-stream | stream | other. Optional; reflects what the consuming SDD declares. The async set (`event`/`event-stream`/`stream`) marks a shared-stream subscription that binds to a stream contract rather than a realiser service (M-ADR 0192) — such rows skip the synchronous realiser-join at aggregation.
2930
2930
  - name: required_stability
2931
2931
  type: string
2932
2932
  nullable: true
@@ -2948,7 +2948,7 @@ catalogues:
2948
2948
  nullable: true
2949
2949
  description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
2950
2950
  description: |
2951
- Per-SDD edge: an application-side or integration-side SDD declares that its product depends on a platform-side capability + service pair. Built from `reverse-evidence/feature-grounding-evidence.yaml` `platform_dependencies:` block (product-level — the cohort source shape carries product-scoped dependency rows with a `consumed_by:` list naming which services within the consuming product depend on the target). Each row records one (consuming product, target capability, target service) triple. The aggregator then joins these rows against platform-side `capability_service` to emit `cross_sdd_platform_capability` edges at aggregation time. Cross- SDD references (target_capability_id, target_service_id) are external — they're not expected to resolve in the per-SDD build, only at aggregation. Added in schema 2.9.0; see `content/plans/v0-9-0-cohort-sweep-derived-bundle.md` §"Cross-SDD platform-capability-edge".
2951
+ Per-SDD edge: an application-side or integration-side SDD declares that its product depends on a platform-side capability + service pair. Built from `reverse-evidence/feature-grounding-evidence.yaml` `platform_dependencies:` block (product-level — the cohort source shape carries product-scoped dependency rows with a `consumed_by:` list naming which services within the consuming product depend on the target). Each row records one (consuming product, target capability, target service) triple. The aggregator then joins these rows against platform-side `capability_service` to emit `cross_sdd_platform_capability` edges at aggregation time. Cross- SDD references (target_capability_id, target_service_id) are external — they're not expected to resolve in the per-SDD build, only at aggregation. Added in schema 2.9.0.
2952
2952
  foreign_keys:
2953
2953
  - column: product_id
2954
2954
  references: products.id
@@ -2993,7 +2993,7 @@ catalogues:
2993
2993
  nullable: false
2994
2994
  description: Service identifier the consuming SDD references and the platform SDD owns. The join key.
2995
2995
  description: |
2996
- Federated edge: an application-side or integration-side SDD's `product_platform_dependencies` reference resolves against a platform-side SDD's capability spec via the platform capability's `capability_service` roster. Surfaces ecosystem-wide platform- consumer relationships at product → capability granularity. Built **only at aggregation time** — `sdd-cli catalogue aggregate` joins each consuming SDD's `product_platform_dependencies` rows against every other SDD's `capability_service` rows on `target_service_id == service_id`, emitting one row per resolved (consuming-product, platform-capability) pair. Per-SDD builders emit nothing for this catalogue; the aggregator owns it entirely. A platform-dependency whose target_service_id doesn't resolve to any platform-side capability_service row is logged as an `orphan` finding and produces no row. Cross-SDD references are external by definition. Added in schema 2.9.0; see `content/plans/v0-9-0-cohort-sweep-derived-bundle.md` §"Cross-SDD platform-capability-edge" and M-ADR 0134.
2996
+ Federated edge: an application-side or integration-side SDD's `product_platform_dependencies` reference resolves against a platform-side SDD's capability spec via the platform capability's `capability_service` roster. Surfaces ecosystem-wide platform- consumer relationships at product → capability granularity. Built **only at aggregation time** — `sdd-cli catalogue aggregate` joins each consuming SDD's `product_platform_dependencies` rows against every other SDD's `capability_service` rows on `target_service_id == service_id`, emitting one row per resolved (consuming-product, platform-capability) pair. Per-SDD builders emit nothing for this catalogue; the aggregator owns it entirely. A platform-dependency whose target_service_id doesn't resolve to any platform-side capability_service row is logged as an `orphan` finding and produces no row. Cross-SDD references are external by definition. Added in schema 2.9.0; see M-ADR 0134.
2997
2997
  foreign_keys:
2998
2998
  - column: consuming_product_id
2999
2999
  references: products.id
@@ -3131,7 +3131,7 @@ catalogues:
3131
3131
  nullable: true
3132
3132
  description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator per M-ADR 0208 §4.4 (preserve-if-set, fill rule by pull path); left empty by per-SDD builders. Added in 3.7.0.
3133
3133
  description: |
3134
- Application-product annotation (M-ADR 0191 §4.3 overlay, second element — WS4): one row per conformance tag the product asserts about its archetype's conformance expectations. Built per-SDD from the manifest's `conformance` block so the AGGREGATION-tier conformance GATE can read it. `status=covered` means the product's own conformance suite exercises the tag; `status=deviated` means the product declares (with a rationale) that it deliberately does not. At aggregation, an archetype-expected conformance tag that is neither covered nor deviated raises a deviation-visible WARN. The method gates the DECLARATION — it does not run the adopter's conformance suite. Added in schema 2.39.0.
3134
+ Application-product annotation (M-ADR 0191 §4.3 overlay, second element): one row per conformance tag the product asserts about its archetype's conformance expectations. Built per-SDD from the manifest's `conformance` block so the AGGREGATION-tier conformance GATE can read it. `status=covered` means the product's own conformance suite exercises the tag; `status=deviated` means the product declares (with a rationale) that it deliberately does not. At aggregation, an archetype-expected conformance tag that is neither covered nor deviated raises a deviation-visible WARN. The method gates the DECLARATION — it does not run the adopter's conformance suite. Added in schema 2.39.0.
3135
3135
  foreign_keys:
3136
3136
  - column: product_id
3137
3137
  references: products.id
@@ -13,12 +13,12 @@
13
13
  * §4.3 (`orphaned` is aggregate-only; member tier reports
14
14
  * `unresolved-locally`, cause-agnostic); the Path-X leverage axis is
15
15
  * reported separately and only when repo_capability.csv exists.
16
- * - event_coverage (WS3): every catalogued event is attributed to a
16
+ * - event_coverage: every catalogued event is attributed to a
17
17
  * runtime — service_event edge or resolving publisher column — with
18
18
  * the contract-twin alternative route, the column-vs-edge disagreement
19
19
  * controls, and the chain-readout/consumer-enumerability metric lines
20
20
  * that are never verdicts.
21
- * - contract_coverage (WS2): every catalogued contract is reachable —
21
+ * - contract_coverage: every catalogued contract is reachable —
22
22
  * capability_contract or service_contract — with the three computed
23
23
  * exclusions (deprecated / provider-surface / not-an-interface, the
24
24
  * last a positive READ of the spec file), the covered-as-event
@@ -178,8 +178,8 @@ FROM "services" s
178
178
  WHERE s."id" IS NOT NULL AND s."id" <> ''
179
179
  ORDER BY service_id, primary_repo;`;
180
180
  export async function runServiceCapabilityCoverage(session, options) {
181
- // Known-bad probe, carried over from the reference implementation
182
- // (WS11's third requirement): a synthetic claimed service must classify
181
+ // Known-bad probe, carried over from the reference implementation:
182
+ // a synthetic claimed service must classify
183
183
  // as claimed BEFORE any real data is reported — a run whose
184
184
  // classification never executed must not be able to report a healthy
185
185
  // estate.
@@ -89,7 +89,7 @@ export async function runCatalogueCoverage(inputs, write) {
89
89
  const available = new Set(schema.catalogues.map((c) => c.name).filter((n) => present(n)));
90
90
  lines.push(...renderScoresCoverage(await runScoresCoverage(session, { tier, available, sddRoot }), tier));
91
91
  // Staleness VERDICTS — the second half of the fourteenth check's §4.5
92
- // scope (ledger §3o), member-tier only with the aggregate skip stated.
92
+ // scope, member-tier only with the aggregate skip stated.
93
93
  // Same coverage class, same reporting-only staging as the rest of the
94
94
  // scores family.
95
95
  lines.push(...renderStalenessVerdicts(await runStalenessVerdicts(session, { tier, available, sddRoot }), tier));
@@ -12,12 +12,12 @@
12
12
  // carries (`name`), so the old→new map is a build artefact, not an authored
13
13
  // migration: `sdd-cli catalogue migration-map` emits it, and the exemption
14
14
  // loader consults it during the cut window (an authored exemption keyed by
15
- // a mapped old id is regenerable, NOT drift — the F30 rule at schema scale).
15
+ // a mapped old id is regenerable, NOT drift — the regenerability rule at schema scale).
16
16
  /**
17
17
  * Normalise a declared diagram entry id to slug form. Camel humps split
18
- * BEFORE lowercasing (the F11a rule, matching the aggregator's resolver):
19
- * most SDD ids are hyphenated, and `bvfmGatewayFramework` must derive the
20
- * same identity the adjudicator resolves for it (`bvfm-gateway-framework`).
18
+ * BEFORE lowercasing (matching the aggregator's resolver):
19
+ * most SDD ids are hyphenated, and `posGatewayFramework` must derive the
20
+ * same identity the adjudicator resolves for it (`pos-gateway-framework`).
21
21
  */
22
22
  export function normaliseEntryId(entryId) {
23
23
  return entryId
@@ -14,7 +14,7 @@
14
14
  * AuthHandler: example-service-api
15
15
  * web-ui: example-web-ui
16
16
  * repo_aliases:
17
- * camera-bff: global-camera-bff
17
+ * stock-bff: northwind-stock-bff
18
18
  *
19
19
  * Either block is optional. Empty / missing file = no aliases (every
20
20
  * reference must resolve directly against the canonical id).
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Finding IDENTITY — the stable key a findings baseline is compared on, per
3
- * M-ADR 0132 §4.3 as amended, delivering the ruling recorded at ledger §3p.
3
+ * M-ADR 0132 §4.3 as amended.
4
4
  *
5
5
  * THE PROBLEM THIS EXISTS FOR, measured rather than asserted. A findings
6
6
  * baseline stores whole `[finding]` lines, so improving a message invalidates
@@ -17,7 +17,7 @@
17
17
  * files, 74 non-empty, 1,677 body lines — that works for 1,434 lines (85.5%)
18
18
  * and fails for 243 (14.5%), which embed their subject in prose:
19
19
  *
20
- * [finding] orphan: repo integration-calamp resolves to no capability ...
20
+ * [finding] orphan: repo integration-acme resolves to no capability ...
21
21
  *
22
22
  * One renderer template, all orphan class, spread across 62 repos. Recovering
23
23
  * identity there means teaching the comparator the renderer's message
@@ -30,7 +30,7 @@
30
30
  * baseline is reviewed by people and a side-channel nobody sees is a side
31
31
  * channel nobody maintains:
32
32
  *
33
- * [finding] orphan (repo:integration-calamp): repo integration-calamp ...
33
+ * [finding] orphan (repo:integration-acme): repo integration-acme ...
34
34
  * [finding] divergence (docs/domains/asset/product/x.traceability.yaml): ...
35
35
  *
36
36
  * A finding with no declared subject renders EXACTLY as it does today. That is
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `catalogue build --check`: are the committed catalogues what the committed specs build to?
3
- * (M-ADR 0218 §5, bead sdm-jts.6.)
3
+ * (M-ADR 0218 §5.)
4
4
  *
5
5
  * Builds into a temporary directory and compares, so it writes nothing into the SDD. Only what
6
6
  * the build PRODUCES is compared: a hand-written file that happens to sit in catalogues/ (a
@@ -123,7 +123,7 @@ const DataPlaneSchema = z.object({
123
123
  bindings: z.array(DataPlaneBindingSchema),
124
124
  deviations: z.array(DataPlaneDeviationSchema).optional(),
125
125
  });
126
- // Conformance coverage (M-ADR 0191 §4.3, second overlay element — WS4). An
126
+ // Conformance coverage (M-ADR 0191 §4.3, second overlay element). An
127
127
  // application-product asserts which of its archetype's conformance expectations
128
128
  // its own conformance suite exercises (`coverage`), and declares a rationale for
129
129
  // any expected tag it deliberately does not cover (`deviations`). The method
@@ -249,7 +249,7 @@ const ManifestSchema = z.object({
249
249
  // optional declared deviations from the archetype default, each carrying a
250
250
  // rationale so the mismatch is sanctioned variance, not silent drift.
251
251
  data_plane: DataPlaneSchema.optional(),
252
- // Conformance coverage declaration (M-ADR 0191 §4.3 WS4, sdd-manifest-schema
252
+ // Conformance coverage declaration (M-ADR 0191 §4.3, sdd-manifest-schema
253
253
  // 1.5.0). Which archetype conformance expectations the product's suite
254
254
  // exercises, plus rationales for any it deliberately does not.
255
255
  conformance: ConformanceSchema.optional(),
@@ -216,7 +216,7 @@ export async function runScaffoldDelta(session, options) {
216
216
  }
217
217
  /**
218
218
  * The delta's DEFECT classes are gate-acting (`divergence`), PROMOTED by a
219
- * recorded governance act after the M-ADR 0207 §4.9 pilot completed with no
219
+ * recorded method act after the M-ADR 0207 §4.9 pilot completed with no
220
220
  * surprise in any gated figure (2026-08-08) — the deliberate later act the
221
221
  * scores-coverage precedent reserves, performed. `scaffold-stale` stays
222
222
  * `coverage` with its staleness family: a stale anchor is a re-declare
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Per-schema compatibility ranges declared by this sdd-cli build.
3
3
  *
4
- * Per resolution of adopt-canonical-catalogue-builder.md §7.5, the three
5
- * sdd-method-distributed schemas version independently. The CLI declares
4
+ * The three sdd-method-distributed schemas version independently. The CLI declares
6
5
  * the version range it accepts for each and fails fast at read time on
7
6
  * out-of-range manifests.
8
7
  *
@@ -171,7 +171,7 @@ export const RUBRIC_ELIGIBILITY = [
171
171
  joinColumn: "id",
172
172
  refTransform: null,
173
173
  eligibilityFilter: null,
174
- eligibilityNote: "NO local population, measured rather than assumed. Refs are '<domain>-feature-grounding-<slug>' and the tails are SIBLING SDD SLUGS (app-shell, cold-chain, connect-rapide), not rows in this SDD: 0/15 against products, applications, features and repositories, and 1/15 against capabilities — a coincidence, not a join. A cross-SDD reference the member tier cannot resolve, so its coverage is declared unmeasurable rather than given a transform that would produce plausible wrong numbers.",
174
+ eligibilityNote: "NO local population, measured rather than assumed. Refs are '<domain>-feature-grounding-<slug>' and the tails are SIBLING SDD SLUGS (app-shell, gift-cards, storefront-express), not rows in this SDD: 0/15 against products, applications, features and repositories, and 1/15 against capabilities — a coincidence, not a join. A cross-SDD reference the member tier cannot resolve, so its coverage is declared unmeasurable rather than given a transform that would produce plausible wrong numbers.",
175
175
  },
176
176
  {
177
177
  rubricId: "reverse-contract-quality",
@@ -15,7 +15,7 @@
15
15
  * - Divider
16
16
  * archetype_overlays: # per-platform overlay (ADR 0169 §4.3)
17
17
  * web-generic:
18
- * strip_prefixes: [Mdc, Mde] # render-only: stripped when humanising labels
18
+ * strip_prefixes: [Nwc, Nwe] # render-only: stripped when humanising labels
19
19
  * overrides:
20
20
  * VBespokeGrid: grid
21
21
  * swiftui:
@@ -27,7 +27,7 @@
27
27
  * # services). Use when the federated SDD lacks a services.csv to
28
28
  * # gate emission and consumes rows would otherwise dangle at the
29
29
  * # aggregator with unresolved service_id.
30
- * - postman-tacho
30
+ * - postman-stock
31
31
  * - smoke-tests
32
32
  *
33
33
  * same_product_inference:
@@ -1,5 +1,5 @@
1
- // Staleness VERDICTS — the second half of M-ADR 0205 §4.5's staleness scope
2
- // (ledger §3o): "staleness meaning corpus_ref older than the target's last
1
+ // Staleness VERDICTS — the second half of M-ADR 0205 §4.5's staleness scope:
2
+ // "staleness meaning corpus_ref older than the target's last
3
3
  // content change". The scores-coverage predicate computes each row's BASIS;
4
4
  // this module computes the VERDICT the basis supports. A stale score is worse
5
5
  // than a missing one, because it reads as coverage — the predicate otherwise
@@ -262,8 +262,7 @@ function extractContracts(raw) {
262
262
  // entries used to raise disappeared — the declarations became invisible
263
263
  // to row emission and to finding emission at once. The builder had been
264
264
  // taught the object form (contracts.ts) while its only feeder had not:
265
- // the F36 shape again, one surface ahead of the pipeline that supplies
266
- // it. `spicedb` was likewise never extracted, so `.zed` declarations
265
+ // a builder one surface ahead of the pipeline that supplies it. `spicedb` was likewise never extracted, so `.zed` declarations
267
266
  // never reached any builder even as bare strings.
268
267
  const arr = (key) => {
269
268
  const v = o[key];
@@ -282,7 +281,7 @@ function extractContracts(raw) {
282
281
  const graphql = arr("graphql");
283
282
  // `provenance` (3.5.0) MUST be extracted here or the builder that reads it
284
283
  // sees nothing — this function reconstructs the block key-by-key rather than
285
- // passing the parsed object through, which is precisely the F36 shape the
284
+ // passing the parsed object through, which is precisely the shape the
286
285
  // comment above records. It was re-committed once during 3.5.0's own
287
286
  // implementation: the builder was taught to read provenance while this feeder
288
287
  // still dropped it, and only an end-to-end catalogue build caught it, because
@@ -19,7 +19,7 @@ const markdownProviderSignals = async (path, spec) => {
19
19
  const out = new Set();
20
20
  // `section` (ADR 0180 delta-pilot extension) scopes the scan to one heading block and accepts a plain
21
21
  // "Field" header, so a check can read ONE entity table inside a multi-table data-model doc (e.g.
22
- // data-model.mermaid.md "### Collection: feature") without duplicating the field list into a bespoke
22
+ // data-model.mermaid.md "### Collection: orders") without duplicating the field list into a bespoke
23
23
  // surface file. With no `section` the behaviour is byte-for-byte the legacy one (whole file, strict
24
24
  // header, no `*` stripping) — existing gateway bindings are unaffected.
25
25
  const section = spec?.section;
@@ -35,7 +35,7 @@ const markdownProviderSignals = async (path, spec) => {
35
35
  for (const raw of (await readFile(path, "utf-8")).split("\n")) {
36
36
  const s = raw.trim();
37
37
  if (section && s.startsWith("#")) {
38
- // strip leading #'s and any markdown code/emphasis in the title (`### Collection: ` + "`feature`")
38
+ // strip leading #'s and any markdown code/emphasis in the title (`### Collection: ` + "`orders`")
39
39
  inSection = s.replace(/^#+\s*/, "").replace(/[`*]/g, "").includes(section);
40
40
  inTable = false;
41
41
  continue;
@@ -61,7 +61,7 @@ const markdownProviderSignals = async (path, spec) => {
61
61
  if (!sig || sig === "—" || sig.startsWith("Computed") || sig.startsWith("MB "))
62
62
  continue;
63
63
  sig = sig.replace(/\([^)]*\)/g, "").replace(/\[\]/g, "").trim(); // drop inline "(...)" notes + array markers
64
- // Range shorthand: `datacoldTemp1..6` / `setpointTemp1..3` -> one declared name per index.
64
+ // Range shorthand: `sensor1..6` / `zone1..3` -> one declared name per index.
65
65
  const range = sig.match(/^(.*?)(\d+)\.\.(\d+)$/);
66
66
  if (range?.[1] !== undefined) {
67
67
  const base = range[1];
@@ -369,7 +369,7 @@ const REGISTRY = {
369
369
  function norm(s, mode) {
370
370
  if (mode === "leaf") {
371
371
  // Match a nested/dotted declared path against a leaf code identifier: take the last dotted
372
- // segment, drop array markers, then lower-nosep. `statusInfo.truStatus` ~ `truStatus`.
372
+ // segment, drop array markers, then lower-nosep. `order.status.code` ~ `code`.
373
373
  const leaf = s.split(".").pop() ?? s;
374
374
  return leaf.replace(/\[\]/g, "").replace(/[-_./ ]/g, "").toLowerCase();
375
375
  }
@@ -474,7 +474,28 @@ function printHuman(r, out) {
474
474
  out(` - ${m}\n`);
475
475
  }
476
476
  }
477
- function printReconcile(chk, r, out) {
477
+ /**
478
+ * Where the regenerated `produces` entries live: the spec's schema_dir, else the directory the binding's own
479
+ * declared entries already use (so a regenerated block keeps the adopter's layout), else `events`.
480
+ */
481
+ async function reconcileSchemaDir(targetRepo, rc) {
482
+ if (rc.schema_dir)
483
+ return rc.schema_dir.replace(/\/+$/, "");
484
+ const path = isAbsolute(rc.binding) ? rc.binding : resolve(targetRepo, rc.binding);
485
+ try {
486
+ const doc = parseYaml(await readFile(path, "utf-8"));
487
+ for (const p of doc?.events?.produces ?? []) {
488
+ const s = p?.schema;
489
+ if (typeof s === "string" && s.includes("/"))
490
+ return s.slice(0, s.lastIndexOf("/"));
491
+ }
492
+ }
493
+ catch {
494
+ /* an unreadable binding falls through to the default */
495
+ }
496
+ return "events";
497
+ }
498
+ function printReconcile(chk, r, schemaDir, out) {
478
499
  if (!chk.reconcile || r.target !== "binding-vs-reality")
479
500
  return;
480
501
  const rc = chk.reconcile;
@@ -486,7 +507,7 @@ function printReconcile(chk, r, out) {
486
507
  out(`${rc.block.split(".")[0]}:\n`);
487
508
  out(` produces: # ${full.length} schemas (was ${r.declared} declared)\n`);
488
509
  for (const name of full)
489
- out(` - schema: party/events/${name}.avsc\n`);
510
+ out(` - schema: ${schemaDir}/${name}.avsc\n`);
490
511
  if (r.declaredNotObserved.length) {
491
512
  out(` # DROP (dangling — no such schema in code/contracts): ${r.declaredNotObserved.join(", ")}\n`);
492
513
  }
@@ -546,8 +567,8 @@ export async function runConformance(opts, stdout = (line) => process.stdout.wri
546
567
  printHuman(r, stdout);
547
568
  if (opts.reconcile) {
548
569
  const chk = reconcileByline.get(r.id);
549
- if (chk)
550
- printReconcile(chk, r, stdout);
570
+ if (chk?.reconcile)
571
+ printReconcile(chk, r, await reconcileSchemaDir(opts.targetRepo, chk.reconcile), stdout);
551
572
  }
552
573
  }
553
574
  for (const id of skipped)
@@ -66,6 +66,8 @@ export function serialiseLockFile(lock) {
66
66
  ...(lock.ciRunner !== undefined ? [`ci_runner: "${lock.ciRunner}"`] : []),
67
67
  ...(lock.ciWorkflowBlockSha256 !== undefined ? [`ci_workflow_block_sha256: "${lock.ciWorkflowBlockSha256}"`] : []),
68
68
  ...(lock.ciModuleSha256 !== undefined ? [`ci_module_sha256: "${lock.ciModuleSha256}"`] : []),
69
+ ...(lock.cliSource !== undefined ? [`cli_source: ${lock.cliSource}`] : []),
70
+ ...(lock.cliPackage !== undefined ? [`cli_package: "${lock.cliPackage}"`] : []),
69
71
  "bundles:",
70
72
  ];
71
73
  for (const b of lock.bundles) {
@@ -117,6 +119,8 @@ function parseVersioned(raw) {
117
119
  let ciBinding;
118
120
  let ciRunner;
119
121
  let ciModuleSha256;
122
+ let cliSource;
123
+ let cliPackage;
120
124
  const bundles = [];
121
125
  let inBundles = false;
122
126
  let inPaths = false;
@@ -186,6 +190,10 @@ function parseVersioned(raw) {
186
190
  ciRunner = stripQuotes(value);
187
191
  else if (key === "ci_module_sha256")
188
192
  ciModuleSha256 = stripQuotes(value);
193
+ else if (key === "cli_source")
194
+ cliSource = stripQuotes(value);
195
+ else if (key === "cli_package")
196
+ cliPackage = stripQuotes(value);
189
197
  }
190
198
  continue;
191
199
  }
@@ -207,7 +215,7 @@ function parseVersioned(raw) {
207
215
  // a YAML reader parses 113 of 325 entries in one real lock as MAPPINGS
208
216
  // rather than strings — silently, with no error. Quoting on write is the
209
217
  // fix; reading both forms ships first so no lock can be written that an
210
- // older reader cannot parse. See method-open-items §3l.
218
+ // older reader cannot parse.
211
219
  const entry = stripQuotes(line.slice(" - ".length));
212
220
  const withDigest = DIGEST_ENTRY_RE.exec(entry);
213
221
  if (withDigest) {
@@ -313,6 +321,8 @@ function parseVersioned(raw) {
313
321
  ...(ciRunner !== undefined ? { ciRunner } : {}),
314
322
  ...(ciWorkflowBlockSha256 !== undefined ? { ciWorkflowBlockSha256 } : {}),
315
323
  ...(ciModuleSha256 !== undefined ? { ciModuleSha256 } : {}),
324
+ ...(cliSource !== undefined ? { cliSource } : {}),
325
+ ...(cliPackage !== undefined ? { cliPackage } : {}),
316
326
  };
317
327
  }
318
328
  function migrateV1(raw, legacyBundleName) {
@@ -83,7 +83,7 @@ export function getVerifyCliVersion() {
83
83
  }
84
84
  /**
85
85
  * The @sdd-method/maintain-cli version released WITH this sdd-cli. The managed CI workflow's
86
- * catalogue freshness step installs exactly this version (M-ADR 0218 §5; ruled 2026-10-15, sdm-jts.6).
86
+ * catalogue freshness step installs exactly this version (M-ADR 0218 §5).
87
87
  * Read the same way as the verify version, for the same reason (dist/ copy, never scripts/ live).
88
88
  */
89
89
  export function getMaintainCliVersion() {
@@ -82,7 +82,7 @@ export async function readManifest(areaRoot, id) {
82
82
  }
83
83
  if (!Array.isArray(raw.members))
84
84
  problems.push("members must be a list");
85
- // A hand-written revision of digits only (`50834756022`) parses as a NUMBER, and
85
+ // A hand-written revision of digits only (`12345678901`) parses as a NUMBER, and
86
86
  // a strict comparison with the folder's string suffix then calls an unchanged
87
87
  // member changed. Identity fields are strings, whatever YAML made of them.
88
88
  for (const m of raw.members ?? []) {
@@ -68,7 +68,7 @@ export function routeFinding(f, artefacts, shared = new Set()) {
68
68
  const targets = new Map();
69
69
  const paths = [...f.files.added, ...f.files.removed, ...f.files.modified].filter(citable);
70
70
  // The key alone, or in export-folder form (`<key>-master@<rev>`): a hyphen may
71
- // follow it only as a branch suffix, so `tcu` never matches inside `tcu-collector`.
71
+ // follow it only as a branch suffix, so `scanner` never matches inside `scanner-collector`.
72
72
  const keyRe = new RegExp(`(^|[^A-Za-z0-9_-])${f.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(-(master|main|develop|trunk)@|[^A-Za-z0-9_-]|$)`);
73
73
  const cites = (text, p) => shared.has(p) ? text.split("\n").some((line) => line.includes(p) && keyRe.test(line)) : text.includes(p);
74
74
  // Folder-qualified — `<key>-<branch>@<rev>/<path>` — names the member AND the file,
@@ -88,7 +88,7 @@ export function routeFinding(f, artefacts, shared = new Set()) {
88
88
  return [...targets.values()].sort((a, b) => (a.grain === b.grain ? a.artefact.localeCompare(b.artefact) : a.grain === "file" ? -1 : 1));
89
89
  }
90
90
  /**
91
- * Artefacts that mention a changed file by BASENAME only (`CollectorActor.java:49`) —
91
+ * Artefacts that mention a changed file by BASENAME only (`OrderHandler.java:49`) —
92
92
  * too ambiguous to route, too likely to matter to drop silently. Reported, not routed.
93
93
  */
94
94
  export function basenameOnly(f, artefacts, routed) {
@@ -54,7 +54,7 @@ export async function runValidate(opts, stdout = (line) => process.stdout.write(
54
54
  // passed" with none were previously the same line (M-ADR 0200 §4.3). The
55
55
  // exit code is deliberately unchanged — the defect is the claim, not the
56
56
  // gating; whether an unexecutable validator should FAIL is the open design
57
- // question (post-3.0.0-followons.md A2), not this one.
57
+ // question, not this one.
58
58
  function verdict(summary, selected) {
59
59
  if (summary.exitCode !== 0)
60
60
  return fail("One or more validators failed");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The method-managed validation job, as DATA (bead sdm-dd6.2; Dagger ruling sdm-s48, 2026-10-15).
2
+ * The method-managed validation job, as DATA (M-ADR 0219).
3
3
  *
4
4
  * One ordered plan per profile: every default validator, the platform's script-only checks, then the
5
5
  * advisory catalogue freshness check. Every executor renders or runs THIS: the GitHub workflow
@@ -25,7 +25,7 @@ const TITLES = {
25
25
  // CI has no platform checkout, so this step runs the app-only checks; the met gate needs the platform beside it.
26
26
  "app-dependencies": "Validate app dependencies (app-only checks; no platform checkout in CI)",
27
27
  };
28
- export function methodValidationPlan(profile) {
28
+ export function methodValidationPlan(profile, opts = {}) {
29
29
  const steps = validatorsForProfile(profile)
30
30
  .filter((v) => v.optional !== true)
31
31
  .map((v) => ({
@@ -38,12 +38,15 @@ export function methodValidationPlan(profile) {
38
38
  if (profile === "platform") {
39
39
  steps.push({ id: "cascade-completeness", title: "Validate cascade completeness", tool: "bash", args: ["orchestration/scripts/validate-cascade-completeness.sh", "all"], advisory: false }, { id: "architecture-alignment", title: "Validate architecture alignment", tool: "bash", args: ["orchestration/scripts/validate-architecture-alignment.sh"], advisory: false });
40
40
  }
41
+ const gating = opts.catalogueRebuild === true;
41
42
  steps.push({
42
43
  id: "catalogue-freshness",
43
- title: "Check catalogues reproduce from the specs (advisory)",
44
+ title: gating
45
+ ? "Check catalogues reproduce from the specs (gating: the pipeline rebuilt them)"
46
+ : "Check catalogues reproduce from the specs (advisory)",
44
47
  tool: "sdd-maintain",
45
48
  args: ["catalogue", "build", "--check", "--sdd-path", "."],
46
- advisory: true,
49
+ advisory: !gating,
47
50
  });
48
51
  return steps;
49
52
  }