@sdd-method/maintain-cli 0.8.0 → 0.10.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 (105) hide show
  1. package/dist/lib/agent-harness.js +2 -2
  2. package/dist/lib/behaviour-store/archive.js +28 -1
  3. package/dist/lib/behaviour-store/schema.js +85 -0
  4. package/dist/lib/briefing/index.js +1 -1
  5. package/dist/lib/briefing/render.js +1 -1
  6. package/dist/lib/briefing/variables.js +1 -1
  7. package/dist/lib/bundle.js +1 -1
  8. package/dist/lib/catalogue/aggregate.js +7 -7
  9. package/dist/lib/catalogue/application-archetype/vocabulary.js +2 -2
  10. package/dist/lib/catalogue/archetype/maps/android.yaml +2 -2
  11. package/dist/lib/catalogue/archetype/maps/swiftui.yaml +2 -2
  12. package/dist/lib/catalogue/archetype/maps/uikit.yaml +2 -2
  13. package/dist/lib/catalogue/archetype/maps/web-generic.yaml +2 -2
  14. package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +3 -3
  15. package/dist/lib/catalogue/archetype/resolver.js +4 -4
  16. package/dist/lib/catalogue/archetype/wireframe.js +1 -1
  17. package/dist/lib/catalogue/build.js +36 -33
  18. package/dist/lib/catalogue/builders/adr-supersedes.js +3 -3
  19. package/dist/lib/catalogue/builders/adrs.js +3 -3
  20. package/dist/lib/catalogue/builders/c4-containers.js +2 -2
  21. package/dist/lib/catalogue/builders/c4-entity-rollups.js +1 -1
  22. package/dist/lib/catalogue/builders/c4-external-systems.js +2 -2
  23. package/dist/lib/catalogue/builders/c4-systems.js +1 -1
  24. package/dist/lib/catalogue/builders/capabilities.js +1 -1
  25. package/dist/lib/catalogue/builders/capability-domain-model.js +1 -1
  26. package/dist/lib/catalogue/builders/capability-spec-walker.js +2 -2
  27. package/dist/lib/catalogue/builders/capability-spec.js +4 -4
  28. package/dist/lib/catalogue/builders/contracts.js +3 -3
  29. package/dist/lib/catalogue/builders/data-store-mapping.js +2 -2
  30. package/dist/lib/catalogue/builders/domain-models.js +2 -2
  31. package/dist/lib/catalogue/builders/domains.js +3 -3
  32. package/dist/lib/catalogue/builders/entities-manifest.js +8 -8
  33. package/dist/lib/catalogue/builders/entities-mermaid.js +4 -4
  34. package/dist/lib/catalogue/builders/entity-persistence.js +1 -1
  35. package/dist/lib/catalogue/builders/entity-slug.js +3 -3
  36. package/dist/lib/catalogue/builders/event-catalog-walker.js +6 -6
  37. package/dist/lib/catalogue/builders/event-edges.js +6 -6
  38. package/dist/lib/catalogue/builders/events.js +3 -3
  39. package/dist/lib/catalogue/builders/evidence-class.js +1 -1
  40. package/dist/lib/catalogue/builders/feature-domain-model.js +1 -1
  41. package/dist/lib/catalogue/builders/feature-grounding-walker.js +2 -2
  42. package/dist/lib/catalogue/builders/feature-grounding.js +5 -5
  43. package/dist/lib/catalogue/builders/feature-subscription-channels.js +3 -3
  44. package/dist/lib/catalogue/builders/features.js +1 -1
  45. package/dist/lib/catalogue/builders/glossary-xref.js +4 -4
  46. package/dist/lib/catalogue/builders/integrations.js +4 -4
  47. package/dist/lib/catalogue/builders/persona-c4system.js +1 -1
  48. package/dist/lib/catalogue/builders/product-edges.js +1 -1
  49. package/dist/lib/catalogue/builders/product-platform-dependencies.js +2 -2
  50. package/dist/lib/catalogue/builders/products.js +5 -5
  51. package/dist/lib/catalogue/builders/repo-edges.js +12 -12
  52. package/dist/lib/catalogue/builders/repositories.js +11 -7
  53. package/dist/lib/catalogue/builders/requirements.js +110 -11
  54. package/dist/lib/catalogue/builders/screen-tree.js +6 -6
  55. package/dist/lib/catalogue/builders/screens.js +18 -18
  56. package/dist/lib/catalogue/builders/service-contract-consumes.js +1 -1
  57. package/dist/lib/catalogue/builders/service-entity-tables.js +3 -3
  58. package/dist/lib/catalogue/builders/services.js +5 -5
  59. package/dist/lib/catalogue/builders/system-external-dependency.js +2 -2
  60. package/dist/lib/catalogue/canonical-schema.yaml +138 -2
  61. package/dist/lib/catalogue/coverage-render.js +15 -15
  62. package/dist/lib/catalogue/csv-writer.js +1 -1
  63. package/dist/lib/catalogue/feature-grounding-aliases-loader.js +1 -1
  64. package/dist/lib/catalogue/findings.js +1 -1
  65. package/dist/lib/catalogue/fk-validator.js +2 -2
  66. package/dist/lib/catalogue/manifest-loader.js +3 -3
  67. package/dist/lib/catalogue/rubric-coverage-map.yaml +28 -0
  68. package/dist/lib/catalogue/schema-compat.js +2 -2
  69. package/dist/lib/catalogue/schema-loader.js +1 -1
  70. package/dist/lib/catalogue/screen-validation-loader.js +2 -2
  71. package/dist/lib/catalogue/service-architecture-parser.js +1 -1
  72. package/dist/lib/catalogue/traceability-loader.js +6 -4
  73. package/dist/lib/ci-binding/module.js +4 -2
  74. package/dist/lib/generate-briefing/index.js +2 -2
  75. package/dist/lib/hooks-seed/entries.js +1 -1
  76. package/dist/lib/metamodel/tql-subset-parser.js +280 -0
  77. package/dist/lib/pack/builder.js +2 -2
  78. package/dist/lib/pack/index.js +1 -1
  79. package/dist/lib/pack/leakage.js +1 -1
  80. package/dist/lib/pack/publish-transform.js +2 -2
  81. package/dist/lib/pack/schema.js +2 -2
  82. package/dist/lib/pack/seed.js +2 -2
  83. package/dist/lib/pack/validate.js +1 -1
  84. package/dist/lib/repo-kind.js +4 -12
  85. package/dist/lib/scaffold/archetype-behaviour.js +173 -0
  86. package/dist/lib/scaffold/registry.js +136 -0
  87. package/dist/lib/sync/adr-transitions.js +4 -2
  88. package/dist/lib/sync/briefing-merge.js +2 -2
  89. package/dist/lib/sync/bundle-cache.js +4 -4
  90. package/dist/lib/sync/index.js +39 -12
  91. package/dist/lib/sync/settings-merge.js +1 -1
  92. package/dist/lib/sync/yaml-block-merge.js +1 -1
  93. package/dist/lib/validate/archetype-citations.js +169 -0
  94. package/dist/lib/validate/behaviour-store.js +161 -6
  95. package/dist/lib/validate/dispatch.js +8 -1
  96. package/dist/maintain-program.js +2 -2
  97. package/dist/maintain-version.json +2 -2
  98. package/dist/verbs/list.js +1 -1
  99. package/dist/verbs/pack-build.js +2 -2
  100. package/dist/verbs/render.js +1 -1
  101. package/dist/verify-program.js +2 -2
  102. package/dist/verify-version.json +2 -2
  103. package/package.json +2 -2
  104. package/scripts/validate-app-dependencies.sh +1 -1
  105. package/scripts/validate-integration-docs.sh +1 -1
@@ -1,4 +1,4 @@
1
- // Screens builder — emits the UI screens catalogue family (per ADR 0166):
1
+ // Screens builder — emits the UI screens catalogue family (per M-ADR 0166):
2
2
  // screens, ui_components, screen_component, screen_feature,
3
3
  // persona_screen, screen_action.
4
4
  //
@@ -7,7 +7,7 @@
7
7
  // ---
8
8
  // screen: <slug> # optional; defaults to filename stem
9
9
  // route: /path # optional
10
- // status: working # optional (ADR 0165 maturity vocabulary)
10
+ // status: working # optional (M-ADR 0165 maturity vocabulary)
11
11
  // persona_access: [<persona-slug>, ...] # -> persona_screen
12
12
  // realises: [<feature-slug>, ...] # -> screen_feature
13
13
  // ---
@@ -25,7 +25,7 @@
25
25
  // toolkit projects an app's router + component tree into the same shape (the
26
26
  // upstream step that produces these artefacts).
27
27
  //
28
- // Per ADR 0135 the rows are identical regardless of authoring direction; only
28
+ // Per M-ADR 0135 the rows are identical regardless of authoring direction; only
29
29
  // the `source` discriminator differs (screen-spec vs screen-extraction).
30
30
  //
31
31
  // Application-product profile. Like personas, the walker reads `screens/` at
@@ -36,9 +36,9 @@ import { basename, join } from "node:path";
36
36
  import { parse as parseYaml } from "yaml";
37
37
  import { analyzeComposition, maskStringLiterals, stripBlockComments } from "./screen-tree.js";
38
38
  import { isContainerArchetype } from "../archetype/resolver.js";
39
- /** Method default for the flat-composition threshold (ADR 0168 §4.2). */
39
+ /** Method default for the flat-composition threshold (M-ADR 0168 §4.2). */
40
40
  const DEFAULT_FLAT_THRESHOLD = 6;
41
- /** The only maturity value that asserts a faithful, complete composition (ADR 0168 §4.3). */
41
+ /** The only maturity value that asserts a faithful, complete composition (M-ADR 0168 §4.3). */
42
42
  const COMPLETENESS_STATUS = "working";
43
43
  const SCREENS_DIR_REL = "screens";
44
44
  /** Subdir of screens/ holding rendered wireframe artifacts — never screen specs. */
@@ -72,7 +72,7 @@ async function collectScreenSpecFiles(dir) {
72
72
  await walk("");
73
73
  return out.sort();
74
74
  }
75
- // Recognised standard maturity vocabulary (ADR 0165); other values pass through.
75
+ // Recognised standard maturity vocabulary (M-ADR 0165); other values pass through.
76
76
  const STANDARD_MATURITY = new Set(["working", "provisional", "unknown", "absent"]);
77
77
  export async function buildScreens(inputs) {
78
78
  const screens = [];
@@ -105,7 +105,7 @@ export async function buildScreens(inputs) {
105
105
  }
106
106
  const name = extractH1(text) || (typeof fm.name === "string" ? fm.name : stem);
107
107
  const status = typeof fm.status === "string" && fm.status !== "" ? maturity(fm.status, inputs.findings, screenId) : "";
108
- // Direction discriminator (ADR 0135): forward authoring is `screen-spec`,
108
+ // Direction discriminator (M-ADR 0135): forward authoring is `screen-spec`,
109
109
  // reverse extraction `screen-extraction`. Read from frontmatter, default
110
110
  // forward; only these two are recognised (others pass through, flagged).
111
111
  const source = screenSource(fm.source, inputs.findings, screenId);
@@ -130,7 +130,7 @@ export async function buildScreens(inputs) {
130
130
  evidence_class: evidenceClass(fm),
131
131
  source_sdd_id: "",
132
132
  });
133
- // Evidence integrity (ADR 0168 §4.2, defect f): a reverse-extracted screen
133
+ // Evidence integrity (M-ADR 0168 §4.2, defect f): a reverse-extracted screen
134
134
  // must cite its route + component evidence with a `[^…]` footnote. Forward-
135
135
  // authored screens make no such claim, so the check is reverse-scoped.
136
136
  if (source === "screen-extraction" && !/\[\^[^\]]+\]/.test(text)) {
@@ -142,7 +142,7 @@ export async function buildScreens(inputs) {
142
142
  }
143
143
  else {
144
144
  // Strip inline `#` comments before any scan so a `Capitalised(` token in a
145
- // comment can't emit a phantom component (ADR 0168 §4.4, defect g).
145
+ // comment can't emit a phantom component (M-ADR 0168 §4.4, defect g).
146
146
  const clean = stripBlockComments(block);
147
147
  // Component invocations: Capitalised call heads (e.g. Stack(, StatCard().
148
148
  // Local id references (lowercase, no parens) and list literals are skipped.
@@ -168,12 +168,12 @@ export async function buildScreens(inputs) {
168
168
  const aKey = `${screenId}__${screenSlug(action)}`;
169
169
  if (!screenAction.has(aKey)) {
170
170
  // event_id left empty: external-capable, resolved against the event
171
- // catalogue when an explicit mapping exists (best-effort per ADR 0166).
171
+ // catalogue when an explicit mapping exists (best-effort per M-ADR 0166).
172
172
  screenAction.set(aKey, { id: aKey, screen_id: screenId, action, event_id: "", source_sdd_id: "" });
173
173
  }
174
174
  }
175
- // Composition-depth validation (ADR 0168): structural fidelity floor +
176
- // status gate + the archetype-aware root check (ADR 0169). The vocabulary-
175
+ // Composition-depth validation (M-ADR 0168): structural fidelity floor +
176
+ // status gate + the archetype-aware root check (M-ADR 0169). The vocabulary-
177
177
  // free checks reason about tree shape + status only; root-is-leaf consults
178
178
  // the archetype map when the screen declares a `platform:`.
179
179
  const platform = typeof fm.platform === "string" ? fm.platform : undefined;
@@ -192,7 +192,7 @@ export async function buildScreens(inputs) {
192
192
  if (!fid) {
193
193
  // App/integration screens can realise a platform-resident feature
194
194
  // (MFE-over-platform: the platform owns the capability tree and its
195
- // features — ADR 0177 single-slug). Emit a deferred cross-SDD edge
195
+ // features — M-ADR 0177 single-slug). Emit a deferred cross-SDD edge
196
196
  // carrying the bare realises slug; the aggregator resolves it to the
197
197
  // platform feature id, and the FK validator logs it `[finding] external`
198
198
  // for this build (screen_feature.feature_id is external_for_profiles).
@@ -261,24 +261,24 @@ function emitDepthFindings(findings, screenId, status, m, ctx) {
261
261
  findings.add("coverage", `screen ${screenId}: composition is flat — root has ${m.rootChildCount} children at depth ${m.maxDepth} (screen-flat-composition)`);
262
262
  }
263
263
  // Status gate (keystone): `working` is the only maturity that asserts a
264
- // faithful, complete composition (ADR 0168 §4.3). provisional/unknown/absent
264
+ // faithful, complete composition (M-ADR 0168 §4.3). provisional/unknown/absent
265
265
  // make no completeness claim and are not gated.
266
266
  if (status === COMPLETENESS_STATUS && (empty || flat)) {
267
267
  findings.add("convention", `screen ${screenId}: status "${COMPLETENESS_STATUS}" exceeds composition depth — block is ${empty ? "empty" : "flat"} (screen-status-exceeds-depth)`, "error");
268
268
  }
269
- // Mis-rooted (ADR 0168 §4.2 screen-root-is-leaf, archetype-aware per ADR 0169):
269
+ // Mis-rooted (M-ADR 0168 §4.2 screen-root-is-leaf, archetype-aware per M-ADR 0169):
270
270
  // root is a leaf/control rather than a screen container. isContainer combines
271
271
  // the appears-with-children heuristic (containers) with the archetype map.
272
272
  // Only fires when the screen declares a known `platform:` — without a map the
273
273
  // heuristic alone can't distinguish an empty container root (Stack([])) from a
274
- // genuine leaf root (Button([])), so the check no-ops (ADR 0168 §4.5).
274
+ // genuine leaf root (Button([])), so the check no-ops (M-ADR 0168 §4.5).
275
275
  if (m.rootComponent !== "" && m.rootChildCount === 0) {
276
276
  const byMap = isContainerArchetype(m.rootComponent, platform, overlay);
277
277
  if (byMap === false && !m.containers.has(m.rootComponent)) {
278
278
  findings.add("convention", `screen ${screenId}: root component "${m.rootComponent}" is a leaf, not a container (screen-root-is-leaf)`, "error");
279
279
  }
280
280
  }
281
- // Styling-primitive leakage (ADR 0168 §4.2 screen-styling-primitive-as-structure):
281
+ // Styling-primitive leakage (M-ADR 0168 §4.2 screen-styling-primitive-as-structure):
282
282
  // an adopter-flagged decorative primitive composed as a structural component.
283
283
  // No-ops when the adopter declares no decorative set.
284
284
  if (decorativePrimitives !== undefined && decorativePrimitives.size > 0) {
@@ -317,7 +317,7 @@ function screenSlug(s) {
317
317
  function normalise(s) {
318
318
  return s.trim().toLowerCase().replace(/\s+/g, " ");
319
319
  }
320
- // Recognised direction discriminators (ADR 0135). Others pass through flagged.
320
+ // Recognised direction discriminators (M-ADR 0135). Others pass through flagged.
321
321
  const SCREEN_SOURCES = new Set(["screen-spec", "screen-extraction"]);
322
322
  function screenSource(value, findings, screenId) {
323
323
  if (typeof value !== "string" || value.trim() === "")
@@ -22,7 +22,7 @@
22
22
  // the platform-root SDD validates internally first per the fk-validator
23
23
  // optimistic-resolve rule.
24
24
  //
25
- // Pattern documented in sdd-method ADR 0147.
25
+ // Pattern documented in sdd-method M-ADR 0147.
26
26
  import { readFile, readdir } from "node:fs/promises";
27
27
  import { join } from "node:path";
28
28
  const REPOS_DIR_REL = "repos";
@@ -1,8 +1,8 @@
1
1
  // Primary reverse-method walker for the service_entity edge catalogue per
2
- // ADR 0159 §4.5. Reads per-domain `## Data Store Mapping` markdown tables in
2
+ // M-ADR 0159 §4.5. Reads per-domain `## Data Store Mapping` markdown tables in
3
3
  // `docs/domains/{domain}/architecture/data-model.mermaid.md` files.
4
4
  //
5
- // Prototype findings (per ADR 0159 — data-store-mapping table variations):
5
+ // Prototype findings (per M-ADR 0159 — data-store-mapping table variations):
6
6
  // - Two heading variants: `## Data Store Mapping` and `## Data Store Inventory`
7
7
  // (the latter is store-centric — different schema, no Entity column — and
8
8
  // gets skipped with a convention finding).
@@ -14,7 +14,7 @@
14
14
  // - Cross-domain entity references in tables resolve against entities.csv
15
15
  // from any domain (not just the table's own domain) — same-domain first.
16
16
  //
17
- // Service-name resolution rules (ADR 0159 §4.7):
17
+ // Service-name resolution rules (M-ADR 0159 §4.7):
18
18
  // 1. Try alias-map exact match (case-insensitive on display name).
19
19
  // 2. Try entitySlug(display_name) ⋈ entitySlug(services.name).
20
20
  // 3. Try entitySlug(display_name) ⋈ last_segment(services.id).
@@ -10,7 +10,7 @@
10
10
  // 0197 §4.1 — a dangling reference to it resolves to an orphan finding
11
11
  // elsewhere, not a crash here).
12
12
  //
13
- // Per ADR 0134 + Q7.1 resolution, runtime kinds are
13
+ // Per M-ADR 0134 + Q7.1 resolution, runtime kinds are
14
14
  // {api_service, worker, gateway, frontend, mobile} — read from the
15
15
  // canonical schema's enums.service_type block where
16
16
  // `emits_service_row: true`. The runtime-kind gate applies only to the
@@ -29,13 +29,13 @@
29
29
  // service-architecture-parser module.
30
30
  //
31
31
  // The `description` column has two authoring sources, honoured per the
32
- // forward/reverse parity principle (ADR 0157, extended to node columns by
33
- // ADR 0163):
32
+ // forward/reverse parity principle (M-ADR 0157, extended to node columns by
33
+ // M-ADR 0163):
34
34
  // - forward: `orchestration/repo-config/<repo>.yaml` `description:` —
35
35
  // the deliberate spec, authored by forward-method adopters.
36
36
  // - reverse: the arch doc's `## Purpose` lead paragraph.
37
37
  // When both are present for the same service the forward repo-config
38
- // description wins (matching the ADR 0156 / 0162 merge convention) and a
38
+ // description wins (matching the M-ADR 0156 / 0162 merge convention) and a
39
39
  // `divergence` finding records the conflict. In practice an adopter has
40
40
  // only one of the two, so this is usually a plain coalesce.
41
41
  //
@@ -111,7 +111,7 @@ export async function buildServices(inputs) {
111
111
  // repos/<group>/<repo>/docs/..., so a recursive walk is required.
112
112
  // Services whose architecture doc lives in a federated member SDD (rather
113
113
  // than this SDD's own repos/) resolve to no entry here — their metadata is
114
- // an aggregation-time concern (ADR 0163 P2), not a local build gap.
114
+ // an aggregation-time concern (M-ADR 0163 P2), not a local build gap.
115
115
  const archIndex = await indexServiceArchitectures(inputs.sddPath);
116
116
  const repoDescriptions = inputs.repoDescriptions ?? new Map();
117
117
  const declaredByRepo = inputs.declaredServicesByRepo ?? new Map();
@@ -1,12 +1,12 @@
1
1
  // system_external_dependency edge builder — emits one row per
2
2
  // `Rel(...)` line on an L1/L2 diagram that connects the SDD's own system
3
- // (or one of its containers) to an external system (per ADR 0155).
3
+ // (or one of its containers) to an external system (per M-ADR 0155).
4
4
  //
5
5
  // Resolution: the c4-external-systems builder supplies `externalIdByEntryId`
6
6
  // (diagram entry-id → ext-... row id). For each `Rel(a, b, "label")` where
7
7
  // exactly one endpoint is a known external entry-id, we emit an edge from
8
8
  // the SDD's c4_systems row to that external system. Container-level external
9
- // dependencies are intentionally collapsed to the system level — ADR 0155
9
+ // dependencies are intentionally collapsed to the system level — M-ADR 0155
10
10
  // §4.2 keeps the edge system-scoped.
11
11
  import { readFile } from "node:fs/promises";
12
12
  import { collectC4DiagramFiles } from "./c4-external-systems.js";
@@ -7,7 +7,7 @@
7
7
  # Emitted by sdd-cli's YAML emitter with no line folding; the three header scalars are quoted for the
8
8
  # textual readers downstream. The one deliberate difference from the authored form is schema_status:
9
9
  # the authored form says canonical; a projection never does.
10
- schema_version: "4.1.0"
10
+ schema_version: "4.2.0"
11
11
  schema_owner: "sdd-method"
12
12
  schema_status: "generated"
13
13
  enums:
@@ -643,6 +643,31 @@ enums:
643
643
  - value: observed
644
644
  description: Independent evidence from this repository's code matches (a baseline's characterization tests or a reverse snapshot).
645
645
  description: A requirement's status in one implementing repository (M-ADR 0221 §4.5).
646
+ change_status:
647
+ values:
648
+ - value: proposed
649
+ description: Open, with a proposed agreement record or none yet.
650
+ - value: agreed
651
+ description: "Open, with an agreed record: being built."
652
+ - value: superseded
653
+ description: Open, its record superseded by another change.
654
+ - value: withdrawn
655
+ description: Open, its record withdrawn.
656
+ - value: archived
657
+ description: Archived into the living specs.
658
+ description: Where a change stands (M-ADR 0222 §4.6, M-ADR 0223 §4.6).
659
+ judge_verdict:
660
+ values:
661
+ - value: PASS
662
+ - value: FAIL
663
+ description: The judge's verdict on a change (M-ADR 0223 §4.5).
664
+ work_order_status:
665
+ values:
666
+ - value: agreed
667
+ description: Agreed; the repository's work is not yet judged.
668
+ - value: completed
669
+ description: Marked completed by a passing judge.
670
+ description: A work order's status in its change's agreement record (M-ADR 0223 §4.2, §4.6).
646
671
  catalogues:
647
672
  domains:
648
673
  primary_key: id
@@ -4200,7 +4225,7 @@ catalogues:
4200
4225
  - name: change_id
4201
4226
  type: string
4202
4227
  nullable: false
4203
- description: The archived change's directory name (its archive date prefix included).
4228
+ description: The archived change's directory name (its archive date prefix included); a changes.id.
4204
4229
  - name: change_kind
4205
4230
  type: string
4206
4231
  nullable: false
@@ -4226,6 +4251,9 @@ catalogues:
4226
4251
  - column: requirement_id
4227
4252
  references: requirements.id
4228
4253
  nullable: false
4254
+ - column: change_id
4255
+ references: changes.id
4256
+ nullable: false
4229
4257
  column_enums:
4230
4258
  change_kind: enums.requirement_change_kind
4231
4259
  operation: enums.requirement_operation
@@ -4269,3 +4297,111 @@ catalogues:
4269
4297
  nullable: false
4270
4298
  column_enums:
4271
4299
  status: enums.requirement_status_value
4300
+ changes:
4301
+ primary_key: id
4302
+ columns:
4303
+ - name: id
4304
+ type: string
4305
+ nullable: false
4306
+ description: "The change's directory name: the open change's name, or the archived change's directory (its archive date prefix included). requirement_lineage.change_id references it."
4307
+ - name: name
4308
+ type: string
4309
+ nullable: false
4310
+ description: "The change's name without the archive date prefix: the same before and after archive, so a work item that cites the change keeps its link."
4311
+ - name: change_kind
4312
+ type: string
4313
+ nullable: false
4314
+ description: "Bound to requirement_change_kind: baseline (it carries baseline-evidence.md) or change."
4315
+ - name: status
4316
+ type: string
4317
+ nullable: false
4318
+ description: "Bound to change_status: archived for an archived change; otherwise the agreement record's status, or proposed when there is no record yet."
4319
+ - name: archived_on
4320
+ type: string
4321
+ nullable: true
4322
+ description: The archive date (YYYY-MM-DD) from the archived change's directory name; empty for an open change.
4323
+ - name: verdict
4324
+ type: string
4325
+ nullable: true
4326
+ description: "Bound to judge_verdict: the agreement record's verdict result (M-ADR 0223 §4.6). Empty for a baseline, and for a change not yet judged."
4327
+ - name: source_sdd_id
4328
+ type: string
4329
+ nullable: true
4330
+ description: Originating SDD (stamped at aggregation; empty in a per-SDD build).
4331
+ - name: platform_sdd_id
4332
+ type: string
4333
+ nullable: true
4334
+ description: The platform SDD the row belongs to (stamped at aggregation).
4335
+ description: "A change in the SDD's behaviour store (M-ADR 0223 §4.2), open or archived, including baseline changes. Derived, never authored: from the store's change directories and their agreement records (M-ADR 0222 §4.6)."
4336
+ column_enums:
4337
+ change_kind: enums.requirement_change_kind
4338
+ status: enums.change_status
4339
+ verdict: enums.judge_verdict
4340
+ change_work_order:
4341
+ primary_key: id
4342
+ columns:
4343
+ - name: id
4344
+ type: string
4345
+ nullable: false
4346
+ description: <change_id>__<repo_id>.
4347
+ - name: change_id
4348
+ type: string
4349
+ nullable: false
4350
+ - name: repo_id
4351
+ type: string
4352
+ nullable: false
4353
+ description: The repository the work order is for.
4354
+ - name: status
4355
+ type: string
4356
+ nullable: false
4357
+ description: "Bound to work_order_status: the record's status for the work order; a judge PASS marks it completed (M-ADR 0223 §4.6)."
4358
+ - name: source_sdd_id
4359
+ type: string
4360
+ nullable: true
4361
+ description: Originating SDD (stamped at aggregation; empty in a per-SDD build).
4362
+ - name: platform_sdd_id
4363
+ type: string
4364
+ nullable: true
4365
+ description: The platform SDD the row belongs to (stamped at aggregation).
4366
+ description: "Edge: a change's work order for one implementing repository (M-ADR 0223 §4.2), from the change's agreement record. Derived, never authored."
4367
+ foreign_keys:
4368
+ - column: change_id
4369
+ references: changes.id
4370
+ nullable: false
4371
+ - column: repo_id
4372
+ references: repositories.id
4373
+ nullable: false
4374
+ column_enums:
4375
+ status: enums.work_order_status
4376
+ change_work_item:
4377
+ primary_key: id
4378
+ columns:
4379
+ - name: id
4380
+ type: string
4381
+ nullable: false
4382
+ description: "<change_name>__<work_item_id>: keyed by the change's undated name, so the row is the same before and after archive."
4383
+ - name: change_id
4384
+ type: string
4385
+ nullable: false
4386
+ description: "The change as it stands now: its open name, or its archive directory."
4387
+ - name: change_name
4388
+ type: string
4389
+ nullable: false
4390
+ description: The change's undated name.
4391
+ - name: work_item_id
4392
+ type: string
4393
+ nullable: false
4394
+ description: The work item's id in the adopter's work-item tool, as the proposal's Work Items section gives it.
4395
+ - name: source_sdd_id
4396
+ type: string
4397
+ nullable: true
4398
+ description: Originating SDD (stamped at aggregation; empty in a per-SDD build).
4399
+ - name: platform_sdd_id
4400
+ type: string
4401
+ nullable: true
4402
+ description: The platform SDD the row belongs to (stamped at aggregation).
4403
+ description: "Annotation: a work item a change's proposal says it delivers (M-ADR 0223 §4.2), giving the roll-up from work item to changes to requirements. The work item's id is the adopter's work-item tool's (M-ADR 0035), so it has no foreign key."
4404
+ foreign_keys:
4405
+ - column: change_id
4406
+ references: changes.id
4407
+ nullable: false
@@ -281,47 +281,47 @@ export function renderContractCoverage(res, tier) {
281
281
  }
282
282
  // ─── the nine Phase-0 gate checks ────────────────────────────────────────
283
283
  export function renderEventCatalogueCheck(gaps) {
284
- return gaps.map((g) => `[finding] coverage: domain ${g.domain} event-catalog.yaml declares ${g.declared} events but events.csv contains ${g.emitted} rows for that domain (per ADR 0156 Phase 0)`);
284
+ return gaps.map((g) => `[finding] coverage: domain ${g.domain} event-catalog.yaml declares ${g.declared} events but events.csv contains ${g.emitted} rows for that domain (per M-ADR 0156 Phase 0)`);
285
285
  }
286
286
  export function renderRepoEdgesCheck(res) {
287
287
  const lines = [];
288
288
  if (res.repoCapabilityGap) {
289
289
  const g = res.repoCapabilityGap;
290
- lines.push(`[finding] coverage: repo_capability.csv is empty but ${g.capabilityService} capability_service rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method derivation likely missing (per ADR 0157 §4.3, Phase 0)`);
290
+ lines.push(`[finding] coverage: repo_capability.csv is empty but ${g.capabilityService} capability_service rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method derivation likely missing (per M-ADR 0157 §4.3, Phase 0)`);
291
291
  }
292
292
  if (res.repoContractForwardGap) {
293
293
  const g = res.repoContractForwardGap;
294
- lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.contracts} contracts and ${g.repoConfigsWithContracts} repo-configs with contracts: blocks present — forward-method derivation likely missing (per ADR 0157 §4.2, Phase 0)`);
294
+ lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.contracts} contracts and ${g.repoConfigsWithContracts} repo-configs with contracts: blocks present — forward-method derivation likely missing (per M-ADR 0157 §4.2, Phase 0)`);
295
295
  }
296
296
  if (res.repoContractPathXGap) {
297
297
  const g = res.repoContractPathXGap;
298
- lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.serviceContracts} service_contract rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method Path X (service_contract ⋈ services.primary_repo_id) derivation likely missing (per ADR 0157 §4.2, Phase 0)`);
298
+ lines.push(`[finding] coverage: repo_contract.csv is empty but ${g.serviceContracts} service_contract rows + ${g.servicesWithRepo} services with primary_repo_id present — reverse-method Path X (service_contract ⋈ services.primary_repo_id) derivation likely missing (per M-ADR 0157 §4.2, Phase 0)`);
299
299
  }
300
300
  if (res.repoContractReverseGap) {
301
301
  const g = res.repoContractReverseGap;
302
- lines.push(`[finding] coverage: repo_contract.csv is empty with ${g.contracts} contracts present and no repo-config.contracts: blocks or service_contract rows — reverse-method derivation via contracts.source_repo_id likely missing (per ADR 0157 §4.2, Phase 0)`);
302
+ lines.push(`[finding] coverage: repo_contract.csv is empty with ${g.contracts} contracts present and no repo-config.contracts: blocks or service_contract rows — reverse-method derivation via contracts.source_repo_id likely missing (per M-ADR 0157 §4.2, Phase 0)`);
303
303
  }
304
304
  return lines;
305
305
  }
306
306
  export function renderGlossaryXrefCheck(finding) {
307
307
  return finding
308
308
  ? [
309
- `[finding] coverage: docs/platform/reference/glossary-alignment-manifest.yaml exists but glossary_xref.csv emits 0 rows — multi-source derivation likely missing (per ADR 0158 §4.5, Phase 0)`,
309
+ `[finding] coverage: docs/platform/reference/glossary-alignment-manifest.yaml exists but glossary_xref.csv emits 0 rows — multi-source derivation likely missing (per M-ADR 0158 §4.5, Phase 0)`,
310
310
  ]
311
311
  : [];
312
312
  }
313
313
  export function renderDataModelEntitiesCheck(res) {
314
314
  const lines = [];
315
315
  if (res.entitiesGap) {
316
- lines.push(`[finding] coverage: ${res.entitiesGap.domainModels} domain-model files catalogued but entities.csv emits 0 rows — reverse-method entity extraction missing (per ADR 0159 Phase 0)`);
316
+ lines.push(`[finding] coverage: ${res.entitiesGap.domainModels} domain-model files catalogued but entities.csv emits 0 rows — reverse-method entity extraction missing (per M-ADR 0159 Phase 0)`);
317
317
  }
318
318
  if (res.serviceEntityGap) {
319
319
  const g = res.serviceEntityGap;
320
- lines.push(`[finding] coverage: ${g.entities} entities + ${g.services} services catalogued but service_entity.csv emits 0 rows — Data Store Mapping ingestion missing (per ADR 0159 Phase 0)`);
320
+ lines.push(`[finding] coverage: ${g.entities} entities + ${g.services} services catalogued but service_entity.csv emits 0 rows — Data Store Mapping ingestion missing (per M-ADR 0159 Phase 0)`);
321
321
  }
322
322
  if (res.containerEntityGap) {
323
323
  const g = res.containerEntityGap;
324
- lines.push(`[finding] coverage: ${g.containersWithService} containers map to services + ${g.serviceEntity} service_entity edges present but c4_container_entity.csv emits 0 rows — Phase 3 derivation missing (per ADR 0159 Phase 0)`);
324
+ lines.push(`[finding] coverage: ${g.containersWithService} containers map to services + ${g.serviceEntity} service_entity edges present but c4_container_entity.csv emits 0 rows — Phase 3 derivation missing (per M-ADR 0159 Phase 0)`);
325
325
  }
326
326
  return lines;
327
327
  }
@@ -329,24 +329,24 @@ export function renderCapabilityEventCheck(res) {
329
329
  const lines = [];
330
330
  if (res.derivableGap) {
331
331
  const g = res.derivableGap;
332
- lines.push(`[finding] coverage: service_event (${g.serviceEvent}) + capability_service (${g.capabilityService}) populated but capability_event emits 0 rows — reverse-derivable edge missing (per ADR 0160 Phase 0)`);
332
+ lines.push(`[finding] coverage: service_event (${g.serviceEvent}) + capability_service (${g.capabilityService}) populated but capability_event emits 0 rows — reverse-derivable edge missing (per M-ADR 0160 Phase 0)`);
333
333
  }
334
334
  if (res.authoredGap) {
335
- lines.push(`[finding] coverage: capability_event has ${res.authoredGap.derived} derived rows but 0 authored rows — no traceability.events[] or traceability.contracts.{published,consumed}_events declarations are landing in the catalogue. Governance queries filtering to source=authored will return empty (per ADR 0160 Phase 0)`);
335
+ lines.push(`[finding] coverage: capability_event has ${res.authoredGap.derived} derived rows but 0 authored rows — no traceability.events[] or traceability.contracts.{published,consumed}_events declarations are landing in the catalogue. Governance queries filtering to source=authored will return empty (per M-ADR 0160 Phase 0)`);
336
336
  }
337
337
  return lines;
338
338
  }
339
339
  export function renderFeatureEventCheck(res) {
340
340
  return res.gap
341
341
  ? [
342
- `[finding] coverage: service_event (${res.gap.serviceEvent}) + feature_service (${res.gap.featureService}) populated but feature_event emits 0 rows — reverse-derivable edge missing (per ADR 0161 Phase 0)`,
342
+ `[finding] coverage: service_event (${res.gap.serviceEvent}) + feature_service (${res.gap.featureService}) populated but feature_event emits 0 rows — reverse-derivable edge missing (per M-ADR 0161 Phase 0)`,
343
343
  ]
344
344
  : [];
345
345
  }
346
346
  export function renderPlatformDependenciesCheck(finding) {
347
347
  return finding
348
348
  ? [
349
- `[finding] coverage: app-platform-dependencies.yaml declares a platform_dependencies block but product_platform_dependencies.csv emits 0 rows — forward-method ingestion missing (per ADR 0162 Phase 0). Resolves once the builder reads the forward source.`,
349
+ `[finding] coverage: app-platform-dependencies.yaml declares a platform_dependencies block but product_platform_dependencies.csv emits 0 rows — forward-method ingestion missing (per M-ADR 0162 Phase 0). Resolves once the builder reads the forward source.`,
350
350
  ]
351
351
  : [];
352
352
  }
@@ -358,7 +358,7 @@ export function renderFeatureSpecPromotionCheck(res) {
358
358
  `[finding] coverage: ${f.intermediate} of ${f.total} features are reverse-intermediate ` +
359
359
  `(capability-spec/feature-grounding) not yet promoted to feature-spec — ` +
360
360
  `unresolvable as realises:/FK targets (${f.pctResolvable}% resolvable, ${f.note}; ` +
361
- `per ADR 0177 §4.2)`,
361
+ `per M-ADR 0177 §4.2)`,
362
362
  ];
363
363
  }
364
364
  export function renderFeatureSurfaceDuplicationCheck(res) {
@@ -370,7 +370,7 @@ export function renderFeatureSurfaceDuplicationCheck(res) {
370
370
  `[finding] coverage: ${f.pairs} near-duplicate feature surface(s) across ` +
371
371
  `${f.capabilities} ${capWord} — a capability-spec (rollup) and feature-grounding ` +
372
372
  `(detailed) slug naming the same surface; reconcile to one slug per surface, ` +
373
- `detailed slug canonical (per ADR 0177 §4.3)`,
373
+ `detailed slug canonical (per M-ADR 0177 §4.3)`,
374
374
  ];
375
375
  }
376
376
  // ─── Scores coverage (M-ADR 0205 §4.5, B2 — the fourteenth check) ─────────
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Deterministic CSV writer for catalogues.
3
3
  *
4
- * Emits headers + sorted rows per ADR 0134 builder contract:
4
+ * Emits headers + sorted rows per M-ADR 0134 builder contract:
5
5
  * - column order matches the canonical schema exactly,
6
6
  * - rows sorted by primary-key value,
7
7
  * - cells with embedded comma/quote/newline are RFC-4180 quoted,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Loader for the optional `.sdd/feature-grounding-aliases.yaml` config
3
- * per ADR 0143 (schema 2.5.0).
3
+ * per M-ADR 0143 (schema 2.5.0).
4
4
  *
5
5
  * Adopters use this map to translate display names in
6
6
  * `feature-grounding.md` tables (class names, file basenames, route
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Findings collector for the canonical builder per ADR 0134 / builder-spec
2
+ * Findings collector for the canonical builder per M-ADR 0134 / builder-spec
3
3
  * §"Emit findings as it parses".
4
4
  *
5
5
  * Findings are human-readable AND machine-parseable. Format per finding:
@@ -1,5 +1,5 @@
1
1
  /**
2
- * FK validator per ADR 0134 builder-spec §"Run internal FK validation
2
+ * FK validator per M-ADR 0134 builder-spec §"Run internal FK validation
3
3
  * before exiting" + §"External FK handling".
4
4
  *
5
5
  * For each catalogue's foreign keys:
@@ -9,7 +9,7 @@
9
9
  * - External FKs (external=true, or external for the build's profile via
10
10
  * `externalForProfiles` — schema 2.23.0): every non-empty value is logged
11
11
  * as a `[finding] external` line and skipped from validation. The
12
- * aggregator resolves these post-merge per ADR 0134 §4.5. A required
12
+ * aggregator resolves these post-merge per M-ADR 0134 §4.5. A required
13
13
  * (non-nullable) external FK still fails when EMPTY — the value must be
14
14
  * named even if it resolves cross-SDD.
15
15
  *
@@ -223,8 +223,8 @@ const ManifestSchema = z.object({
223
223
  // shape here. Whether the value resolves against the active vocabulary
224
224
  // (method ∪ RA-bundle ∪ adopter) is checked in the build step, which has
225
225
  // the bundle cache. Absent reads as "unclassified" (with a finding). Named
226
- // application_archetype to stay distinct from the ADR 0173 domain archetype
227
- // and the ADR 0169 openui archetype.
226
+ // application_archetype to stay distinct from the M-ADR 0173 domain archetype
227
+ // and the M-ADR 0169 openui archetype.
228
228
  application_archetype: z
229
229
  .string()
230
230
  .regex(/^[a-z][a-z0-9-]*$/, {
@@ -298,7 +298,7 @@ export async function loadSddManifest(sddPath, options) {
298
298
  const code = err.code;
299
299
  if (code === "ENOENT") {
300
300
  throw new Error(`SDD manifest not found at ${sourcePath}\n\n` +
301
- `Every SDD repo MUST declare an .sdd/sdd-manifest.yaml per ADR 0134 §4.6.\n` +
301
+ `Every SDD repo MUST declare an .sdd/sdd-manifest.yaml per M-ADR 0134 §4.6.\n` +
302
302
  `Schema: docs/method/methods/sdd-manifest-schema.yaml in sdd-method.\n` +
303
303
  `Bootstrap: \`sdd-cli init\` (when migrated to support manifest authoring).`);
304
304
  }
@@ -905,3 +905,31 @@ coverage:
905
905
  kind: enum
906
906
  scoring: not-applicable
907
907
  reason: closed vocabulary, not an artefact
908
+ changes:
909
+ kind: entity
910
+ scoring: not-applicable
911
+ reason: projected from the behaviour store, whose change gate and judge hold its quality (M-ADR 0222, M-ADR 0223)
912
+ change_work_order:
913
+ kind: edge
914
+ scoring: derived-edge
915
+ class: realisation
916
+ impact_flow: both
917
+ reason: derived from agreement records and verdicts
918
+ change_work_item:
919
+ kind: annotation
920
+ scoring: derived-edge
921
+ class: traceability
922
+ impact_flow: none
923
+ reason: derived from each change's proposal
924
+ change_status:
925
+ kind: enum
926
+ scoring: not-applicable
927
+ reason: closed vocabulary, not an artefact
928
+ judge_verdict:
929
+ kind: enum
930
+ scoring: not-applicable
931
+ reason: closed vocabulary, not an artefact
932
+ work_order_status:
933
+ kind: enum
934
+ scoring: not-applicable
935
+ reason: closed vocabulary, not an artefact
@@ -23,8 +23,8 @@
23
23
  * release; migration tooling (`sdd-cli migrate <schema> --to <v>`,
24
24
  * planned) covers downgrade paths.
25
25
  *
26
- * See ADR 0132 (catalogue-integrity gate's schema-drift digest hook) +
27
- * ADR 0134 §4.6 (canonical builder + aggregator).
26
+ * See M-ADR 0132 (catalogue-integrity gate's schema-drift digest hook) +
27
+ * M-ADR 0134 §4.6 (canonical builder + aggregator).
28
28
  */
29
29
  export const SCHEMA_COMPAT = {
30
30
  catalogue_schema: "^4.0.0",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Loader for the canonical catalogue-schema.yaml vendored from sdd-method.
3
3
  *
4
- * Per ADR 0134 §4.6 the canonical builder reads the schema rather than
4
+ * Per M-ADR 0134 §4.6 the canonical builder reads the schema rather than
5
5
  * hardcoding column shapes or FK rules. This loader is the only place in
6
6
  * the CLI that knows the schema file's location.
7
7
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Loader for the optional `.sdd/screen-validation.yaml` config — the
3
3
  * adopter-tunable parameters for screen-spec composition-depth validation
4
- * (ADR 0168 §4.5) and the archetype overlay (ADR 0169 §4.3).
4
+ * (M-ADR 0168 §4.5) and the archetype overlay (M-ADR 0169 §4.3).
5
5
  *
6
6
  * Every field is optional and carries a method default; an absent file means
7
7
  * "method defaults only" (the vocabulary-free checks all still run). Follows
@@ -13,7 +13,7 @@
13
13
  * decorative_primitives: # unlocks screen-styling-primitive-as-structure
14
14
  * - Spacer
15
15
  * - Divider
16
- * archetype_overlays: # per-platform overlay (ADR 0169 §4.3)
16
+ * archetype_overlays: # per-platform overlay (M-ADR 0169 §4.3)
17
17
  * web-generic:
18
18
  * strip_prefixes: [Nwc, Nwe] # render-only: stripped when humanising labels
19
19
  * overrides:
@@ -3,7 +3,7 @@
3
3
  // Extracted from builders/services.ts so two callers can share it:
4
4
  // - the per-SDD services builder (buildServices), and
5
5
  // - the aggregator's federated reverse-metadata enrichment pass
6
- // (ADR 0163 P2), which reaches member SDDs' arch docs that the
6
+ // (M-ADR 0163 P2), which reaches member SDDs' arch docs that the
7
7
  // platform-root's own build never sees.
8
8
  //
9
9
  // The metadata is read faithfully — not inferred — from three blocks: