@sdd-method/verify-cli 1.0.0 → 1.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.
@@ -2,9 +2,9 @@
2
2
  * sdd-cli version constant, used in error messages from the catalogue
3
3
  * builder + aggregator + validate subcommands.
4
4
  *
5
- * Read from package.json via getPackageVersion() to guarantee the
5
+ * Read from package.json via getSddCliVersion() (the ENGINE version, also in a persona package) to guarantee the
6
6
  * constant cannot drift from the published version.
7
7
  */
8
- import { getPackageVersion } from "../package-version.js";
9
- export const CLI_VERSION = getPackageVersion();
8
+ import { getSddCliVersion } from "../package-version.js";
9
+ export const CLI_VERSION = getSddCliVersion();
10
10
  //# sourceMappingURL=cli-version.js.map
@@ -62,6 +62,10 @@ export function serialiseLockFile(lock) {
62
62
  const lines = [
63
63
  `method_repo: ${lock.methodRepo}`,
64
64
  `lock_version: ${lock.lockVersion}`,
65
+ ...(lock.ciBinding !== undefined ? [`ci_binding: ${lock.ciBinding}`] : []),
66
+ ...(lock.ciRunner !== undefined ? [`ci_runner: "${lock.ciRunner}"`] : []),
67
+ ...(lock.ciWorkflowBlockSha256 !== undefined ? [`ci_workflow_block_sha256: "${lock.ciWorkflowBlockSha256}"`] : []),
68
+ ...(lock.ciModuleSha256 !== undefined ? [`ci_module_sha256: "${lock.ciModuleSha256}"`] : []),
65
69
  "bundles:",
66
70
  ];
67
71
  for (const b of lock.bundles) {
@@ -109,6 +113,10 @@ function stripQuotes(value) {
109
113
  function parseVersioned(raw) {
110
114
  let methodRepo;
111
115
  let lockVersion;
116
+ let ciWorkflowBlockSha256;
117
+ let ciBinding;
118
+ let ciRunner;
119
+ let ciModuleSha256;
112
120
  const bundles = [];
113
121
  let inBundles = false;
114
122
  let inPaths = false;
@@ -170,6 +178,14 @@ function parseVersioned(raw) {
170
178
  methodRepo = stripQuotes(value);
171
179
  else if (key === "lock_version")
172
180
  lockVersion = Number(stripQuotes(value));
181
+ else if (key === "ci_workflow_block_sha256")
182
+ ciWorkflowBlockSha256 = stripQuotes(value);
183
+ else if (key === "ci_binding")
184
+ ciBinding = stripQuotes(value);
185
+ else if (key === "ci_runner")
186
+ ciRunner = stripQuotes(value);
187
+ else if (key === "ci_module_sha256")
188
+ ciModuleSha256 = stripQuotes(value);
173
189
  }
174
190
  continue;
175
191
  }
@@ -289,7 +305,15 @@ function parseVersioned(raw) {
289
305
  ? ` — the lock was written by a newer sdd-cli; upgrade rather than downgrading the lock.`
290
306
  : `.`));
291
307
  }
292
- return { methodRepo, lockVersion: LOCK_VERSION, bundles };
308
+ return {
309
+ methodRepo,
310
+ lockVersion: LOCK_VERSION,
311
+ bundles,
312
+ ...(ciBinding !== undefined ? { ciBinding } : {}),
313
+ ...(ciRunner !== undefined ? { ciRunner } : {}),
314
+ ...(ciWorkflowBlockSha256 !== undefined ? { ciWorkflowBlockSha256 } : {}),
315
+ ...(ciModuleSha256 !== undefined ? { ciModuleSha256 } : {}),
316
+ };
293
317
  }
294
318
  function migrateV1(raw, legacyBundleName) {
295
319
  const scalars = new Map();
@@ -4,7 +4,7 @@
4
4
  // (M-ADR 0212 §4.3b). `index.ts` re-exports it, so existing imports are unchanged.
5
5
  import { validateSeed } from "./schema.js";
6
6
  import { loadSeed } from "./seed.js";
7
- import { getPackageVersion } from "../package-version.js";
7
+ import { getSddCliVersion } from "../package-version.js";
8
8
  import { fail, info, pass, section } from "./log.js";
9
9
  export async function runPackValidate(opts, stdout = (s) => process.stdout.write(s)) {
10
10
  stdout(section("Pack validate"));
@@ -21,7 +21,7 @@ export async function runPackValidate(opts, stdout = (s) => process.stdout.write
21
21
  // than itself, exactly as for `.sdd/sdd-manifest.yaml`. Silence here is what let an
22
22
  // adopter follow the schema and get a clean validation with no effect.
23
23
  if (seed.unknownKeys && seed.unknownKeys.length > 0) {
24
- stdout(info(`${seed.unknownKeys.length} seed key(s) not understood by sdd-cli ${getPackageVersion()} ` +
24
+ stdout(info(`${seed.unknownKeys.length} seed key(s) not understood by sdd-cli ${getSddCliVersion()} ` +
25
25
  `and therefore IGNORED: ${seed.unknownKeys.join(", ")}. If the seed follows a newer ` +
26
26
  `context-pack-seed-schema than this build, upgrade sdd-cli; otherwise remove or correct the key.`));
27
27
  }
@@ -13,6 +13,25 @@ import { fileURLToPath } from "node:url";
13
13
  * Cached on first call.
14
14
  */
15
15
  let cached;
16
+ let cachedEngine;
17
+ /**
18
+ * The version of the sdd-cli ENGINE this code is. In @sdd-method/sdd-cli it is the package
19
+ * version. In a persona package (verify-cli, maintain-cli) the package version is the
20
+ * persona's own (0.1.0, 1.0.0), while every check and stamp that means "which sdd-cli is
21
+ * this" (a bundle's minimum_sdd_cli_version, the CLI version stamped into catalogues, the
22
+ * MCP server identity) must see the engine it was built from. The persona build writes that
23
+ * as `sddCliVersion` in the package's package.json. Found by maintain-cli's acceptance run:
24
+ * its sync refused every bundle because 0.1.0 < 1.41.0.
25
+ */
26
+ export function getSddCliVersion() {
27
+ if (cachedEngine !== undefined)
28
+ return cachedEngine;
29
+ const here = dirname(fileURLToPath(import.meta.url));
30
+ const pkg = JSON.parse(readFileSync(resolve(here, "..", "..", "package.json"), "utf8"));
31
+ cachedEngine = pkg.sddCliVersion ?? pkg.version;
32
+ return cachedEngine;
33
+ }
34
+ /** The package's own version: what `--version` prints. For the engine version, use getSddCliVersion. */
16
35
  export function getPackageVersion() {
17
36
  if (cached !== undefined)
18
37
  return cached;
@@ -36,20 +55,38 @@ export function getPackageVersion() {
36
55
  *
37
56
  * Throws rather than defaults: a missing version must not render a floating install.
38
57
  */
39
- let cachedVerify;
40
- export function getVerifyCliVersion() {
41
- if (cachedVerify !== undefined)
42
- return cachedVerify;
58
+ const cachedPersona = {};
59
+ /**
60
+ * One exact npm version: X.Y.Z, optionally with a prerelease tag (1.1.0-rc.0), which a release
61
+ * candidate published under the `next` dist-tag needs so hosted CI can run it before the release.
62
+ * Never a range, a tag name or a wildcard.
63
+ */
64
+ export const EXACT_VERSION = /^\d+\.\d+\.\d+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$/;
65
+ function personaVersion(persona) {
66
+ const hit = cachedPersona[persona];
67
+ if (hit !== undefined)
68
+ return hit;
43
69
  const here = dirname(fileURLToPath(import.meta.url));
44
70
  const fromSource = here.endsWith(`${sep}src${sep}lib`);
45
71
  const file = fromSource
46
- ? resolve(here, "..", "..", "scripts", "verify-version.json")
47
- : resolve(here, "..", "verify-version.json");
72
+ ? resolve(here, "..", "..", "scripts", `${persona}-version.json`)
73
+ : resolve(here, "..", `${persona}-version.json`);
48
74
  const parsed = JSON.parse(readFileSync(file, "utf8"));
49
- if (typeof parsed.version !== "string" || !/^\d+\.\d+\.\d+$/.test(parsed.version)) {
50
- throw new Error(`${file} has no semver \`version\`; refusing to render an unpinned verify-cli install`);
75
+ if (typeof parsed.version !== "string" || !EXACT_VERSION.test(parsed.version)) {
76
+ throw new Error(`${file} has no semver \`version\`; refusing to render an unpinned ${persona}-cli install`);
51
77
  }
52
- cachedVerify = parsed.version;
53
- return cachedVerify;
78
+ cachedPersona[persona] = parsed.version;
79
+ return parsed.version;
80
+ }
81
+ export function getVerifyCliVersion() {
82
+ return personaVersion("verify");
83
+ }
84
+ /**
85
+ * The @sdd-method/maintain-cli version released WITH this sdd-cli. The managed CI workflow's
86
+ * catalogue freshness step installs exactly this version (M-ADR 0218 §5; ruled 2026-10-15, sdm-jts.6).
87
+ * Read the same way as the verify version, for the same reason (dist/ copy, never scripts/ live).
88
+ */
89
+ export function getMaintainCliVersion() {
90
+ return personaVersion("maintain");
54
91
  }
55
92
  //# sourceMappingURL=package-version.js.map
@@ -11,7 +11,7 @@
11
11
  // read 0 (the scores-coverage precedent, M-ADR 0205 §4.5).
12
12
  import { readLockFile, findForwardBase } from "../lock-file.js";
13
13
  import { loadSddManifest, } from "../catalogue/manifest-loader.js";
14
- import { getPackageVersion } from "../package-version.js";
14
+ import { getSddCliVersion } from "../package-version.js";
15
15
  /**
16
16
  * Manifest profile → the forward base that profile expects. Profiles with no
17
17
  * entry (support-repo, organisation-profile) carry no expectation — silence,
@@ -37,7 +37,7 @@ export async function checkBundleCoherence(targetRepo) {
37
37
  let profile;
38
38
  try {
39
39
  const manifest = await loadSddManifest(targetRepo, {
40
- cliVersion: getPackageVersion(),
40
+ cliVersion: getSddCliVersion(),
41
41
  });
42
42
  profile = manifest.profile;
43
43
  }
@@ -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,4 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "built_against_sdd_cli": "1.42.0"
4
+ }
@@ -1,4 +1,8 @@
1
1
  import { runValidate } from "../lib/validate/index.js";
2
+ import { methodValidationPlan } from "../lib/validate/plan.js";
3
+ import { profileFromRepoKind, readRepoKind } from "../lib/repo-kind.js";
4
+ const LISTABLE = ["platform", "application", "integration"];
5
+ const isListable = (p) => LISTABLE.includes(p);
2
6
  import { resolvePathOption } from "./path-option.js";
3
7
  export function registerValidate(program) {
4
8
  program
@@ -8,7 +12,29 @@ export function registerValidate(program) {
8
12
  .option("--platform-root <dir>", "path to the sdd-method (or platform) repo, required for app-dependencies", resolvePathOption)
9
13
  .option("--validator <name>", "run only the named validator")
10
14
  .option("--strict", "treat warnings as failures")
15
+ .option("--list", "print the method-managed validation job for this repo's profile as JSON (the plan every CI executor runs, the Dagger module included) and run nothing")
16
+ .option("--profile <profile>", "with --list: the profile to list, instead of reading .sdd-repo-kind (platform | application | integration)")
11
17
  .action(async (opts) => {
18
+ if (opts.list === true) {
19
+ let profile;
20
+ if (opts.profile !== undefined) {
21
+ if (!isListable(opts.profile)) {
22
+ process.stderr.write(`--profile must be platform, application or integration (got: ${opts.profile})\n`);
23
+ process.exit(2);
24
+ }
25
+ profile = opts.profile;
26
+ }
27
+ else {
28
+ const kind = await readRepoKind(opts.targetRepo);
29
+ if (kind === null) {
30
+ process.stderr.write(`Not an SDD repo (no .sdd-repo-kind in ${opts.targetRepo}); pass --profile\n`);
31
+ process.exit(1);
32
+ }
33
+ profile = profileFromRepoKind(kind);
34
+ }
35
+ process.stdout.write(JSON.stringify({ profile, steps: methodValidationPlan(profile) }, null, 2) + "\n");
36
+ return;
37
+ }
12
38
  const { exitCode } = await runValidate({
13
39
  targetRepo: opts.targetRepo,
14
40
  ...(opts.platformRoot ? { platformRoot: opts.platformRoot } : {}),
@@ -49,7 +49,7 @@ const OUTSIDE = "is not part of sdd-verify, which cannot write to the repository
49
49
  * this binary exists to prevent. So an excluded verb fails, loudly, whatever
50
50
  * flags accompany it.
51
51
  */
52
- export function rejectOutsideSurface(program, argv) {
52
+ export function rejectOutsideSurface(program, argv, outside = OUTSIDE) {
53
53
  const words = argv.filter((a) => !a.startsWith("-"));
54
54
  if (words.length === 0)
55
55
  return;
@@ -58,10 +58,10 @@ export function rejectOutsideSurface(program, argv) {
58
58
  return;
59
59
  const cmd = program.commands.find((c) => c.name() === verb);
60
60
  if (!cmd)
61
- throw new Error(`'${verb}' ${OUTSIDE}`);
61
+ throw new Error(`'${verb}' ${outside}`);
62
62
  const subs = cmd.commands.map((c) => c.name()).filter((n) => n !== "help");
63
63
  if (subs.length > 0 && sub !== undefined && sub !== "help" && !subs.includes(sub)) {
64
- throw new Error(`'${verb} ${sub}' ${OUTSIDE}`);
64
+ throw new Error(`'${verb} ${sub}' ${outside}`);
65
65
  }
66
66
  }
67
67
  export async function runVerify(argv) {
@@ -97,6 +97,14 @@ export function buildVerifyProgram() {
97
97
  writeOut: (str) => process.stdout.write(str),
98
98
  writeErr: (str) => process.stderr.write(str),
99
99
  });
100
+ registerVerifySurface(program);
101
+ return program;
102
+ }
103
+ /**
104
+ * The verify verbs, registered onto any program. sdd-verify calls it; sdd-maintain calls it too,
105
+ * which is what makes the personas nest structurally: whatever verify ships, maintain ships.
106
+ */
107
+ export function registerVerifySurface(program) {
100
108
  registerValidate(program);
101
109
  registerList(program);
102
110
  const pack = program
@@ -110,6 +118,5 @@ export function buildVerifyProgram() {
110
118
  registerCatalogueImpact(catalogue);
111
119
  registerCatalogueBaselineCheck(catalogue);
112
120
  registerCatalogueVersion(catalogue);
113
- return program;
114
121
  }
115
122
  //# sourceMappingURL=verify-program.js.map
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "1.0.0",
3
- "built_against_sdd_cli": "1.41.0"
2
+ "version": "1.1.0",
3
+ "built_against_sdd_cli": "1.42.0"
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@sdd-method/verify-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
+ "sddCliVersion": "1.42.0",
4
5
  "description": "Verify an SDD repository without changing it — validate, list, and read-only catalogue and pack checks. Ships only the code those verbs reach; it cannot write to the repository it is pointed at. Authoring and maintenance are in @sdd-method/sdd-cli.",
5
6
  "license": "Apache-2.0",
6
7
  "type": "module",