@sdd-method/verify-cli 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 (53) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +17 -0
  3. package/README.md +7 -0
  4. package/dist/lib/catalogue/baseline-check.js +136 -0
  5. package/dist/lib/catalogue/canonical-schema.yaml +3557 -0
  6. package/dist/lib/catalogue/cli-version.js +10 -0
  7. package/dist/lib/catalogue/coverage-predicates.js +603 -0
  8. package/dist/lib/catalogue/coverage-render.js +522 -0
  9. package/dist/lib/catalogue/coverage-run.js +115 -0
  10. package/dist/lib/catalogue/csv-reader.js +97 -0
  11. package/dist/lib/catalogue/csv-writer.js +96 -0
  12. package/dist/lib/catalogue/ddl-generator.js +233 -0
  13. package/dist/lib/catalogue/engine-validator.js +222 -0
  14. package/dist/lib/catalogue/finding-identity.js +183 -0
  15. package/dist/lib/catalogue/findings.js +46 -0
  16. package/dist/lib/catalogue/fk-validator.js +136 -0
  17. package/dist/lib/catalogue/gate-checks.js +338 -0
  18. package/dist/lib/catalogue/graph-projection.js +329 -0
  19. package/dist/lib/catalogue/impact-run.js +228 -0
  20. package/dist/lib/catalogue/manifest-loader.js +422 -0
  21. package/dist/lib/catalogue/relationship-semantics.js +66 -0
  22. package/dist/lib/catalogue/rubric-coverage-map.yaml +764 -0
  23. package/dist/lib/catalogue/scaffold-delta.js +261 -0
  24. package/dist/lib/catalogue/schema-compat.js +52 -0
  25. package/dist/lib/catalogue/schema-loader.js +87 -0
  26. package/dist/lib/catalogue/scores-coverage.js +535 -0
  27. package/dist/lib/catalogue/staleness-verdicts.js +345 -0
  28. package/dist/lib/catalogue/validate.js +176 -0
  29. package/dist/lib/catalogue/version.js +18 -0
  30. package/dist/lib/list/discover.js +100 -0
  31. package/dist/lib/list/format.js +84 -0
  32. package/dist/lib/list/index.js +16 -0
  33. package/dist/lib/lock-file.js +413 -0
  34. package/dist/lib/pack/log.js +18 -0
  35. package/dist/lib/pack/schema.js +121 -0
  36. package/dist/lib/pack/seed.js +182 -0
  37. package/dist/lib/pack/validate.js +49 -0
  38. package/dist/lib/package-version.js +25 -0
  39. package/dist/lib/repo-kind.js +129 -0
  40. package/dist/lib/scripts.js +7 -0
  41. package/dist/lib/validate/aggregate.js +35 -0
  42. package/dist/lib/validate/bundle-coherence.js +69 -0
  43. package/dist/lib/validate/dispatch.js +218 -0
  44. package/dist/lib/validate/index.js +88 -0
  45. package/dist/lib/validate/runner.js +85 -0
  46. package/dist/verbs/catalogue-verify.js +114 -0
  47. package/dist/verbs/list.js +45 -0
  48. package/dist/verbs/pack-validate.js +17 -0
  49. package/dist/verbs/path-option.js +25 -0
  50. package/dist/verbs/validate.js +22 -0
  51. package/dist/verify-bin.js +8 -0
  52. package/dist/verify-program.js +115 -0
  53. package/package.json +32 -0
@@ -0,0 +1,218 @@
1
+ import { checkBundleCoherence } from "./bundle-coherence.js";
2
+ // Validators that accept --app-root / --repo-root flags work fine from the
3
+ // bundled location because they override their internal ROOT_DIR via the
4
+ // flag. Validators without that flag (adr-structure, runway-classification,
5
+ // spec-lifecycle) compute ROOT_DIR as SCRIPT_DIR/../.. — so we must run the
6
+ // copy that sync placed inside the target repo, not the bundled sdd-cli copy.
7
+ const APP_VALIDATORS = [
8
+ {
9
+ name: "app-docs",
10
+ source: { kind: "bundled", script: "validate-app-docs.sh" },
11
+ build: (ctx) => ({ status: "run", args: ["--app-root", ctx.targetRepo] }),
12
+ },
13
+ {
14
+ name: "app-dependencies",
15
+ source: { kind: "bundled", script: "validate-app-dependencies.sh" },
16
+ // NO LONGER SKIPS WITHOUT --platform-root. It used to, and the consequence was
17
+ // that the validator had never executed: measured across 22/22 forward-estate
18
+ // app repos it skipped every time, because nothing passes --platform-root, and
19
+ // the suite printed "All validators passed" over the skip.
20
+ //
21
+ // Only the resolution halves of checks 2 and 3 need a platform surface. The
22
+ // stability vocabulary, "is a service_id declared", and "does a locked
23
+ // dependency name a contract_path" are all answerable in the app repo alone,
24
+ // and they are the more basic defects. The script now runs those and reports
25
+ // the resolutions it could not perform, with their denominator — a partial run
26
+ // that never claims the gate is met (M-ADR 0200 §4.3).
27
+ build: (ctx) => ({
28
+ status: "run",
29
+ args: [
30
+ "--app-root",
31
+ ctx.targetRepo,
32
+ ...(ctx.platformRoot ? ["--platform-root", ctx.platformRoot] : []),
33
+ ],
34
+ }),
35
+ },
36
+ {
37
+ name: "adr-structure",
38
+ source: {
39
+ kind: "target-repo",
40
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
41
+ },
42
+ build: () => ({ status: "run", args: [] }),
43
+ },
44
+ {
45
+ name: "spec-lifecycle",
46
+ source: {
47
+ kind: "target-repo",
48
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
49
+ },
50
+ build: () => ({ status: "run", args: [] }),
51
+ },
52
+ {
53
+ name: "no-bare-adr",
54
+ source: {
55
+ kind: "target-repo",
56
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
57
+ },
58
+ build: () => ({ status: "run", args: [] }),
59
+ },
60
+ {
61
+ name: "bundle-coherence",
62
+ source: {
63
+ kind: "native",
64
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
65
+ },
66
+ build: () => ({ status: "run", args: [] }),
67
+ },
68
+ // Note: validate-runway-classification is intentionally NOT in the app set.
69
+ // It requires platform-level files (docs/platform/feature-tree.md,
70
+ // architecture-runway-index.md) that app repos don't own. Per ADR 0089,
71
+ // the reduced app suite is app-specific validators + shared foundations,
72
+ // not platform validators.
73
+ ];
74
+ const INTEGRATION_VALIDATORS = [
75
+ {
76
+ name: "integration-docs",
77
+ source: { kind: "bundled", script: "validate-integration-docs.sh" },
78
+ build: (ctx) => ({ status: "run", args: ["--repo-root", ctx.targetRepo] }),
79
+ },
80
+ {
81
+ name: "adr-structure",
82
+ source: {
83
+ kind: "target-repo",
84
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
85
+ },
86
+ build: () => ({ status: "run", args: [] }),
87
+ },
88
+ {
89
+ name: "spec-lifecycle",
90
+ source: {
91
+ kind: "target-repo",
92
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
93
+ },
94
+ build: () => ({ status: "run", args: [] }),
95
+ },
96
+ {
97
+ name: "no-bare-adr",
98
+ source: {
99
+ kind: "target-repo",
100
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
101
+ },
102
+ build: () => ({ status: "run", args: [] }),
103
+ },
104
+ {
105
+ name: "bundle-coherence",
106
+ source: {
107
+ kind: "native",
108
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
109
+ },
110
+ build: () => ({ status: "run", args: [] }),
111
+ },
112
+ ];
113
+ const PLATFORM_VALIDATORS = [
114
+ {
115
+ name: "traceability",
116
+ source: {
117
+ kind: "target-repo",
118
+ relPath: "orchestration/scripts/validate-traceability.sh",
119
+ },
120
+ build: () => ({ status: "run", args: ["all"] }),
121
+ },
122
+ {
123
+ name: "runway-classification",
124
+ source: {
125
+ kind: "target-repo",
126
+ relPath: "orchestration/scripts/validate-runway-classification.sh",
127
+ },
128
+ build: () => ({ status: "run", args: [] }),
129
+ },
130
+ {
131
+ name: "adr-structure",
132
+ source: {
133
+ kind: "target-repo",
134
+ relPath: "orchestration/scripts/validate-adr-structure.sh",
135
+ },
136
+ build: () => ({ status: "run", args: [] }),
137
+ },
138
+ {
139
+ name: "spec-lifecycle",
140
+ source: {
141
+ kind: "target-repo",
142
+ relPath: "orchestration/scripts/validate-spec-lifecycle.sh",
143
+ },
144
+ build: () => ({ status: "run", args: [] }),
145
+ },
146
+ {
147
+ name: "no-bare-adr",
148
+ source: {
149
+ kind: "target-repo",
150
+ relPath: "orchestration/scripts/validate-no-bare-adr.sh",
151
+ },
152
+ build: () => ({ status: "run", args: [] }),
153
+ },
154
+ {
155
+ name: "corpus-consistency",
156
+ source: {
157
+ kind: "target-repo",
158
+ relPath: "orchestration/scripts/validate-corpus-consistency.sh",
159
+ },
160
+ build: () => ({ status: "run", args: [] }),
161
+ },
162
+ {
163
+ name: "config",
164
+ source: {
165
+ kind: "target-repo",
166
+ relPath: "orchestration/scripts/validate-config.sh",
167
+ },
168
+ build: () => ({ status: "run", args: [] }),
169
+ },
170
+ {
171
+ name: "gate-readiness",
172
+ source: {
173
+ kind: "target-repo",
174
+ relPath: "orchestration/scripts/validate-gate-readiness.sh",
175
+ },
176
+ build: () => ({ status: "run", args: [] }),
177
+ },
178
+ {
179
+ name: "bundle-coherence",
180
+ source: {
181
+ kind: "native",
182
+ run: async (ctx) => checkBundleCoherence(ctx.targetRepo),
183
+ },
184
+ build: () => ({ status: "run", args: [] }),
185
+ },
186
+ // Optional: the upstream validate-docs.sh dispatcher covers the 6
187
+ // conditional validators (backward-compatibility, deprecation-runway,
188
+ // migration-completeness, ddd-artifacts, glossary-alignment,
189
+ // intent-quality-readiness) and self-skips when their inputs (BASELINE.md,
190
+ // gate-registry files) aren't present. Invoked via --validator docs-all.
191
+ {
192
+ name: "docs-all",
193
+ source: {
194
+ kind: "target-repo",
195
+ relPath: "orchestration/scripts/validate-docs.sh",
196
+ },
197
+ build: () => ({ status: "run", args: ["all"] }),
198
+ optional: true,
199
+ },
200
+ ];
201
+ export function validatorsForProfile(profile) {
202
+ if (profile === "platform")
203
+ return PLATFORM_VALIDATORS;
204
+ return profile === "application" ? APP_VALIDATORS : INTEGRATION_VALIDATORS;
205
+ }
206
+ export function selectValidators(profile, filter) {
207
+ const all = validatorsForProfile(profile);
208
+ if (!filter)
209
+ return all.filter((v) => v.optional !== true);
210
+ const matched = all.find((v) => v.name === filter);
211
+ if (!matched) {
212
+ throw new Error(`Unknown validator "${filter}" for ${profile} profile. Available: ${all
213
+ .map((v) => v.name)
214
+ .join(", ")}`);
215
+ }
216
+ return [matched];
217
+ }
218
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1,88 @@
1
+ import { profileFromRepoKind, readRepoKind } from "../repo-kind.js";
2
+ import { aggregateResults } 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(pass(`${cfg.name}: ok`));
40
+ else
41
+ stdout(fail(`${cfg.name}: exit ${result.exitCode}`));
42
+ }
43
+ const summary = aggregateResults(results, { strict: opts.strict });
44
+ stdout(info(`Summary: passed=${summary.passed} failed=${summary.failed} skipped=${summary.skipped} warned=${summary.warned}${opts.strict ? " (strict)" : ""}`));
45
+ stdout(verdict(summary, validators.length));
46
+ return { exitCode: summary.exitCode, summary };
47
+ }
48
+ // Every verdict states its denominator. A skipped validator did not run, so it
49
+ // cannot be covered by a green verdict: "all passed" with one skipped and "all
50
+ // passed" with none were previously the same line (M-ADR 0200 §4.3). The
51
+ // exit code is deliberately unchanged — the defect is the claim, not the
52
+ // gating; whether an unexecutable validator should FAIL is the open design
53
+ // question (post-3.0.0-followons.md A2), not this one.
54
+ function verdict(summary, selected) {
55
+ if (summary.exitCode !== 0)
56
+ return fail("One or more validators failed");
57
+ const ran = summary.passed + summary.failed;
58
+ if (summary.skipped === 0)
59
+ return pass("All validators passed");
60
+ if (ran === 0) {
61
+ return warn(`0 of ${selected} validators ran — all ${summary.skipped} skipped. ` +
62
+ `This is an absence of evidence, not a pass.`);
63
+ }
64
+ return warn(`${ran} of ${selected} validators passed — ${summary.skipped} skipped, ` +
65
+ `and a skipped validator is not covered by this verdict.`);
66
+ }
67
+ function section(title) {
68
+ return `\n${SECTION_RULE}\n ${title}\n${SECTION_RULE}\n\n`;
69
+ }
70
+ function info(msg) {
71
+ return `[INFO] ${msg}\n`;
72
+ }
73
+ function pass(msg) {
74
+ return `[PASS] ${msg}\n`;
75
+ }
76
+ function fail(msg) {
77
+ return `[FAIL] ${msg}\n`;
78
+ }
79
+ function skip(msg) {
80
+ return `[SKIP] ${msg}\n`;
81
+ }
82
+ function warn(msg) {
83
+ return `[WARN] ${msg}\n`;
84
+ }
85
+ function emptySummary() {
86
+ return { exitCode: 1, passed: 0, failed: 0, skipped: 0, warned: 0 };
87
+ }
88
+ //# sourceMappingURL=index.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,114 @@
1
+ // The read-only `catalogue` subcommands — validate (incl. --coverage), impact,
2
+ // baseline-check, version — split from `catalogue.ts` so a program can register
3
+ // them without importing the catalogue BUILD path. The verify-only CLI ships
4
+ // these and must not ship code that writes the derived model (M-ADR 0212 §4.3b).
5
+ // `registerCatalogue` calls each at its original position, so sdd-cli's
6
+ // `catalogue` surface and help order are unchanged.
7
+ //
8
+ // ⚠ Import each run* from its own module, NEVER from `../lib/catalogue/index.js`:
9
+ // that barrel re-exports the build and aggregate paths, and one convenience
10
+ // import from it would silently put authoring code back into the verify package.
11
+ import { runBaselineCheck } from "../lib/catalogue/baseline-check.js";
12
+ import { runCatalogueCoverage } from "../lib/catalogue/coverage-run.js";
13
+ import { runCatalogueImpact } from "../lib/catalogue/impact-run.js";
14
+ import { runCatalogueValidate } from "../lib/catalogue/validate.js";
15
+ import { runCatalogueVersion } from "../lib/catalogue/version.js";
16
+ export function registerCatalogueValidate(catalogue) {
17
+ catalogue
18
+ .command("validate")
19
+ .description("Re-run column-shape + FK validation against existing catalogues/ (no rebuild)")
20
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
21
+ .option("--strict", "Treat findings as failures")
22
+ .option("--coverage", "Coverage mode: run the engine coverage predicates instead (same lines the gate scripts printed; exit 0 — gating stays the baseline's job)")
23
+ .option("--tier <tier>", "Coverage tier, explicit never inferred (member | aggregate; default member)")
24
+ .option("--catalogues-dir <dir>", "Coverage mode: catalogues directory relative to the SDD root (default catalogues)")
25
+ .action(async (opts) => {
26
+ if (opts.coverage === true) {
27
+ if (opts.tier !== undefined &&
28
+ opts.tier !== "member" &&
29
+ opts.tier !== "aggregate") {
30
+ process.stderr.write(`--tier must be 'member' or 'aggregate' (got: ${opts.tier})\n`);
31
+ process.exit(2);
32
+ }
33
+ const result = await runCatalogueCoverage({
34
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
35
+ ...(opts.cataloguesDir !== undefined
36
+ ? { cataloguesDir: opts.cataloguesDir }
37
+ : {}),
38
+ ...(opts.tier !== undefined
39
+ ? { tier: opts.tier }
40
+ : {}),
41
+ }, (s) => process.stdout.write(s));
42
+ if (result.exitCode !== 0)
43
+ process.exit(result.exitCode);
44
+ return;
45
+ }
46
+ const result = await runCatalogueValidate({
47
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
48
+ ...(opts.strict !== undefined ? { strict: opts.strict } : {}),
49
+ }, (s) => process.stdout.write(s));
50
+ if (result.exitCode !== 0)
51
+ process.exit(result.exitCode);
52
+ });
53
+ }
54
+ export function registerCatalogueImpact(catalogue) {
55
+ catalogue
56
+ .command("impact")
57
+ .description("Change-impact closure of a node (M-ADR 0206): each relationship is traversed per its declared impact-flow from the rubric-coverage-map — dependency edges reach their consumers, membership edges propagate part-to-whole only, xref/lineage records are inert — computed by recursive SQL on the embedded engine, with the co-directionality invariant asserted on every run")
58
+ .requiredOption("--of <node-id>", 'Node to compute impact of, in the projection\'s "<catalogue>/<id>" form (e.g. contracts/orders-api)')
59
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
60
+ .option("--catalogues-dir <dir>", "Catalogues directory relative to the SDD root (default catalogues)")
61
+ .option("--tier <tier>", "Tier, explicit never inferred (member | aggregate; default member)")
62
+ .action(async (opts) => {
63
+ if (opts.tier !== undefined &&
64
+ opts.tier !== "member" &&
65
+ opts.tier !== "aggregate") {
66
+ process.stderr.write(`--tier must be 'member' or 'aggregate' (got: ${opts.tier})\n`);
67
+ process.exit(2);
68
+ }
69
+ const result = await runCatalogueImpact({
70
+ of: opts.of,
71
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
72
+ ...(opts.cataloguesDir !== undefined
73
+ ? { cataloguesDir: opts.cataloguesDir }
74
+ : {}),
75
+ ...(opts.tier !== undefined
76
+ ? { tier: opts.tier }
77
+ : {}),
78
+ }, (s) => process.stdout.write(s));
79
+ if (result.exitCode !== 0)
80
+ process.exit(result.exitCode);
81
+ });
82
+ }
83
+ export function registerCatalogueBaselineCheck(catalogue) {
84
+ catalogue
85
+ .command("baseline-check")
86
+ .description("Compare emitted findings against the repo's findings baseline BY IDENTITY (M-ADR 0132 §9) — a reworded finding reports as reworded, not as one closed and one new")
87
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
88
+ .option("--findings <path>", "File of findings to compare (defaults to stdin, so it composes with `catalogue validate --coverage`)")
89
+ .option("--baseline <path>", "Baseline file (defaults to <sdd-path>/orchestration/catalogue-findings-baseline.txt)")
90
+ .action(async (opts) => {
91
+ const result = await runBaselineCheck({
92
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
93
+ ...(opts.findings !== undefined ? { findings: opts.findings } : {}),
94
+ ...(opts.baseline !== undefined ? { baseline: opts.baseline } : {}),
95
+ stdin: async () => {
96
+ const chunks = [];
97
+ for await (const c of process.stdin)
98
+ chunks.push(Buffer.from(c));
99
+ return Buffer.concat(chunks).toString("utf-8");
100
+ },
101
+ }, (s) => process.stdout.write(s));
102
+ if (result.exitCode !== 0)
103
+ process.exit(result.exitCode);
104
+ });
105
+ }
106
+ export function registerCatalogueVersion(catalogue) {
107
+ catalogue
108
+ .command("version")
109
+ .description("Print the catalogue schema version + sdd-cli version pin")
110
+ .action(async () => {
111
+ await runCatalogueVersion((s) => process.stdout.write(s));
112
+ });
113
+ }
114
+ //# sourceMappingURL=catalogue-verify.js.map
@@ -0,0 +1,45 @@
1
+ import { runList } from "../lib/list/index.js";
2
+ const VALID_PROFILES = [
3
+ "platform",
4
+ "application",
5
+ "integration",
6
+ ];
7
+ export function registerList(program) {
8
+ program
9
+ .command("list")
10
+ .description("Enumerate SDD repositories discoverable in the current workspace, including adopter pack provenance (ADR 0151)")
11
+ .option("--profile <profile>", "filter by profile: platform | application | integration")
12
+ .option("--workspace-root <path>", "workspace directory to walk (repeatable; defaults to cwd)", collectRoots, [])
13
+ .option("--format <format>", "output format: text | json", "text")
14
+ .option("--verbose", "show managed_path counts per bundle (text format)")
15
+ .action(async (opts) => {
16
+ const profileFilter = parseProfile(opts.profile);
17
+ const format = parseFormat(opts.format);
18
+ const { exitCode } = await runList({
19
+ workspaceRoots: opts.workspaceRoot ?? [],
20
+ format,
21
+ verbose: opts.verbose === true,
22
+ ...(profileFilter ? { profileFilter } : {}),
23
+ }, (s) => process.stdout.write(s));
24
+ if (exitCode !== 0)
25
+ process.exit(exitCode);
26
+ });
27
+ }
28
+ function collectRoots(value, prior) {
29
+ prior.push(value);
30
+ return prior;
31
+ }
32
+ function parseProfile(value) {
33
+ if (value === undefined)
34
+ return undefined;
35
+ if (!VALID_PROFILES.includes(value)) {
36
+ throw new Error(`Unknown --profile "${value}". Expected one of: ${VALID_PROFILES.join(", ")}`);
37
+ }
38
+ return value;
39
+ }
40
+ function parseFormat(value) {
41
+ if (value === "text" || value === "json")
42
+ return value;
43
+ throw new Error(`Unknown --format "${value}". Expected "text" or "json".`);
44
+ }
45
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1,17 @@
1
+ // `pack validate` registration, split from `pack.ts` so a program can register
2
+ // it without importing `runPackBuild` — the verify-only CLI must not ship the
3
+ // code that writes a pack (M-ADR 0212 §4.3b). `registerPack` calls this, so
4
+ // sdd-cli's `pack` surface and help order are unchanged.
5
+ import { runPackValidate } from "../lib/pack/validate.js";
6
+ export function registerPackValidate(pack) {
7
+ pack
8
+ .command("validate")
9
+ .description("Validate a context-pack seed file against the canonical schema. Fast feedback for authoring.")
10
+ .requiredOption("--seed <path>", "path to the seed file (orchestration/distribution/{name}-seed.yaml)")
11
+ .action(async (opts) => {
12
+ const { exitCode } = await runPackValidate({ seedPath: opts.seed });
13
+ if (exitCode !== 0)
14
+ process.exit(exitCode);
15
+ });
16
+ }
17
+ //# sourceMappingURL=pack-validate.js.map
@@ -0,0 +1,25 @@
1
+ import { resolve } from "node:path";
2
+ /**
3
+ * Commander parse-arg coercion for an option naming a DIRECTORY ON DISK.
4
+ *
5
+ * Resolves the operand to an absolute path ONCE, at the option boundary, before
6
+ * any verb sees it. That placement is the fix, not a detail: B6 was a relative
7
+ * `--target-repo` that resolved far enough to succeed at the first read and
8
+ * then failed at every later one.
9
+ *
10
+ * `validate` read `.sdd-repo-kind` relative to the process cwd — which WORKS,
11
+ * so the run announced the correct profile and dispatched the full validator
12
+ * set — then handed the same unresolved string to bash validators running from
13
+ * a different working directory, which reported `App root not found` and exit
14
+ * 127. A healthy repo came back `passed=0 failed=5`, so the operator saw a
15
+ * broken REPO rather than a bad ARGUMENT. Repairing those messages would have
16
+ * been the wrong fix; the path is what is wrong, and it is wrong exactly once.
17
+ *
18
+ * Resolving at the boundary also means a verb cannot forget: there is no
19
+ * per-consumer `resolve()` to omit, and a new consumer of the option inherits
20
+ * an absolute path by construction.
21
+ */
22
+ export function resolvePathOption(value) {
23
+ return resolve(value);
24
+ }
25
+ //# sourceMappingURL=path-option.js.map
@@ -0,0 +1,22 @@
1
+ import { runValidate } from "../lib/validate/index.js";
2
+ import { resolvePathOption } from "./path-option.js";
3
+ export function registerValidate(program) {
4
+ program
5
+ .command("validate")
6
+ .description("Run the method-level validation suite for this SDD repository")
7
+ .option("--target-repo <dir>", "target SDD repo", resolvePathOption, process.cwd())
8
+ .option("--platform-root <dir>", "path to the sdd-method (or platform) repo, required for app-dependencies", resolvePathOption)
9
+ .option("--validator <name>", "run only the named validator")
10
+ .option("--strict", "treat warnings as failures")
11
+ .action(async (opts) => {
12
+ const { exitCode } = await runValidate({
13
+ targetRepo: opts.targetRepo,
14
+ ...(opts.platformRoot ? { platformRoot: opts.platformRoot } : {}),
15
+ ...(opts.validator ? { validator: opts.validator } : {}),
16
+ strict: opts.strict === true,
17
+ });
18
+ if (exitCode !== 0)
19
+ process.exit(exitCode);
20
+ });
21
+ }
22
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { runVerify } from "./verify-program.js";
3
+ runVerify(process.argv.slice(2)).catch((err) => {
4
+ const message = err instanceof Error ? err.message : String(err);
5
+ process.stderr.write(`sdd-verify: ${message}\n`);
6
+ process.exit(1);
7
+ });
8
+ //# sourceMappingURL=verify-bin.js.map