@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,103 @@
1
+ // adr_supersedes edge builder — emits one row per (superseding, superseded)
2
+ // ADR pair where either side declares the relationship:
3
+ // - the superseding ADR's `Supersedes:` text references a known ADR id, or
4
+ // - the superseded ADR's `Superseded By:` text references a known ADR id
5
+ // (the ADR 0174 §4.5 lazy-migration case: distributed reference ADRs are
6
+ // adopter-agnostic and cannot name adopter ADR numbers, so only the
7
+ // superseded local copy carries the link — including the cross-repo
8
+ // `<repo>#9000` form, whose number resolves locally once the bundle that
9
+ // ships ADR 9000 is pinned).
10
+ //
11
+ // Only rows where BOTH endpoints resolve to known ADR ids are emitted —
12
+ // the canonical schema's FKs on this edge are required+internal.
13
+ //
14
+ // Also computes the inverse (`superseded_by_id`) backfill values that the
15
+ // orchestrator stitches into adrs.csv after this builder runs.
16
+ const ADR_REF_RE = /\bADR[\s\-_]?(\d{3,4})\b/gi;
17
+ // `Superseded By:` additionally accepts the ADR 0140 cross-repo reference
18
+ // form `<repo>#9000` — the lazy-migration case, where the superseding
19
+ // record lives in a distributed bundle. Procedural citations of the
20
+ // migration rule itself ("per sdd-method#0174 §4.5") are stripped first
21
+ // so they never read as supersession targets.
22
+ const ADR_REF_WITH_HASH_RE = /(?:\bADR[\s\-_]?|#)(\d{3,4})\b/gi;
23
+ const PROCEDURAL_CITATION_RE = /\bper\s+[\w./-]*#\d{3,4}(?:\s*§[\d.]+)?/gi;
24
+ export function buildAdrSupersedes(inputs) {
25
+ const knownIds = new Set(inputs.adrs.map((a) => a.id));
26
+ const rows = [];
27
+ const seen = new Set();
28
+ const supersededByIndex = new Map();
29
+ for (const adr of inputs.adrs) {
30
+ if (adr.supersedesRaw === "")
31
+ continue;
32
+ const refs = extractAdrRefs(adr.supersedesRaw);
33
+ for (const supersededId of refs) {
34
+ if (supersededId === adr.id)
35
+ continue; // self-supersession is nonsense
36
+ if (!knownIds.has(supersededId)) {
37
+ inputs.findings.add("orphan", `${adr.docPath}: Supersedes references "${supersededId}" but no such ADR exists`);
38
+ continue;
39
+ }
40
+ const id = `${adr.id}__${supersededId}`;
41
+ if (seen.has(id))
42
+ continue;
43
+ seen.add(id);
44
+ rows.push({
45
+ id,
46
+ superseding_adr_id: adr.id,
47
+ superseded_adr_id: supersededId,
48
+ });
49
+ // Last writer wins on the inverse index — multiple superseders for
50
+ // the same ADR is rare; if it happens we surface a finding.
51
+ const prior = supersededByIndex.get(supersededId);
52
+ if (prior !== undefined && prior !== adr.id) {
53
+ inputs.findings.add("divergence", `ADR ${supersededId} is superseded by both ${prior} and ${adr.id}`);
54
+ }
55
+ supersededByIndex.set(supersededId, adr.id);
56
+ }
57
+ }
58
+ // Second pass: edges declared from the superseded side (`Superseded By:`).
59
+ for (const adr of inputs.adrs) {
60
+ if (adr.supersededByRaw === "" || /^none$/i.test(adr.supersededByRaw.trim()))
61
+ continue;
62
+ const refs = extractAdrRefs(adr.supersededByRaw.replace(PROCEDURAL_CITATION_RE, " "), ADR_REF_WITH_HASH_RE);
63
+ for (const supersedingId of refs) {
64
+ if (supersedingId === adr.id)
65
+ continue;
66
+ if (!knownIds.has(supersedingId)) {
67
+ inputs.findings.add("orphan", `${adr.docPath}: Superseded By references "${supersedingId}" but no such ADR exists locally — edge skipped (pin the bundle that ships it, or fix the reference)`);
68
+ continue;
69
+ }
70
+ const id = `${supersedingId}__${adr.id}`;
71
+ if (seen.has(id))
72
+ continue; // already declared from the Supersedes side
73
+ seen.add(id);
74
+ rows.push({
75
+ id,
76
+ superseding_adr_id: supersedingId,
77
+ superseded_adr_id: adr.id,
78
+ });
79
+ const prior = supersededByIndex.get(adr.id);
80
+ if (prior !== undefined && prior !== supersedingId) {
81
+ inputs.findings.add("divergence", `ADR ${adr.id} is superseded by both ${prior} and ${supersedingId}`);
82
+ }
83
+ supersededByIndex.set(adr.id, supersedingId);
84
+ }
85
+ }
86
+ return { rows, supersededByIndex };
87
+ }
88
+ function extractAdrRefs(raw, re = ADR_REF_RE) {
89
+ const out = [];
90
+ let m;
91
+ // Reset regex state between calls.
92
+ re.lastIndex = 0;
93
+ while ((m = re.exec(raw)) !== null) {
94
+ if (m[1] === undefined)
95
+ continue;
96
+ const num = parseInt(m[1], 10);
97
+ if (!Number.isFinite(num))
98
+ continue;
99
+ out.push(`ADR-${String(num).padStart(4, "0")}`);
100
+ }
101
+ return out;
102
+ }
103
+ //# sourceMappingURL=adr-supersedes.js.map
@@ -0,0 +1,222 @@
1
+ // ADR discoverer — emits `adrs.csv` rows from every `adr/*.md` file
2
+ // under `docs/`.
3
+ //
4
+ // Source paths discovered:
5
+ // - docs/platform/adr/ADR XXXX: Title.md (platform-scoped)
6
+ // - docs/method/adr/ADR XXXX: Title.md (method-scoped → domain_id EMPTY, schema 3.10.0)
7
+ // - docs/method/platform-adr/ADR XXXX: Title.md (method-scoped; the frozen
8
+ // platform-foundational 0001-0099 band per M-ADR 0140)
9
+ // - docs/domains/<domain>/architecture/adr/ADR XXXX: Title.md
10
+ //
11
+ // Per ADR 0136 the method-scope ADRs are owned by the platform-root in
12
+ // the federation chain (the highest-up SDD that holds them via the seed-
13
+ // file distribution). App and integration profile SDDs skip emission of
14
+ // method-scope ADRs so the aggregated catalogue carries each method ADR
15
+ // exactly once with an unambiguous source_sdd_id.
16
+ //
17
+ // File-content extraction (best-effort, no failure on missing fields):
18
+ // - **Status:** value
19
+ // - **Date:** YYYY-MM-DD
20
+ // - **Supersedes:** raw text (captured into supersedes_raw for the
21
+ // adr_supersedes builder to parse)
22
+ //
23
+ // ID canonicalisation: filename stem `ADR XXXX[: Title]` → `ADR-{0-padded}`.
24
+ // Title = the portion after the colon when present, else the file stem
25
+ // minus the prefix.
26
+ import { readdir, readFile, stat } from "node:fs/promises";
27
+ import { join, relative, sep } from "node:path";
28
+ import { ORIGIN_PREFIX_SRC } from "../../adr-origin.js";
29
+ const PROFILES_THAT_OWN_METHOD_SCOPE = new Set([
30
+ "platform-profile",
31
+ ]);
32
+ const CATEGORIES_THAT_DO_NOT_OWN_METHOD_SCOPE = new Set([
33
+ "platform-subsystem",
34
+ ]);
35
+ // Dual-resolution (M-ADR 0140 §4.1 / M-ADR 0186): the durable identity is the
36
+ // NUMBER, not the origin prefix. A file named `ADR NNNN` (bare/legacy), or any
37
+ // canonical prefix — `M-ADR`, `RA-ADR`, `P-ADR`, `D-ADR` — all resolve to the
38
+ // same `ADR-NNNN` id. The prefix set is composed from the shared source so a new
39
+ // tier is a one-line change in adr-origin.ts.
40
+ const FILENAME_RE = new RegExp(`^${ORIGIN_PREFIX_SRC}ADR[\\s-]?(\\d{3,4})(?:[:\\s-]+(.*))?$`, "i");
41
+ export async function buildAdrs(inputs) {
42
+ const docsDir = join(inputs.sddPath, "docs");
43
+ const files = await collectAdrFiles(docsDir);
44
+ const seen = new Set();
45
+ const parsed = [];
46
+ const ownsMethodScope = PROFILES_THAT_OWN_METHOD_SCOPE.has(inputs.profile) &&
47
+ !CATEGORIES_THAT_DO_NOT_OWN_METHOD_SCOPE.has(inputs.category ?? "");
48
+ let methodScopeSkipCount = 0;
49
+ for (const file of files.sort()) {
50
+ const stem = baseStem(file);
51
+ const m = FILENAME_RE.exec(stem);
52
+ if (!m || m[1] === undefined) {
53
+ // Template file (e.g. `ADR {0000}: ...`) or non-ADR markdown — skip silently.
54
+ continue;
55
+ }
56
+ const num = parseInt(m[1], 10);
57
+ if (!Number.isFinite(num))
58
+ continue;
59
+ const adrId = `ADR-${String(num).padStart(4, "0")}`;
60
+ if (seen.has(adrId)) {
61
+ inputs.findings.add("divergence", `${relative(inputs.sddPath, file)}: ADR id ${adrId} already emitted by an earlier file`);
62
+ continue;
63
+ }
64
+ const relPath = relative(inputs.sddPath, file);
65
+ const { domainId, scope } = scopeFromPath(relPath);
66
+ if ((scope === "method" || scope === "platform") && !ownsMethodScope) {
67
+ // Per ADR 0136: method- and platform-scope ADRs are emitted only by
68
+ // the SDD that owns the platform scope (platform-profile in
69
+ // adopting ecosystems). App and integration SDDs hold seed-file
70
+ // copies on disk but do not contribute them to the federated
71
+ // catalogue.
72
+ methodScopeSkipCount++;
73
+ continue;
74
+ }
75
+ seen.add(adrId);
76
+ const titleFromFilename = (m[2] ?? "").trim() || stem;
77
+ const text = await readFile(file, "utf-8");
78
+ const title = extractH1Title(text) || titleFromFilename;
79
+ const status = extractField(text, "Status") || "Accepted";
80
+ const decisionDate = extractField(text, "Date") || "";
81
+ const supersedesRaw = extractField(text, "Supersedes") || "";
82
+ const supersededByRaw = extractField(text, "Superseded By") || "";
83
+ if (scope === "domain" && !inputs.knownDomainIds.has(domainId)) {
84
+ inputs.findings.add("orphan", `${relPath}: ADR is in domain "${domainId}" but no docs/domains/${domainId}/ exists`);
85
+ }
86
+ // Method-scoped ADRs are cross-cutting governance and belong to no
87
+ // domain: domain_id stays EMPTY (schema 3.10.0, M-ADR 0136 §4.3
88
+ // amendment). The former pin to `platform` resolved only where the
89
+ // root happened to own a domain of that name and dangled everywhere
90
+ // else — every method ADR row in a packaged-SaaS root failed FK.
91
+ parsed.push({
92
+ id: adrId,
93
+ title,
94
+ status,
95
+ domainId,
96
+ decisionDate,
97
+ docPath: relPath,
98
+ supersedesRaw,
99
+ supersededByRaw,
100
+ });
101
+ }
102
+ if (methodScopeSkipCount > 0) {
103
+ inputs.findings.add("coverage", `${methodScopeSkipCount} method/platform-scope ADR(s) skipped — emitted by ecosystem root only per ADR 0136`);
104
+ }
105
+ const rows = parsed.map((a) => ({
106
+ id: a.id,
107
+ title: a.title,
108
+ status: a.status,
109
+ domain_id: a.domainId,
110
+ decision_date: a.decisionDate,
111
+ doc_path: a.docPath,
112
+ superseded_by_id: "", // populated in a backfill pass after adr_supersedes builds
113
+ inference_confidence: "",
114
+ supersedes_raw: a.supersedesRaw,
115
+ source_sdd_id: "",
116
+ }));
117
+ return { rows, parsed };
118
+ }
119
+ function baseStem(absPath) {
120
+ const base = absPath.split(sep).slice(-1)[0] ?? "";
121
+ return base.replace(/\.md$/i, "");
122
+ }
123
+ function scopeFromPath(relPath) {
124
+ const parts = relPath.split(sep);
125
+ const domainsIdx = parts.indexOf("domains");
126
+ if (domainsIdx !== -1 && domainsIdx + 1 < parts.length) {
127
+ return { domainId: parts[domainsIdx + 1] ?? "", scope: "domain" };
128
+ }
129
+ if (parts.includes("method")) {
130
+ return { domainId: "", scope: "method" };
131
+ }
132
+ return { domainId: "platform", scope: "platform" };
133
+ }
134
+ function extractH1Title(text) {
135
+ for (const line of text.split(/\r?\n/)) {
136
+ const t = line.trim();
137
+ if (t === "")
138
+ continue;
139
+ if (!t.startsWith("#"))
140
+ return "";
141
+ // `# ADR 0001: Title` → `Title`; otherwise return the heading text.
142
+ // Accepts any canonical origin prefix (M-/RA-/P-/D-) — dual-resolution.
143
+ const stripped = t.replace(/^#+\s*/, "").trim();
144
+ const adrPrefix = new RegExp(`^${ORIGIN_PREFIX_SRC}ADR[\\s-]?\\d+(?:[:\\s-]+(.*))?$`, "i").exec(stripped);
145
+ if (adrPrefix) {
146
+ return (adrPrefix[1] ?? "").trim();
147
+ }
148
+ return stripped;
149
+ }
150
+ return "";
151
+ }
152
+ /**
153
+ * Extract a value from a metadata bullet of the form:
154
+ * * **Field:** value
155
+ * - **Field:** value
156
+ * **Field:** value
157
+ * Returns the trimmed value, or empty string if not found.
158
+ */
159
+ function extractField(text, field) {
160
+ const re = new RegExp(`^\\s*[*\\-]?\\s*\\*\\*${escapeRegex(field)}:\\*\\*\\s*(.*?)\\s*$`, "im");
161
+ const m = re.exec(text);
162
+ if (!m || m[1] === undefined)
163
+ return "";
164
+ return m[1].trim();
165
+ }
166
+ function escapeRegex(s) {
167
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
168
+ }
169
+ /**
170
+ * Directory names that hold ADR files. An EXPLICIT SET, not a pattern.
171
+ *
172
+ * `platform-adr` was invisible until 2026-08-06 because this walk tested for a
173
+ * directory named exactly "adr" — so `docs/method/platform-adr/` was recursed
174
+ * into and its files were never collected. Measured at discovery: 14 files
175
+ * present, 0 rows, in EVERY SDD of both reference estates (~1,414 files), while
176
+ * the directory is bundle payload and its ADRs are cited, superseded and scored
177
+ * like any other. One estate held 13 assessments of them, which read as orphaned
178
+ * scores and were nearly withdrawn as ADRs that no longer existed.
179
+ *
180
+ * Named rather than matched (`endsWith("adr")`, `/adr$/`) on purpose: a pattern
181
+ * here would also swallow any future directory an adopter happens to name
182
+ * `<something>-adr`, and this file has just paid for one loose match.
183
+ */
184
+ const ADR_DIR_NAMES = new Set(["adr", "platform-adr"]);
185
+ async function collectAdrFiles(docsDir) {
186
+ const out = [];
187
+ let entries;
188
+ try {
189
+ entries = await readdir(docsDir, { withFileTypes: true });
190
+ }
191
+ catch (err) {
192
+ const code = err.code;
193
+ if (code === "ENOENT")
194
+ return [];
195
+ throw err;
196
+ }
197
+ for (const entry of entries) {
198
+ const full = join(docsDir, entry.name);
199
+ if (entry.isDirectory()) {
200
+ if (ADR_DIR_NAMES.has(entry.name)) {
201
+ // Walk all files in the adr/ directory (no nesting expected).
202
+ const inner = await readdir(full, { withFileTypes: true });
203
+ for (const f of inner) {
204
+ if (f.isFile() && f.name.endsWith(".md") && !isTemplate(f.name)) {
205
+ out.push(join(full, f.name));
206
+ }
207
+ }
208
+ }
209
+ else {
210
+ out.push(...(await collectAdrFiles(full)));
211
+ }
212
+ }
213
+ }
214
+ return out;
215
+ }
216
+ function isTemplate(filename) {
217
+ // Excludes template files like `ADR {0000}: {Short, Descriptive Title}.md`.
218
+ return /[{}]/.test(filename);
219
+ }
220
+ /** Drop-in stat used during testing — unused at runtime but kept exported for symmetry. */
221
+ export const _statForTest = stat;
222
+ //# sourceMappingURL=adrs.js.map
@@ -0,0 +1,47 @@
1
+ // application_conformance_coverage builder (M-ADR 0191 §4.3 overlay, WS4).
2
+ //
3
+ // Emits one row per conformance tag the product declares about its archetype's
4
+ // conformance expectations: `status=covered` (its suite exercises the tag) or
5
+ // `status=deviated` (deliberately not covered, with a rationale). Built per-SDD
6
+ // from the manifest's `conformance` block so the AGGREGATION-tier conformance
7
+ // gate can read it — the gate itself runs at aggregation, where the reference-
8
+ // architecture bundle's per-archetype `conformance` tags resolve.
9
+ export function buildApplicationConformanceCoverage(inputs) {
10
+ const { manifest, findings } = inputs;
11
+ const rows = [];
12
+ const conformance = manifest.conformance;
13
+ if (conformance === undefined)
14
+ return rows;
15
+ if (manifest.profile !== "application-product") {
16
+ if ((conformance.coverage?.length ?? 0) > 0 || (conformance.deviations?.length ?? 0) > 0) {
17
+ findings.add("convention", `application_conformance_coverage: conformance declared on non-application-product SDD "${manifest.sddId}" (profile=${manifest.profile}) — ignored (M-ADR 0191 §4.3 is application-product only)`);
18
+ }
19
+ return rows;
20
+ }
21
+ const seen = new Set();
22
+ const emit = (tag, status, rationale) => {
23
+ const t = tag.trim();
24
+ if (t === "")
25
+ return;
26
+ const id = `${manifest.sddId}__${t}`;
27
+ if (seen.has(id)) {
28
+ findings.add("divergence", `application_conformance_coverage: duplicate conformance tag id="${id}" — same (product_id, tag) already emitted; second dropped`);
29
+ return;
30
+ }
31
+ seen.add(id);
32
+ rows.push({
33
+ id,
34
+ product_id: manifest.sddId,
35
+ tag: t,
36
+ status,
37
+ rationale,
38
+ source_sdd_id: "",
39
+ });
40
+ };
41
+ for (const tag of conformance.coverage ?? [])
42
+ emit(tag, "covered", "");
43
+ for (const d of conformance.deviations ?? [])
44
+ emit(d.tag, "deviated", d.rationale);
45
+ return rows;
46
+ }
47
+ //# sourceMappingURL=application-conformance-coverage.js.map
@@ -0,0 +1,115 @@
1
+ // application_data_plane_bindings builder — emits one row per declared
2
+ // data-plane binding (M-ADR 0193).
3
+ //
4
+ // The data-plane axis models an application-product as a COMPOSITION of typed
5
+ // data bindings, orthogonal to `application_archetype` (0191). Each binding is
6
+ // one `{pattern, locality, transport, runtime, target, notes}` tuple declared
7
+ // in the manifest's `data_plane.bindings` block. The manifest loader has
8
+ // already validated the closed enums and the runtime/locality rules (malformed
9
+ // bindings FAIL at load), so this builder only shapes valid bindings into rows.
10
+ //
11
+ // The `pattern` vocabulary splits by DIRECTION (§4.7): an INBOUND data-sourcing
12
+ // sub-set (retrieve/own-store/stream-ingest/project) and an OUTPUT-side sub-set
13
+ // (generate/publish). Each row carries a derived `direction` column so consumers
14
+ // can branch on the sub-set the ADR declares instead of re-deriving the split.
15
+ //
16
+ // An INBOUND binding whose target is a platform capability/service reconciles
17
+ // with `product_platform_dependencies` (a retrieve@internal binding is a
18
+ // synchronous platform dependency; a stream-ingest@internal binding is the
19
+ // M-ADR 0192 contract_type:event async dependency). OUTPUT-side rows do NOT
20
+ // participate in that reconciliation (§4.7 rule 1) — `publish` is a contract the
21
+ // service PROVIDES, so it has no inbound counterpart to cross-check.
22
+ import { dataPlaneDirection } from "../manifest-loader.js";
23
+ // Recommended transport values (open string per the schema; a value outside the
24
+ // set is a convention finding, never a hard fail — the M-ADR 0156/0192 pattern).
25
+ const RECOMMENDED_TRANSPORTS = new Set([
26
+ "rest",
27
+ "graphql",
28
+ "grpc",
29
+ "appsync",
30
+ "kinesis",
31
+ "kafka",
32
+ "eventhub",
33
+ "cdc",
34
+ ]);
35
+ export function buildApplicationDataPlaneBindings(inputs) {
36
+ const { manifest, findings } = inputs;
37
+ const rows = [];
38
+ const bindings = manifest.dataPlane?.bindings;
39
+ if (bindings === undefined || bindings.length === 0)
40
+ return rows;
41
+ // data_plane is an application-product-only axis. A binding block on another
42
+ // profile is a manifest smell — report it and emit nothing.
43
+ if (manifest.profile !== "application-product") {
44
+ findings.add("convention", `application_data_plane_bindings: data_plane declared on non-application-product SDD "${manifest.sddId}" (profile=${manifest.profile}) — ignored (M-ADR 0193 is application-product only)`);
45
+ return rows;
46
+ }
47
+ const seen = new Set();
48
+ for (const b of bindings) {
49
+ const pattern = b.pattern;
50
+ const locality = b.locality ?? "";
51
+ const transport = b.transport ?? "";
52
+ const runtime = b.runtime ?? "";
53
+ const target = b.target ?? "";
54
+ const notes = b.notes ?? "";
55
+ const id = `${manifest.sddId}__${pattern}__${locality}__${target}`;
56
+ if (seen.has(id)) {
57
+ findings.add("divergence", `application_data_plane_bindings: duplicate binding id="${id}" — same (product_id, pattern, locality, target) already emitted; second dropped`);
58
+ continue;
59
+ }
60
+ seen.add(id);
61
+ if (transport !== "" && !RECOMMENDED_TRANSPORTS.has(transport.toLowerCase())) {
62
+ findings.add("convention", `application_data_plane_bindings: binding "${id}" declares transport="${transport}" outside the recommended set (${[...RECOMMENDED_TRANSPORTS].join(", ")})`);
63
+ }
64
+ rows.push({
65
+ id,
66
+ product_id: manifest.sddId,
67
+ pattern,
68
+ // Derived from `pattern` (M-ADR 0193 §4.7), not authored — it materialises
69
+ // the output-side sub-set so consumers can branch on direction (notably
70
+ // the inbound-only platform-dep reconciliation) without re-deriving the
71
+ // pattern split. Not a PK component: it adds nothing to identity.
72
+ direction: dataPlaneDirection(pattern),
73
+ locality,
74
+ transport,
75
+ runtime,
76
+ target,
77
+ notes,
78
+ source_sdd_id: "",
79
+ });
80
+ }
81
+ return rows;
82
+ }
83
+ /**
84
+ * Emit `application_data_plane_deviations` rows from the manifest's
85
+ * `data_plane.deviations` block (M-ADR 0191 §4.3). Built per-SDD so the
86
+ * aggregation-tier overlay WARN can read them; the WARN itself runs at
87
+ * aggregation, where the reference-architecture `expected_bindings` resolve.
88
+ */
89
+ export function buildApplicationDataPlaneDeviations(inputs) {
90
+ const { manifest, findings } = inputs;
91
+ const rows = [];
92
+ const deviations = manifest.dataPlane?.deviations;
93
+ if (deviations === undefined || deviations.length === 0)
94
+ return rows;
95
+ if (manifest.profile !== "application-product")
96
+ return rows;
97
+ const seen = new Set();
98
+ for (const d of deviations) {
99
+ const id = `${manifest.sddId}__${d.expectation}`;
100
+ if (seen.has(id)) {
101
+ findings.add("divergence", `application_data_plane_deviations: duplicate deviation id="${id}" — same (product_id, expectation) already emitted; second dropped`);
102
+ continue;
103
+ }
104
+ seen.add(id);
105
+ rows.push({
106
+ id,
107
+ product_id: manifest.sddId,
108
+ expectation: d.expectation,
109
+ rationale: d.rationale,
110
+ source_sdd_id: "",
111
+ });
112
+ }
113
+ return rows;
114
+ }
115
+ //# sourceMappingURL=application-data-plane-bindings.js.map