@sdd-method/author-cli 0.0.0-stage → 0.1.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 (203) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +17 -0
  3. package/README.md +6 -2
  4. package/dist/author-bin.js +8 -0
  5. package/dist/author-program.js +99 -0
  6. package/dist/lib/adr-origin.js +59 -0
  7. package/dist/lib/agent-harness.js +37 -0
  8. package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +18 -0
  9. package/dist/lib/api-surface/analyzers/library.js +95 -0
  10. package/dist/lib/api-surface/analyzers/serverless.js +336 -0
  11. package/dist/lib/api-surface/analyzers/spring.js +237 -0
  12. package/dist/lib/api-surface/analyzers/terraform.js +109 -0
  13. package/dist/lib/api-surface/detect.js +25 -0
  14. package/dist/lib/api-surface/emit.js +109 -0
  15. package/dist/lib/api-surface/file-set.js +43 -0
  16. package/dist/lib/api-surface/index.js +134 -0
  17. package/dist/lib/api-surface/types.js +17 -0
  18. package/dist/lib/briefing/index.js +13 -0
  19. package/dist/lib/briefing/render.js +132 -0
  20. package/dist/lib/briefing/variables.js +92 -0
  21. package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
  22. package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
  23. package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
  24. package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
  25. package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
  26. package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
  27. package/dist/lib/catalogue/archetype/resolver.js +103 -0
  28. package/dist/lib/catalogue/archetype/wireframe.js +418 -0
  29. package/dist/lib/catalogue/baseline-check.js +151 -0
  30. package/dist/lib/catalogue/build.js +1502 -0
  31. package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
  32. package/dist/lib/catalogue/builders/adrs.js +222 -0
  33. package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
  34. package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
  35. package/dist/lib/catalogue/builders/c4-components.js +229 -0
  36. package/dist/lib/catalogue/builders/c4-containers.js +240 -0
  37. package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
  38. package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
  39. package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
  40. package/dist/lib/catalogue/builders/c4-systems.js +98 -0
  41. package/dist/lib/catalogue/builders/capabilities.js +488 -0
  42. package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
  43. package/dist/lib/catalogue/builders/capability-edges.js +256 -0
  44. package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
  45. package/dist/lib/catalogue/builders/capability-spec.js +348 -0
  46. package/dist/lib/catalogue/builders/container-dependency.js +183 -0
  47. package/dist/lib/catalogue/builders/contracts.js +402 -0
  48. package/dist/lib/catalogue/builders/data-items.js +344 -0
  49. package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
  50. package/dist/lib/catalogue/builders/domain-models.js +175 -0
  51. package/dist/lib/catalogue/builders/domains.js +119 -0
  52. package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
  53. package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
  54. package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
  55. package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
  56. package/dist/lib/catalogue/builders/entity-slug.js +47 -0
  57. package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
  58. package/dist/lib/catalogue/builders/event-edges.js +327 -0
  59. package/dist/lib/catalogue/builders/events.js +168 -0
  60. package/dist/lib/catalogue/builders/evidence-class.js +43 -0
  61. package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
  62. package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
  63. package/dist/lib/catalogue/builders/feature-contract.js +77 -0
  64. package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
  65. package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
  66. package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
  67. package/dist/lib/catalogue/builders/feature-groups.js +63 -0
  68. package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
  69. package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
  70. package/dist/lib/catalogue/builders/features.js +300 -0
  71. package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
  72. package/dist/lib/catalogue/builders/glossary.js +204 -0
  73. package/dist/lib/catalogue/builders/implementations.js +214 -0
  74. package/dist/lib/catalogue/builders/index.js +64 -0
  75. package/dist/lib/catalogue/builders/integration-components.js +344 -0
  76. package/dist/lib/catalogue/builders/integrations.js +181 -0
  77. package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
  78. package/dist/lib/catalogue/builders/persona-container.js +133 -0
  79. package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
  80. package/dist/lib/catalogue/builders/personas.js +152 -0
  81. package/dist/lib/catalogue/builders/product-edges.js +106 -0
  82. package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
  83. package/dist/lib/catalogue/builders/products.js +162 -0
  84. package/dist/lib/catalogue/builders/quality-scores.js +635 -0
  85. package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
  86. package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
  87. package/dist/lib/catalogue/builders/repo-edges.js +340 -0
  88. package/dist/lib/catalogue/builders/repositories.js +458 -0
  89. package/dist/lib/catalogue/builders/scaffolds.js +197 -0
  90. package/dist/lib/catalogue/builders/screen-tree.js +222 -0
  91. package/dist/lib/catalogue/builders/screens.js +396 -0
  92. package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
  93. package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
  94. package/dist/lib/catalogue/builders/service-contract.js +74 -0
  95. package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
  96. package/dist/lib/catalogue/builders/services.js +232 -0
  97. package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
  98. package/dist/lib/catalogue/builders/user-journeys.js +253 -0
  99. package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
  100. package/dist/lib/catalogue/cli-version.js +10 -0
  101. package/dist/lib/catalogue/contract-id.js +49 -0
  102. package/dist/lib/catalogue/coverage-predicates.js +603 -0
  103. package/dist/lib/catalogue/coverage-render.js +552 -0
  104. package/dist/lib/catalogue/coverage-run.js +115 -0
  105. package/dist/lib/catalogue/csv-reader.js +97 -0
  106. package/dist/lib/catalogue/csv-writer.js +96 -0
  107. package/dist/lib/catalogue/data-plane-coherence.js +77 -0
  108. package/dist/lib/catalogue/ddl-generator.js +233 -0
  109. package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
  110. package/dist/lib/catalogue/engine-validator.js +239 -0
  111. package/dist/lib/catalogue/ext-id.js +76 -0
  112. package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
  113. package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
  114. package/dist/lib/catalogue/finding-identity.js +183 -0
  115. package/dist/lib/catalogue/findings.js +46 -0
  116. package/dist/lib/catalogue/fk-validator.js +136 -0
  117. package/dist/lib/catalogue/freshness.js +70 -0
  118. package/dist/lib/catalogue/gate-checks.js +338 -0
  119. package/dist/lib/catalogue/graph-projection.js +346 -0
  120. package/dist/lib/catalogue/impact-run.js +228 -0
  121. package/dist/lib/catalogue/manifest-loader.js +435 -0
  122. package/dist/lib/catalogue/platform-scope.js +7 -0
  123. package/dist/lib/catalogue/population-checks.js +87 -0
  124. package/dist/lib/catalogue/relationship-semantics.js +66 -0
  125. package/dist/lib/catalogue/reverse-product-quality.js +99 -0
  126. package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
  127. package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
  128. package/dist/lib/catalogue/scaffold-delta.js +261 -0
  129. package/dist/lib/catalogue/schema-compat.js +52 -0
  130. package/dist/lib/catalogue/schema-loader.js +87 -0
  131. package/dist/lib/catalogue/scores-coverage.js +573 -0
  132. package/dist/lib/catalogue/screen-validation-loader.js +89 -0
  133. package/dist/lib/catalogue/service-architecture-parser.js +193 -0
  134. package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
  135. package/dist/lib/catalogue/staleness-verdicts.js +345 -0
  136. package/dist/lib/catalogue/traceability-loader.js +350 -0
  137. package/dist/lib/catalogue/validate.js +200 -0
  138. package/dist/lib/catalogue/version.js +18 -0
  139. package/dist/lib/conformance/binding.js +70 -0
  140. package/dist/lib/conformance/finding.js +69 -0
  141. package/dist/lib/conformance/index.js +578 -0
  142. package/dist/lib/context-pull/index.js +191 -0
  143. package/dist/lib/context-pull/resolve.js +70 -0
  144. package/dist/lib/context-pull/retired-mcp-setup.js +7 -0
  145. package/dist/lib/contracts-availability/index.js +267 -0
  146. package/dist/lib/generate-briefing/index.js +77 -0
  147. package/dist/lib/list/discover.js +100 -0
  148. package/dist/lib/list/format.js +84 -0
  149. package/dist/lib/list/index.js +16 -0
  150. package/dist/lib/lock-file.js +437 -0
  151. package/dist/lib/pack/log.js +18 -0
  152. package/dist/lib/pack/schema.js +121 -0
  153. package/dist/lib/pack/seed.js +182 -0
  154. package/dist/lib/pack/validate.js +49 -0
  155. package/dist/lib/package-version.js +92 -0
  156. package/dist/lib/plan/index.js +69 -0
  157. package/dist/lib/plan/read-sdd.js +102 -0
  158. package/dist/lib/plan/render-template.js +84 -0
  159. package/dist/lib/repo-kind.js +129 -0
  160. package/dist/lib/scaffold/domain.js +234 -0
  161. package/dist/lib/scaffold/registry.js +136 -0
  162. package/dist/lib/scripts.js +7 -0
  163. package/dist/lib/sdd-index/repo-config.js +245 -0
  164. package/dist/lib/sdd-reader.js +91 -0
  165. package/dist/lib/snapshot/advance.js +454 -0
  166. package/dist/lib/snapshot/diff.js +190 -0
  167. package/dist/lib/snapshot/fold.js +208 -0
  168. package/dist/lib/snapshot/land.js +178 -0
  169. package/dist/lib/snapshot/manifest.js +206 -0
  170. package/dist/lib/snapshot/targets.js +169 -0
  171. package/dist/lib/snapshot/verify.js +91 -0
  172. package/dist/lib/sync/briefing-merge.js +106 -0
  173. package/dist/lib/sync/bundle-cache.js +109 -0
  174. package/dist/lib/sync/yaml-block-merge.js +120 -0
  175. package/dist/lib/validate/aggregate.js +51 -0
  176. package/dist/lib/validate/bundle-coherence.js +74 -0
  177. package/dist/lib/validate/dispatch.js +243 -0
  178. package/dist/lib/validate/index.js +110 -0
  179. package/dist/lib/validate/plan.js +54 -0
  180. package/dist/lib/validate/runner.js +85 -0
  181. package/dist/lib/validate/yaml-parse.js +112 -0
  182. package/dist/maintain-version.json +4 -0
  183. package/dist/verbs/api-surface.js +71 -0
  184. package/dist/verbs/catalogue-check.js +28 -0
  185. package/dist/verbs/catalogue-verify.js +114 -0
  186. package/dist/verbs/conformance.js +34 -0
  187. package/dist/verbs/context.js +39 -0
  188. package/dist/verbs/contracts.js +34 -0
  189. package/dist/verbs/generate-briefing.js +29 -0
  190. package/dist/verbs/list.js +45 -0
  191. package/dist/verbs/pack-validate.js +17 -0
  192. package/dist/verbs/path-option.js +25 -0
  193. package/dist/verbs/plan.js +42 -0
  194. package/dist/verbs/render.js +87 -0
  195. package/dist/verbs/scaffold.js +83 -0
  196. package/dist/verbs/snapshot.js +134 -0
  197. package/dist/verbs/validate.js +48 -0
  198. package/dist/verify-program.js +122 -0
  199. package/dist/verify-version.json +4 -0
  200. package/package.json +32 -4
  201. package/scripts/validate-app-dependencies.sh +664 -0
  202. package/scripts/validate-app-docs.sh +199 -0
  203. package/scripts/validate-integration-docs.sh +505 -0
@@ -0,0 +1,256 @@
1
+ // Edge builders for capability_* catalogues — capability_service,
2
+ // capability_adr, capability_feature, capability_contract.
3
+ //
4
+ // All four read traceability YAMLs as the authoritative source. The
5
+ // canonical schema declares capability_id and the target column as
6
+ // required FKs — so each builder skips entries whose target doesn't
7
+ // resolve in the known-target set passed in.
8
+ // capability_service is the exception (F4, schema 2.29.0): its service_id
9
+ // FK is external, and an entry marked `owner_sdd:` declares a cross-SDD
10
+ // reference — the edge is emitted despite not resolving locally and is
11
+ // validated hard at aggregation against the owning sibling's rows.
12
+ // Every other skipped `services[].id` still surfaces as a finding (orphan,
13
+ // or convention when the id names a known external system) — unresolved
14
+ // edges must never drop silently.
15
+ //
16
+ // capability_event is intentionally not built here: its source per the
17
+ // metamodel is `## Related Events` markdown tables in capability specs,
18
+ // which depends on a spec-formatting convention that's only partial in
19
+ // the field. Defer to a later phase.
20
+ import { basename, sep } from "node:path";
21
+ import { declaredContractPaths } from "../traceability-loader.js";
22
+ import { ORIGIN_PREFIX_SRC } from "../../adr-origin.js";
23
+ export function buildCapabilityService(inputs) {
24
+ const rows = [];
25
+ const seen = new Set();
26
+ for (const m of inputs.manifests) {
27
+ const capId = m.capability.id;
28
+ if (!inputs.knownCapabilityIds.has(capId)) {
29
+ // M-ADR 0211 §4.3 — the second bare `continue` in this file, now named. A manifest
30
+ // whose capability produced no row loses every services[] entry; that used to
31
+ // happen in silence, the same shape as the spec-only stem collision.
32
+ const n = (m.traceability.services ?? []).length;
33
+ if (n > 0) {
34
+ inputs.findings.add("coverage", `capability_service: ${m.relPath} declares ${n} services[] entr${n === 1 ? "y" : "ies"} but its capability "${capId}" produced no capabilities row — all ${n} dropped (M-ADR 0211 §4.3)`);
35
+ }
36
+ continue;
37
+ }
38
+ for (const entry of m.traceability.services ?? []) {
39
+ const sid = entry.id;
40
+ if (!inputs.knownServiceIds.has(sid)) {
41
+ // Never drop silently (61 missing edges sat invisible in the field
42
+ // until a manual sweep). Resolution order:
43
+ // - `owner_sdd:` marked (F4, schema 2.29.0) — a DECLARED cross-SDD
44
+ // reference (e.g. the service moved to a sibling at a carve).
45
+ // Emit the edge; the external FK logs it and aggregation
46
+ // validates it hard against the sibling's rows.
47
+ // - id naming a known external system — a modelling convention.
48
+ // - anything else — a dangling reference, member-tier orphan
49
+ // finding. NOT emitted: measured in the field, almost all
50
+ // unmarked danglings are local authoring gaps, and emitting
51
+ // them would fail aggregation on a defect that isn't theirs.
52
+ if (entry.owner_sdd !== undefined) {
53
+ // Emit and fall through — the external FK validation logs the
54
+ // single `[finding] external` line for it (one fact, one finding).
55
+ }
56
+ else if (inputs.knownExternalSystemIds?.has(sid)) {
57
+ inputs.findings.add("convention", `capability_service: ${capId} services[] id "${sid}" resolves to an external system, not a service — edge skipped`);
58
+ continue;
59
+ }
60
+ else {
61
+ // Name the remedy the branch above already implements (M-ADR 0199
62
+ // §4.4: a remedy must be true at the emitting tier). Measured cost
63
+ // of not naming it: an adopter read this line, concluded a
64
+ // cross-SDD capability_service edge was unrepresentable, and
65
+ // reported two permanently-unresolvable orphans that were one
66
+ // authored line from resolving.
67
+ inputs.findings.add("orphan", `capability_service: ${capId} services[] id "${sid}" — no matching service (edge skipped). ` +
68
+ `If that service is owned by a SIBLING SDD, declare \`owner_sdd:\` on this entry ` +
69
+ `(schema 2.29.0) — the edge is then emitted and validated at aggregation. ` +
70
+ `Otherwise it is a local authoring gap: fix the id, or add the service.`);
71
+ continue;
72
+ }
73
+ }
74
+ const role = entry.role ?? "";
75
+ const id = `${capId}__${sid}__${role !== "" ? role : "related"}`;
76
+ if (seen.has(id))
77
+ continue;
78
+ seen.add(id);
79
+ rows.push({
80
+ id,
81
+ capability_id: capId,
82
+ service_id: sid,
83
+ role,
84
+ // M-ADR 0211 §4.1 / schema 3.9.0 — the dependency's contract and wire coupling as
85
+ // catalogued fields. Empty when the entry declares none; the validator has already
86
+ // held `interaction_pattern` to the bound vocabulary and `contract` to resolution.
87
+ contract_id: entry.contract ?? "",
88
+ interaction_pattern: entry.interaction_pattern ?? "",
89
+ qualifier: entry.qualifier ?? "",
90
+ evidence_ref: entry.evidence ?? "",
91
+ });
92
+ }
93
+ }
94
+ return rows;
95
+ }
96
+ export function buildCapabilityAdr(inputs) {
97
+ const rows = [];
98
+ const seen = new Set();
99
+ for (const m of inputs.manifests) {
100
+ const capId = m.capability.id;
101
+ if (!inputs.knownCapabilityIds.has(capId))
102
+ continue;
103
+ for (const entry of m.traceability.adrs ?? []) {
104
+ const adrId = canonicaliseAdrId(entry.id);
105
+ if (adrId === "")
106
+ continue;
107
+ if (!inputs.knownAdrIds.has(adrId))
108
+ continue;
109
+ const id = `${capId}__${adrId}`;
110
+ if (seen.has(id))
111
+ continue;
112
+ seen.add(id);
113
+ rows.push({
114
+ id,
115
+ capability_id: capId,
116
+ adr_id: adrId,
117
+ confidence: "spec",
118
+ });
119
+ }
120
+ }
121
+ return rows;
122
+ }
123
+ export function buildCapabilityFeature(inputs) {
124
+ const rows = [];
125
+ const seen = new Set();
126
+ for (const m of inputs.manifests) {
127
+ const capId = m.capability.id;
128
+ if (!inputs.knownCapabilityIds.has(capId))
129
+ continue;
130
+ for (const path of m.traceability.runway?.enables ?? []) {
131
+ const featureId = resolveFeaturePath(path, inputs.featureIdByPath);
132
+ if (featureId === "")
133
+ continue;
134
+ const id = `${capId}__${featureId}`;
135
+ if (seen.has(id))
136
+ continue;
137
+ seen.add(id);
138
+ rows.push({
139
+ id,
140
+ capability_id: capId,
141
+ feature_id: featureId,
142
+ });
143
+ }
144
+ }
145
+ return rows;
146
+ }
147
+ export function buildCapabilityContract(inputs) {
148
+ const rows = [];
149
+ const seen = new Set();
150
+ for (const m of inputs.manifests) {
151
+ const capId = m.capability.id;
152
+ if (!inputs.knownCapabilityIds.has(capId))
153
+ continue;
154
+ const block = m.traceability.contracts;
155
+ if (!block)
156
+ continue;
157
+ const all = [];
158
+ // Each list holds bare string paths AND object entries ({path,
159
+ // external} — the D13 declared-external form). Both contribute their
160
+ // path here: a declared-external entry's path resolves against the
161
+ // merged id index (which includes the rows buildDeclaredExternalContracts
162
+ // just emitted), which is exactly how an external contract gains its
163
+ // capability_contract edge. spicedb joins the walk with the loader —
164
+ // .zed declarations previously never reached this builder at all.
165
+ // Iterates the SHARED kind list, not a hand-written one. This site is why
166
+ // that helper exists: it carried a four-kind array while discovery, mapping
167
+ // and the loader all knew five, so an estate's 9 correct graphql declarations
168
+ // produced ZERO capability_contract edges and left 7 orphans standing. The
169
+ // comment below already recorded the same bug one kind earlier.
170
+ for (const path of declaredContractPaths(block)) {
171
+ all.push(path);
172
+ }
173
+ for (const path of all) {
174
+ const contractId = resolveContractPath(path, inputs.contractIdByPath);
175
+ if (contractId === "") {
176
+ // Never drop silently. This guard dropped every unresolvable entry with no
177
+ // trace until WS2 of the coverage-invariants work measured it: 62 of 188
178
+ // declared entries across two estates, and NOT ONE of them dangling — every
179
+ // one named a real artefact. So the resolution failure is the interesting
180
+ // fact, and the reader needs it.
181
+ //
182
+ // WORDING IS DELIBERATELY CAUSE-AGNOSTIC (M-ADR 0189 §8 / M-ADR 0199 §4.4).
183
+ // The tempting remedy — "declare the contract" or "fix the path" — is FALSE
184
+ // for the measured majority: 56 of the 62 name a spec that exists at exactly
185
+ // that path in a sibling code repository (the M-ADR 0110 sibling-repo
186
+ // pattern), which the contracts discoverer does not walk, so there is
187
+ // nothing for the author to fix. 4 more name a contract row owned by a
188
+ // sibling SDD, which this edge has no cross-SDD form to express. State the
189
+ // fact, name the candidate causes, prescribe nothing.
190
+ inputs.findings.add("orphan", `capability_contract: ${m.relPath} traceability.contracts entry "${path}" `
191
+ + `resolved to no contract row — edge dropped. The spec may sit outside `
192
+ + `this SDD's contract-discovery paths (contracts/{openapi,proto,avro}/ or `
193
+ + `docs/domains/*/contracts/{openapi,proto,avro}/), or name a contract owned `
194
+ + `by a sibling SDD — capability_contract has no cross-SDD form, unlike `
195
+ + `capability_service's owner_sdd`);
196
+ continue;
197
+ }
198
+ const id = `${capId}__${contractId}`;
199
+ if (seen.has(id))
200
+ continue;
201
+ seen.add(id);
202
+ rows.push({
203
+ id,
204
+ capability_id: capId,
205
+ contract_id: contractId,
206
+ source: "declared",
207
+ });
208
+ }
209
+ }
210
+ return rows;
211
+ }
212
+ // ─── helpers ────────────────────────────────────────────────────────────
213
+ /**
214
+ * Normalise an ADR ID from a traceability entry. Accepts bare numerics
215
+ * (`"0037"`, `"0058"`) and full forms (`"ADR-0037"`, `"ADR 0037"`); emits
216
+ * `ADR-XXXX` (4-digit zero-padded) which is the canonical adrs.id form.
217
+ */
218
+ function canonicaliseAdrId(raw) {
219
+ // Accept a bare number, `ADR-NNNN`/`ADR NNNN`, or any canonical origin prefix
220
+ // (`M-ADR`/`RA-ADR`/`P-ADR`/`D-ADR NNNN`) — all resolve by number.
221
+ const m = new RegExp(`^(?:${ORIGIN_PREFIX_SRC}ADR[-\\s]+)?(\\d+)`, "i").exec(raw);
222
+ if (!m || m[1] === undefined)
223
+ return "";
224
+ const num = parseInt(m[1], 10);
225
+ if (!Number.isFinite(num))
226
+ return "";
227
+ return `ADR-${String(num).padStart(4, "0")}`;
228
+ }
229
+ function resolveFeaturePath(path, index) {
230
+ const cleaned = path.replace(/^\/+/, "");
231
+ return index.get(cleaned) ?? "";
232
+ }
233
+ function resolveContractPath(path, index) {
234
+ const cleaned = path.replace(/^\/+/, "");
235
+ // Try exact path, then suffix matches.
236
+ const direct = index.get(cleaned);
237
+ if (direct !== undefined)
238
+ return direct;
239
+ // Suffix: last two parts (e.g. "asset/asset-twin.yaml")
240
+ const parts = cleaned.split(sep);
241
+ if (parts.length >= 2) {
242
+ const suffix = parts.slice(-2).join("/");
243
+ for (const [k, v] of index) {
244
+ if (k.endsWith(suffix))
245
+ return v;
246
+ }
247
+ }
248
+ // Basename fallback
249
+ const base = basename(cleaned);
250
+ for (const [k, v] of index) {
251
+ if (basename(k) === base)
252
+ return v;
253
+ }
254
+ return "";
255
+ }
256
+ //# sourceMappingURL=capability-edges.js.map
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Walker for capability spec markdown files per ADR 0145 (schema 2.7.0).
3
+ *
4
+ * Reads `## Feature Grounding (code-path trace)` markdown tables of the
5
+ * canonical shape:
6
+ *
7
+ * | Feature | Implementation | Grounding Doc |
8
+ * |---|---|---|
9
+ * | `alerts-module` | `alerts` Vuex module + `src/components/alerts` | docs/.../feature-grounding.md |
10
+ *
11
+ * Path convention (per ADR 0145 §4.3):
12
+ * - platform-profile: `docs/domains/<dom>/product/<capability>.md`
13
+ *
14
+ * Capability id resolves from the filename stem. Tolerant of column
15
+ * re-ordering and missing optional columns. Reuses the table-parsing
16
+ * logic from `feature-grounding-walker.ts` (any markdown table whose
17
+ * first header normalises to `feature` is parsed).
18
+ *
19
+ * Recovery is row-local — a malformed row emits a finding and the
20
+ * walker continues with the next row.
21
+ */
22
+ import { readdir } from "node:fs/promises";
23
+ import { basename, join, relative, sep } from "node:path";
24
+ import { parseGroundingTable, } from "./feature-grounding-walker.js";
25
+ const DOMAINS_DIR_REL = join("docs", "domains");
26
+ const PRODUCT_DIR_NAME = "product";
27
+ /**
28
+ * Find all `docs/domains/<dom>/product/<capability>.md` files under the
29
+ * SDD root. Excludes `.traceability.yaml` sidecars. Sorted for
30
+ * deterministic output.
31
+ */
32
+ export async function findCapabilitySpecFiles(sddPath) {
33
+ const out = [];
34
+ const domainsDir = join(sddPath, DOMAINS_DIR_REL);
35
+ let domains;
36
+ try {
37
+ domains = await readdir(domainsDir, { withFileTypes: true });
38
+ }
39
+ catch {
40
+ return out;
41
+ }
42
+ for (const d of domains) {
43
+ if (!d.isDirectory())
44
+ continue;
45
+ const productDir = join(domainsDir, d.name, PRODUCT_DIR_NAME);
46
+ let files;
47
+ try {
48
+ files = await readdir(productDir, { withFileTypes: true });
49
+ }
50
+ catch {
51
+ continue;
52
+ }
53
+ for (const f of files) {
54
+ if (!f.isFile())
55
+ continue;
56
+ if (!f.name.endsWith(".md"))
57
+ continue;
58
+ // Skip the README that lives in some product/ dirs.
59
+ if (f.name === "README.md")
60
+ continue;
61
+ out.push(join(productDir, f.name));
62
+ }
63
+ }
64
+ return out.sort();
65
+ }
66
+ /**
67
+ * Resolve the capability id from a capability spec file's absolute
68
+ * path. Returns the filename stem (`.md` stripped).
69
+ *
70
+ * docs/domains/applications/product/alerts-and-notifications.md
71
+ * → "alerts-and-notifications"
72
+ */
73
+ export function capabilityIdFromSpecPath(absPath, sddPath) {
74
+ const rel = relative(sddPath, absPath);
75
+ const parts = rel.split(sep);
76
+ // Expected: ["docs", "domains", "<dom>", "product", "<file>.md"]
77
+ if (parts.length < 5)
78
+ return "";
79
+ const file = parts[parts.length - 1] ?? "";
80
+ if (!file.endsWith(".md"))
81
+ return "";
82
+ return file.slice(0, -3);
83
+ }
84
+ /**
85
+ * Parse the markdown content of a capability spec file and return the
86
+ * rows from its `## Feature Grounding (code-path trace)` table.
87
+ *
88
+ * Implementation detail: we delegate to `parseGroundingTable`, which
89
+ * picks the first table whose first column normalises to `feature`.
90
+ * Capability specs typically have one such table under
91
+ * `## Feature Grounding (code-path trace)`; the parser tolerates
92
+ * variations in the heading text.
93
+ */
94
+ export function parseCapabilitySpecTable(text) {
95
+ return parseGroundingTable(text);
96
+ }
97
+ /** Re-export basename for convenience in tests. */
98
+ export { basename };
99
+ //# sourceMappingURL=capability-spec-walker.js.map