@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,74 @@
1
+ // Profile → bundle coherence (A2 of the bundle-migration plan).
2
+ //
3
+ // A repo's declared profile (`.sdd/sdd-manifest.yaml`) and the forward base
4
+ // its lock carries can silently disagree: a reclassification edits the
5
+ // manifest, nothing re-syncs the bundle, and no instrument notices — the
6
+ // registration/adoption-lag class. Measured instance: two platform-subsystem
7
+ // repos carried app-baseline for eight days, found by a human sweep.
8
+ //
9
+ // REPORTING-ONLY: a mismatch prints a [REPORT] line with the migration path
10
+ // and exits 0. Gating is a later, deliberate change-set once both estates
11
+ // read 0 (the scores-coverage precedent, M-ADR 0205 §4.5).
12
+ import { readLockFile, findForwardBase } from "../lock-file.js";
13
+ import { loadSddManifest, } from "../catalogue/manifest-loader.js";
14
+ import { getSddCliVersion } from "../package-version.js";
15
+ /**
16
+ * Manifest profile → the forward base that profile expects. Profiles with no
17
+ * entry (support-repo, organisation-profile) carry no expectation — silence,
18
+ * not a guess. NOTE: platform-subsystem is a CATEGORY under platform-profile,
19
+ * so the two measured mismatch repos resolve through the platform-profile row.
20
+ */
21
+ const EXPECTED_FORWARD_BASE = {
22
+ "platform-profile": "platform-baseline",
23
+ "application-product": "app-baseline",
24
+ "integration-product": "integration-baseline",
25
+ };
26
+ export async function checkBundleCoherence(targetRepo) {
27
+ const lines = [];
28
+ const lock = await readLockFile(targetRepo);
29
+ const forwardBase = findForwardBase(lock);
30
+ if (!forwardBase) {
31
+ return {
32
+ exitCode: 0,
33
+ stdout: "",
34
+ skipped: "no forward base in method-baseline.lock — nothing to check",
35
+ };
36
+ }
37
+ let profile;
38
+ try {
39
+ const manifest = await loadSddManifest(targetRepo, {
40
+ cliVersion: getSddCliVersion(),
41
+ });
42
+ profile = manifest.profile;
43
+ }
44
+ catch (err) {
45
+ // Reporting-only: an absent/invalid manifest is its own (already-gated)
46
+ // problem; this check must not double-report it as a bundle mismatch.
47
+ return {
48
+ exitCode: 0,
49
+ stdout: "",
50
+ skipped: `manifest unavailable (${err.message.split("\n")[0]})`,
51
+ };
52
+ }
53
+ const expected = EXPECTED_FORWARD_BASE[profile];
54
+ if (expected === undefined) {
55
+ // Nothing to compare is not a pass: counting it as one inflated the
56
+ // suite's "passed" figure for profiles that carry no expectation.
57
+ return {
58
+ exitCode: 0,
59
+ stdout: "",
60
+ skipped: `profile '${profile}' declares no forward-base expectation — nothing to compare`,
61
+ };
62
+ }
63
+ if (forwardBase.name === expected) {
64
+ lines.push(`[PASS] bundle-coherence: profile '${profile}' ↔ forward base '${forwardBase.name}'\n`);
65
+ return { exitCode: 0, stdout: lines.join("") };
66
+ }
67
+ lines.push(`[REPORT] bundle-coherence: profile '${profile}' expects forward base '${expected}' ` +
68
+ `but the lock carries '${forwardBase.name}' — profile and bundle disagree. ` +
69
+ `Migration path: sdd-cli sync --bundle <${expected} tarball> --replace-bundle ${forwardBase.name} --dry-run, ` +
70
+ `review the three-way split, then re-run with --yes. ` +
71
+ `(Reporting-only; this check does not gate.)\n`);
72
+ return { exitCode: 0, stdout: lines.join("") };
73
+ }
74
+ //# sourceMappingURL=bundle-coherence.js.map
@@ -0,0 +1,243 @@
1
+ import { checkBundleCoherence } from "./bundle-coherence.js";
2
+ import { checkYamlParse } from "./yaml-parse.js";
3
+ // Validators that accept --app-root / --repo-root flags work fine from the
4
+ // bundled location because they override their internal ROOT_DIR via the
5
+ // flag. Validators without that flag (adr-structure, runway-classification,
6
+ // spec-lifecycle) compute ROOT_DIR as SCRIPT_DIR/../.. — so we must run the
7
+ // copy that sync placed inside the target repo, not the bundled sdd-cli copy.
8
+ const APP_VALIDATORS = [
9
+ {
10
+ name: "app-docs",
11
+ source: { kind: "bundled", script: "validate-app-docs.sh" },
12
+ build: (ctx) => ({ status: "run", args: ["--app-root", ctx.targetRepo] }),
13
+ },
14
+ {
15
+ name: "app-dependencies",
16
+ source: { kind: "bundled", script: "validate-app-dependencies.sh" },
17
+ // NO LONGER SKIPS WITHOUT --platform-root. It used to, and the consequence was
18
+ // that the validator had never executed: measured across 22/22 forward-estate
19
+ // app repos it skipped every time, because nothing passes --platform-root, and
20
+ // the suite printed "All validators passed" over the skip.
21
+ //
22
+ // Only the resolution halves of checks 2 and 3 need a platform surface. The
23
+ // stability vocabulary, "is a service_id declared", and "does a locked
24
+ // dependency name a contract_path" are all answerable in the app repo alone,
25
+ // and they are the more basic defects. The script now runs those and reports
26
+ // the resolutions it could not perform, with their denominator — a partial run
27
+ // that never claims the gate is met (M-ADR 0200 §4.3).
28
+ build: (ctx) => ({
29
+ status: "run",
30
+ args: [
31
+ "--app-root",
32
+ ctx.targetRepo,
33
+ ...(ctx.platformRoot ? ["--platform-root", ctx.platformRoot] : []),
34
+ ],
35
+ }),
36
+ },
37
+ {
38
+ name: "adr-structure",
39
+ source: {
40
+ kind: "target-repo",
41
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
42
+ },
43
+ build: () => ({ status: "run", args: [] }),
44
+ },
45
+ {
46
+ name: "spec-lifecycle",
47
+ source: {
48
+ kind: "target-repo",
49
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
50
+ },
51
+ build: () => ({ status: "run", args: [] }),
52
+ },
53
+ {
54
+ name: "no-bare-adr",
55
+ source: {
56
+ kind: "target-repo",
57
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
58
+ },
59
+ build: () => ({ status: "run", args: [] }),
60
+ },
61
+ {
62
+ name: "bundle-coherence",
63
+ source: {
64
+ kind: "native",
65
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
66
+ },
67
+ build: () => ({ status: "run", args: [] }),
68
+ },
69
+ {
70
+ name: "yaml-parse",
71
+ source: {
72
+ kind: "native",
73
+ run: async (ctx) => checkYamlParse(ctx.targetRepo),
74
+ },
75
+ build: () => ({ status: "run", args: [] }),
76
+ },
77
+ // Note: validate-runway-classification is intentionally NOT in the app set.
78
+ // It requires platform-level files (docs/platform/feature-tree.md,
79
+ // architecture-runway-index.md) that app repos don't own. Per ADR 0089,
80
+ // the reduced app suite is app-specific validators + shared foundations,
81
+ // not platform validators.
82
+ ];
83
+ const INTEGRATION_VALIDATORS = [
84
+ {
85
+ name: "integration-docs",
86
+ source: { kind: "bundled", script: "validate-integration-docs.sh" },
87
+ build: (ctx) => ({ status: "run", args: ["--repo-root", ctx.targetRepo] }),
88
+ },
89
+ {
90
+ name: "adr-structure",
91
+ source: {
92
+ kind: "target-repo",
93
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
94
+ },
95
+ build: () => ({ status: "run", args: [] }),
96
+ },
97
+ {
98
+ name: "spec-lifecycle",
99
+ source: {
100
+ kind: "target-repo",
101
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
102
+ },
103
+ build: () => ({ status: "run", args: [] }),
104
+ },
105
+ {
106
+ name: "no-bare-adr",
107
+ source: {
108
+ kind: "target-repo",
109
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
110
+ },
111
+ build: () => ({ status: "run", args: [] }),
112
+ },
113
+ {
114
+ name: "bundle-coherence",
115
+ source: {
116
+ kind: "native",
117
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
118
+ },
119
+ build: () => ({ status: "run", args: [] }),
120
+ },
121
+ {
122
+ name: "yaml-parse",
123
+ source: {
124
+ kind: "native",
125
+ run: async (ctx) => checkYamlParse(ctx.targetRepo),
126
+ },
127
+ build: () => ({ status: "run", args: [] }),
128
+ },
129
+ ];
130
+ const PLATFORM_VALIDATORS = [
131
+ {
132
+ name: "traceability",
133
+ source: {
134
+ kind: "target-repo",
135
+ relPath: "orchestration/scripts/validate-traceability.sh",
136
+ },
137
+ build: () => ({ status: "run", args: ["all"] }),
138
+ },
139
+ {
140
+ name: "runway-classification",
141
+ source: {
142
+ kind: "target-repo",
143
+ relPath: "orchestration/scripts/validate-runway-classification.sh",
144
+ },
145
+ build: () => ({ status: "run", args: [] }),
146
+ },
147
+ {
148
+ name: "adr-structure",
149
+ source: {
150
+ kind: "target-repo",
151
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
152
+ },
153
+ build: () => ({ status: "run", args: [] }),
154
+ },
155
+ {
156
+ name: "spec-lifecycle",
157
+ source: {
158
+ kind: "target-repo",
159
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
160
+ },
161
+ build: () => ({ status: "run", args: [] }),
162
+ },
163
+ {
164
+ name: "no-bare-adr",
165
+ source: {
166
+ kind: "target-repo",
167
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
168
+ },
169
+ build: () => ({ status: "run", args: [] }),
170
+ },
171
+ {
172
+ name: "corpus-consistency",
173
+ source: {
174
+ kind: "target-repo",
175
+ relPath: "orchestration/scripts/validate-corpus-consistency.sh",
176
+ },
177
+ build: () => ({ status: "run", args: [] }),
178
+ },
179
+ {
180
+ name: "config",
181
+ source: {
182
+ kind: "target-repo",
183
+ relPath: "orchestration/scripts/validate-config.sh",
184
+ },
185
+ build: () => ({ status: "run", args: [] }),
186
+ },
187
+ {
188
+ name: "gate-readiness",
189
+ source: {
190
+ kind: "target-repo",
191
+ relPath: "orchestration/scripts/validate-gate-readiness.sh",
192
+ },
193
+ build: () => ({ status: "run", args: [] }),
194
+ },
195
+ {
196
+ name: "bundle-coherence",
197
+ source: {
198
+ kind: "native",
199
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
200
+ },
201
+ build: () => ({ status: "run", args: [] }),
202
+ },
203
+ {
204
+ name: "yaml-parse",
205
+ source: {
206
+ kind: "native",
207
+ run: async (ctx) => checkYamlParse(ctx.targetRepo),
208
+ },
209
+ build: () => ({ status: "run", args: [] }),
210
+ },
211
+ // Optional: the upstream validate-docs.sh dispatcher covers the 6
212
+ // conditional validators (backward-compatibility, deprecation-runway,
213
+ // migration-completeness, ddd-artifacts, glossary-alignment,
214
+ // intent-quality-readiness) and self-skips when their inputs (BASELINE.md,
215
+ // gate-registry files) aren't present. Invoked via --validator docs-all.
216
+ {
217
+ name: "docs-all",
218
+ source: {
219
+ kind: "target-repo",
220
+ relPath: "orchestration/scripts/validate-docs.sh",
221
+ },
222
+ build: () => ({ status: "run", args: ["all"] }),
223
+ optional: true,
224
+ },
225
+ ];
226
+ export function validatorsForProfile(profile) {
227
+ if (profile === "platform")
228
+ return PLATFORM_VALIDATORS;
229
+ return profile === "application" ? APP_VALIDATORS : INTEGRATION_VALIDATORS;
230
+ }
231
+ export function selectValidators(profile, filter) {
232
+ const all = validatorsForProfile(profile);
233
+ if (!filter)
234
+ return all.filter((v) => v.optional !== true);
235
+ const matched = all.find((v) => v.name === filter);
236
+ if (!matched) {
237
+ throw new Error(`Unknown validator "${filter}" for ${profile} profile. Available: ${all
238
+ .map((v) => v.name)
239
+ .join(", ")}`);
240
+ }
241
+ return [matched];
242
+ }
243
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1,110 @@
1
+ import { profileFromRepoKind, readRepoKind } from "../repo-kind.js";
2
+ import { aggregateResults, classifyResult } from "./aggregate.js";
3
+ import { selectValidators } from "./dispatch.js";
4
+ import { runValidator } from "./runner.js";
5
+ const SECTION_RULE = "═".repeat(59);
6
+ export async function runValidate(opts, stdout = (line) => process.stdout.write(line)) {
7
+ const repoKind = await readRepoKind(opts.targetRepo);
8
+ if (repoKind === null) {
9
+ stdout(fail(`Not an SDD repo: no .sdd-repo-kind marker in ${opts.targetRepo}`));
10
+ return { exitCode: 1, summary: emptySummary() };
11
+ }
12
+ const profile = profileFromRepoKind(repoKind);
13
+ stdout(section(`Validate (${profile})`));
14
+ let validators;
15
+ try {
16
+ validators = selectValidators(profile, opts.validator);
17
+ }
18
+ catch (err) {
19
+ stdout(fail(err.message));
20
+ return { exitCode: 1, summary: emptySummary() };
21
+ }
22
+ const results = [];
23
+ for (const cfg of validators) {
24
+ stdout(info(`Running ${cfg.name}`));
25
+ const result = await runValidator(cfg, {
26
+ targetRepo: opts.targetRepo,
27
+ ...(opts.platformRoot ? { platformRoot: opts.platformRoot } : {}),
28
+ });
29
+ results.push(result);
30
+ if (result.status === "skip") {
31
+ stdout(skip(`${cfg.name}: skipped — ${result.reason}`));
32
+ continue;
33
+ }
34
+ if (result.stdout)
35
+ stdout(result.stdout);
36
+ if (result.stderr)
37
+ stdout(result.stderr);
38
+ if (result.exitCode !== 0)
39
+ stdout(fail(`${cfg.name}: exit ${result.exitCode}`));
40
+ else if (classifyResult(result, { strict: opts.strict }) === "not-measured") {
41
+ stdout(na(`${cfg.name}: nothing measured — its population is empty`));
42
+ }
43
+ else
44
+ stdout(pass(`${cfg.name}: ok`));
45
+ }
46
+ const summary = aggregateResults(results, { strict: opts.strict });
47
+ stdout(info(`Summary: passed=${summary.passed} failed=${summary.failed} skipped=${summary.skipped} ` +
48
+ `not-measured=${summary.notMeasured} warned=${summary.warned}${opts.strict ? " (strict)" : ""}`));
49
+ stdout(verdict(summary, validators.length));
50
+ return { exitCode: summary.exitCode, summary };
51
+ }
52
+ // Every verdict states its denominator. A skipped validator did not run, so it
53
+ // cannot be covered by a green verdict: "all passed" with one skipped and "all
54
+ // passed" with none were previously the same line (M-ADR 0200 §4.3). The
55
+ // exit code is deliberately unchanged — the defect is the claim, not the
56
+ // gating; whether an unexecutable validator should FAIL is the open design
57
+ // question (post-3.0.0-followons.md A2), not this one.
58
+ function verdict(summary, selected) {
59
+ if (summary.exitCode !== 0)
60
+ return fail("One or more validators failed");
61
+ const ran = summary.passed + summary.failed;
62
+ const nm = summary.notMeasured;
63
+ if (summary.skipped === 0 && nm === 0)
64
+ return pass("All validators passed");
65
+ if (ran === 0 && nm === 0) {
66
+ return warn(`0 of ${selected} validators ran — all ${summary.skipped} skipped. ` +
67
+ `This is an absence of evidence, not a pass.`);
68
+ }
69
+ if (nm === 0) {
70
+ return warn(`${ran} of ${selected} validators passed — ${summary.skipped} skipped, ` +
71
+ `and a skipped validator is not covered by this verdict.`);
72
+ }
73
+ const uncovered = [
74
+ summary.skipped > 0 ? `${summary.skipped} skipped` : "",
75
+ nm > 0 ? `${nm} measured nothing (an empty population)` : "",
76
+ ]
77
+ .filter(Boolean)
78
+ .join(", ");
79
+ if (ran === 0) {
80
+ return warn(`0 of ${selected} validators measured anything — ${uncovered}. ` +
81
+ `This is an absence of evidence, not a pass.`);
82
+ }
83
+ return warn(`${ran} of ${selected} validators passed — ${uncovered}; ` +
84
+ `neither is covered by this verdict.`);
85
+ }
86
+ function section(title) {
87
+ return `\n${SECTION_RULE}\n ${title}\n${SECTION_RULE}\n\n`;
88
+ }
89
+ function info(msg) {
90
+ return `[INFO] ${msg}\n`;
91
+ }
92
+ function pass(msg) {
93
+ return `[PASS] ${msg}\n`;
94
+ }
95
+ function fail(msg) {
96
+ return `[FAIL] ${msg}\n`;
97
+ }
98
+ function skip(msg) {
99
+ return `[SKIP] ${msg}\n`;
100
+ }
101
+ function na(msg) {
102
+ return `[N/A] ${msg}\n`;
103
+ }
104
+ function warn(msg) {
105
+ return `[WARN] ${msg}\n`;
106
+ }
107
+ function emptySummary() {
108
+ return { exitCode: 1, passed: 0, failed: 0, skipped: 0, warned: 0, notMeasured: 0 };
109
+ }
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The method-managed validation job, as DATA (bead sdm-dd6.2; Dagger ruling sdm-s48, 2026-10-15).
3
+ *
4
+ * One ordered plan per profile: every default validator, the platform's script-only checks, then the
5
+ * advisory catalogue freshness check. Every executor renders or runs THIS: the GitHub workflow
6
+ * generator (ci-workflow.ts) and the Dagger module (via `sdd-verify validate --list`). The job list
7
+ * drifted once when CI kept a hand copy of the suite (2026-10-01: three validators never reached CI),
8
+ * so no executor may keep its own list.
9
+ */
10
+ import { validatorsForProfile } from "./dispatch.js";
11
+ /** Titles kept from the hand-kept list, so step names stay readable. */
12
+ const TITLES = {
13
+ "adr-structure": "Validate ADR metadata",
14
+ "spec-lifecycle": "Validate spec lifecycle",
15
+ "no-bare-adr": "Reject bare ADR references",
16
+ traceability: "Validate traceability",
17
+ "runway-classification": "Validate runway classification",
18
+ "corpus-consistency": "Validate corpus consistency",
19
+ config: "Validate configuration",
20
+ "app-docs": "Validate app documentation",
21
+ "integration-docs": "Validate integration documentation",
22
+ "yaml-parse": "Validate that tracked YAML parses",
23
+ "gate-readiness": "Validate gate readiness",
24
+ "bundle-coherence": "Report bundle coherence",
25
+ // CI has no platform checkout, so this step runs the app-only checks; the met gate needs the platform beside it.
26
+ "app-dependencies": "Validate app dependencies (app-only checks; no platform checkout in CI)",
27
+ };
28
+ export function methodValidationPlan(profile) {
29
+ const steps = validatorsForProfile(profile)
30
+ .filter((v) => v.optional !== true)
31
+ .map((v) => ({
32
+ id: v.name,
33
+ title: TITLES[v.name] ?? `Validate ${v.name}`,
34
+ tool: "sdd-verify",
35
+ args: ["validate", "--target-repo", ".", "--validator", v.name],
36
+ advisory: false,
37
+ }));
38
+ if (profile === "platform") {
39
+ steps.push({ id: "cascade-completeness", title: "Validate cascade completeness", tool: "bash", args: ["orchestration/scripts/validate-cascade-completeness.sh", "all"], advisory: false }, { id: "architecture-alignment", title: "Validate architecture alignment", tool: "bash", args: ["orchestration/scripts/validate-architecture-alignment.sh"], advisory: false });
40
+ }
41
+ steps.push({
42
+ id: "catalogue-freshness",
43
+ title: "Check catalogues reproduce from the specs (advisory)",
44
+ tool: "sdd-maintain",
45
+ args: ["catalogue", "build", "--check", "--sdd-path", "."],
46
+ advisory: true,
47
+ });
48
+ return steps;
49
+ }
50
+ /** The shell form of a step, as a CI host's `run:` line writes it. */
51
+ export function stepCommand(step) {
52
+ return [step.tool, ...step.args].join(" ");
53
+ }
54
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1,85 @@
1
+ import { execFile as execFileCb } from "node:child_process";
2
+ import { access } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { resolveBundledScript } from "../scripts.js";
6
+ const execFile = promisify(execFileCb);
7
+ export async function runValidator(cfg, ctx) {
8
+ const built = cfg.build(ctx);
9
+ if (built.status === "skip") {
10
+ return {
11
+ name: cfg.name,
12
+ scriptPath: null,
13
+ status: "skip",
14
+ reason: built.reason,
15
+ };
16
+ }
17
+ if (cfg.source.kind === "native") {
18
+ const r = await cfg.source.run(ctx);
19
+ if (r.skipped !== undefined) {
20
+ return {
21
+ name: cfg.name,
22
+ scriptPath: "(native)",
23
+ status: "skip",
24
+ reason: r.skipped,
25
+ };
26
+ }
27
+ return {
28
+ name: cfg.name,
29
+ scriptPath: "(native)",
30
+ status: "run",
31
+ exitCode: r.exitCode,
32
+ stdout: r.stdout,
33
+ stderr: "",
34
+ };
35
+ }
36
+ const resolved = await resolveScriptPath(cfg.source, ctx);
37
+ if (!resolved.exists) {
38
+ return {
39
+ name: cfg.name,
40
+ scriptPath: resolved.path,
41
+ status: "skip",
42
+ reason: `script not found in target repo (${resolved.path}); expected sync to have placed it there`,
43
+ };
44
+ }
45
+ try {
46
+ const { stdout, stderr } = await execFile("bash", [resolved.path, ...built.args], {
47
+ cwd: ctx.targetRepo,
48
+ encoding: "utf8",
49
+ maxBuffer: 16 * 1024 * 1024,
50
+ });
51
+ return {
52
+ name: cfg.name,
53
+ scriptPath: resolved.path,
54
+ status: "run",
55
+ exitCode: 0,
56
+ stdout,
57
+ stderr,
58
+ };
59
+ }
60
+ catch (err) {
61
+ const e = err;
62
+ return {
63
+ name: cfg.name,
64
+ scriptPath: resolved.path,
65
+ status: "run",
66
+ exitCode: typeof e.code === "number" ? e.code : 1,
67
+ stdout: e.stdout ?? "",
68
+ stderr: e.stderr ?? "",
69
+ };
70
+ }
71
+ }
72
+ async function resolveScriptPath(source, ctx) {
73
+ if (source.kind === "bundled") {
74
+ return { exists: true, path: resolveBundledScript(source.script) };
75
+ }
76
+ const path = join(ctx.targetRepo, source.relPath);
77
+ try {
78
+ await access(path);
79
+ return { exists: true, path };
80
+ }
81
+ catch {
82
+ return { exists: false, path };
83
+ }
84
+ }
85
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1,112 @@
1
+ // YAML parse gate — every tracked YAML source must parse STRICTLY.
2
+ //
3
+ // The catalogue builders read YAML file by file and, for most sources, log
4
+ // `invalid YAML` and move on: the file becomes wholly invisible to the
5
+ // catalogue and nothing ever fails. Measured: 19 unparseable files across 4
6
+ // SDDs, 16 in one repo, accumulated that way.
7
+ //
8
+ // THE LOADER IS THE GATE. PyYAML (and any last-key-wins loader) accepts a
9
+ // duplicate key and silently keeps the second value, so a parse-only check
10
+ // with it certifies exactly the corruption a line-surgery transformer
11
+ // produces. `yaml` v2 refuses duplicate keys (DUPLICATE_KEY) and the other
12
+ // malformations a lenient parse glosses — e.g. a `: ` inside an unquoted
13
+ // scalar (`path: (none — tel: URL)`), which no pattern rule catches.
14
+ //
15
+ // Scope: the YAML files git TRACKS (`*.yaml`, `*.yml`) — the repo's sources,
16
+ // not build output or dependencies. Outside a git work tree there is no
17
+ // honest scope, so the check reports a SKIP rather than a pass.
18
+ //
19
+ // A TEMPLATE is not YAML until rendered (Helm/Go `{{- … -}}`, Handlebars).
20
+ // A file that fails to parse AND carries a template directive is listed as
21
+ // templated and not failed — UNLESS one of its errors is a duplicate key: a
22
+ // duplicate key is a duplicate however the file is produced, and exempting it
23
+ // would let any `{{ }}` in a value hide exactly the defect this gate exists for.
24
+ import { execFile } from "node:child_process";
25
+ import { readFile } from "node:fs/promises";
26
+ import { join } from "node:path";
27
+ import { promisify } from "node:util";
28
+ import { parseAllDocuments } from "yaml";
29
+ const execFileAsync = promisify(execFile);
30
+ const TEMPLATE_DIRECTIVE_RE = /\{\{[\s\S]*?\}\}/;
31
+ /** Strict-parse one YAML text; returns the errors (empty = parses). */
32
+ export function strictYamlErrors(text) {
33
+ return parseAllDocuments(text).flatMap((doc) => doc.errors.map((e) => ({
34
+ code: e.code,
35
+ message: e.message.split("\n")[0] ?? e.message,
36
+ })));
37
+ }
38
+ async function trackedYamlFiles(repo) {
39
+ try {
40
+ const { stdout } = await execFileAsync("git", ["-C", repo, "ls-files", "-z", "--", "*.yaml", "*.yml"], { maxBuffer: 64 * 1024 * 1024 });
41
+ return stdout.split("\0").filter((p) => p !== "");
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ export async function checkYamlParse(targetRepo) {
48
+ const files = await trackedYamlFiles(targetRepo);
49
+ if (files === null) {
50
+ return {
51
+ exitCode: 0,
52
+ stdout: "",
53
+ skipped: "not a git work tree — no tracked-file scope to check",
54
+ };
55
+ }
56
+ const failures = [];
57
+ const templated = [];
58
+ let read = 0;
59
+ for (const file of files) {
60
+ let text;
61
+ try {
62
+ text = await readFile(join(targetRepo, file), "utf-8");
63
+ }
64
+ catch {
65
+ continue; // tracked but deleted in the work tree: not a parse question
66
+ }
67
+ read += 1;
68
+ const errors = strictYamlErrors(text);
69
+ if (errors.length > 0 &&
70
+ !errors.some((e) => e.code === "DUPLICATE_KEY") &&
71
+ TEMPLATE_DIRECTIVE_RE.test(text)) {
72
+ templated.push(file);
73
+ continue;
74
+ }
75
+ for (const e of errors) {
76
+ failures.push({ file, code: e.code, message: e.message });
77
+ }
78
+ }
79
+ const checked = read - templated.length;
80
+ const templatedNote = templated.length === 0
81
+ ? ""
82
+ : `[INFO] yaml-parse: ${templated.length} templated file(s) not checked — they do not parse ` +
83
+ `until rendered and carry a {{ … }} directive: ${templated.join(", ")}\n`;
84
+ if (failures.length === 0 && checked === 0) {
85
+ // Zero files checked is absence of evidence, never a pass.
86
+ return {
87
+ exitCode: 0,
88
+ stdout: templatedNote,
89
+ skipped: "no tracked YAML files to parse — nothing to check",
90
+ };
91
+ }
92
+ if (failures.length === 0) {
93
+ return {
94
+ exitCode: 0,
95
+ stdout: templatedNote +
96
+ `[PASS] yaml-parse: ${checked} tracked YAML file(s) parse strictly (duplicate keys refused)\n`,
97
+ };
98
+ }
99
+ const failedFiles = new Set(failures.map((f) => f.file)).size;
100
+ const byCode = new Map();
101
+ for (const f of failures)
102
+ byCode.set(f.code, (byCode.get(f.code) ?? 0) + 1);
103
+ const lines = failures.map((f) => `[FAIL] yaml-parse: ${f.file}: ${f.code}: ${f.message}\n`);
104
+ if (templatedNote !== "")
105
+ lines.unshift(templatedNote);
106
+ lines.push(`[FAIL] yaml-parse: ${failedFiles} of ${checked} tracked YAML file(s) do not parse strictly ` +
107
+ `(${[...byCode].map(([c, n]) => `${c} ×${n}`).join(", ")}). ` +
108
+ `A file that does not parse is invisible to the catalogue; a duplicate key keeps only one ` +
109
+ `of its values under a lenient loader. Fix the file — do not relax the loader.\n`);
110
+ return { exitCode: 1, stdout: lines.join("") };
111
+ }
112
+ //# sourceMappingURL=yaml-parse.js.map
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "built_against_sdd_cli": "1.43.0"
4
+ }