@sdd-method/verify-cli 0.4.0 → 1.1.0-rc.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.
- package/dist/lib/catalogue/canonical-schema.yaml +66 -1
- package/dist/lib/catalogue/cli-version.js +3 -3
- package/dist/lib/catalogue/manifest-loader.js +4 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +10 -0
- package/dist/lib/lock-file.js +20 -1
- package/dist/lib/pack/validate.js +2 -2
- package/dist/lib/package-version.js +68 -1
- package/dist/lib/validate/bundle-coherence.js +2 -2
- package/dist/lib/validate/plan.js +54 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/validate.js +26 -0
- package/dist/verify-program.js +11 -4
- package/dist/verify-version.json +4 -0
- package/package.json +3 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Emitted by sdd-cli's YAML emitter with no line folding; the three header scalars are quoted for the
|
|
8
8
|
# textual readers downstream. The one deliberate difference from the authored form is schema_status:
|
|
9
9
|
# the authored form says canonical; a projection never does.
|
|
10
|
-
schema_version: "3.
|
|
10
|
+
schema_version: "3.20.0"
|
|
11
11
|
schema_owner: "sdd-method"
|
|
12
12
|
schema_status: "generated"
|
|
13
13
|
enums:
|
|
@@ -606,6 +606,13 @@ enums:
|
|
|
606
606
|
- value: no-canonical-home
|
|
607
607
|
description: "The platform has no canonical field for the item: platform contract work."
|
|
608
608
|
description: Why an unsupported feature is unsupported, and so whose work the gap is.
|
|
609
|
+
repo_dependency_kind:
|
|
610
|
+
values:
|
|
611
|
+
- value: library
|
|
612
|
+
description: Linked or packaged at build time.
|
|
613
|
+
- value: generated-from
|
|
614
|
+
description: Its content was produced from the other repository's templates.
|
|
615
|
+
description: How one repository depends on another's code (M-ADR 0217 §4.1).
|
|
609
616
|
catalogues:
|
|
610
617
|
domains:
|
|
611
618
|
primary_key: id
|
|
@@ -688,6 +695,10 @@ catalogues:
|
|
|
688
695
|
type: string
|
|
689
696
|
nullable: true
|
|
690
697
|
description: "`true` when this SDD is a reference exemplar — it demonstrates a profile shape rather than modelling a real product or provider relationship. Empty otherwise, which is the unmarked norm (absent in the manifest = a real SDD, no finding). From the manifest's `exemplar:` field (sdd-manifest-schema 1.8.0). PROFILE-AGNOSTIC, unlike surface/packaging/application_archetype which are application-product-only — the first exemplar cohort found was integration-products. DERIVED, not authorable: this column and `integrations.exemplar` both come from the one manifest field, so they cannot disagree; two independently authored copies could, which is the failure mode the trait exists to remove. No builder branches on it — it is emitted to be filterable and nothing consumes it, because an implicit exclusion inside a rollup is much harder to remove than to add. Added in 2.46.0."
|
|
698
|
+
- name: shared_library
|
|
699
|
+
type: string
|
|
700
|
+
nullable: true
|
|
701
|
+
description: "`true` when this SDD exists to provide code other SDDs build from or generate from (M-ADR 0217 §4.4). Empty otherwise, the unmarked norm. From the SDD manifest's `shared_library:` field. PROFILE-AGNOSTIC, like `exemplar`. DERIVED, not authorable: this column and its twin on the other catalogue come from the one manifest field, so they cannot disagree. No builder branches on it; a rollup filters on it. Added in 3.20.0."
|
|
691
702
|
- name: surface
|
|
692
703
|
type: string
|
|
693
704
|
nullable: true
|
|
@@ -778,6 +789,10 @@ catalogues:
|
|
|
778
789
|
type: string
|
|
779
790
|
nullable: true
|
|
780
791
|
description: "`true` when this SDD is a reference exemplar — it demonstrates a profile shape rather than modelling a real product or provider relationship. Empty otherwise, which is the unmarked norm (absent in the manifest = a real SDD, no finding). From the manifest's `exemplar:` field (sdd-manifest-schema 1.8.0). PROFILE-AGNOSTIC, unlike surface/packaging/application_archetype which are application-product-only — the first exemplar cohort found was integration-products. DERIVED, not authorable: this column and `integrations.exemplar` both come from the one manifest field, so they cannot disagree; two independently authored copies could, which is the failure mode the trait exists to remove. No builder branches on it — it is emitted to be filterable and nothing consumes it, because an implicit exclusion inside a rollup is much harder to remove than to add. Added in 2.46.0."
|
|
792
|
+
- name: shared_library
|
|
793
|
+
type: string
|
|
794
|
+
nullable: true
|
|
795
|
+
description: "`true` when this SDD exists to provide code other SDDs build from or generate from (M-ADR 0217 §4.4). Empty otherwise, the unmarked norm. From the SDD manifest's `shared_library:` field. PROFILE-AGNOSTIC, like `exemplar`. DERIVED, not authorable: this column and its twin on the other catalogue come from the one manifest field, so they cannot disagree. No builder branches on it; a rollup filters on it. Added in 3.20.0."
|
|
781
796
|
- name: surface
|
|
782
797
|
type: string
|
|
783
798
|
nullable: true
|
|
@@ -871,6 +886,10 @@ catalogues:
|
|
|
871
886
|
type: string
|
|
872
887
|
nullable: true
|
|
873
888
|
description: "`true` when the emitting SDD is a reference exemplar (demonstrates a profile shape rather than modelling a real provider relationship); empty otherwise. Derived from the SDD manifest's `exemplar:` field (sdd-manifest-schema 1.8.0), same source as `products.exemplar` — neither column is independently authorable. Carried on `sub_integrations` rows too, INHERITED from the emitting SDD. That deliberately differs from `ingestion_pattern`/`invocation_pattern`, which are empty on sub rows, and the difference is the point: a pattern is a MECHANISM (how this integration acquires data), genuinely one per SDD, whereas `exemplar` is a REALITY STATUS that propagates to everything the SDD emits — if the SDD is a demonstration then every provider relationship it declares is a demonstration. Leaving sub rows empty would flag the umbrella and count its demonstration sub-providers as real, reproducing the rollup overcount this column exists to fix one level down. Do not normalise it to match the pattern columns. The trade accepted: a real SDD with one demonstration sub-provider is unexpressible — a state that should not exist. Added in 2.46.0."
|
|
889
|
+
- name: shared_library
|
|
890
|
+
type: string
|
|
891
|
+
nullable: true
|
|
892
|
+
description: "`true` when this SDD exists to provide code other SDDs build from or generate from (M-ADR 0217 §4.4). Empty otherwise, the unmarked norm. From the SDD manifest's `shared_library:` field. PROFILE-AGNOSTIC, like `exemplar`. DERIVED, not authorable: this column and its twin on the other catalogue come from the one manifest field, so they cannot disagree. No builder branches on it; a rollup filters on it. Added in 3.20.0."
|
|
874
893
|
- name: source_sdd_id
|
|
875
894
|
type: string
|
|
876
895
|
nullable: true
|
|
@@ -4050,3 +4069,49 @@ catalogues:
|
|
|
4050
4069
|
references: repositories.id
|
|
4051
4070
|
nullable: false
|
|
4052
4071
|
external: true
|
|
4072
|
+
repo_dependency:
|
|
4073
|
+
primary_key: id
|
|
4074
|
+
columns:
|
|
4075
|
+
- name: id
|
|
4076
|
+
type: string
|
|
4077
|
+
nullable: false
|
|
4078
|
+
description: <from_repo_id>__<to_repo_id>__<kind>.
|
|
4079
|
+
- name: from_repo_id
|
|
4080
|
+
type: string
|
|
4081
|
+
nullable: false
|
|
4082
|
+
description: The dependent repository; one of this SDD's own.
|
|
4083
|
+
- name: to_repo_id
|
|
4084
|
+
type: string
|
|
4085
|
+
nullable: false
|
|
4086
|
+
description: The repository depended on, in this SDD or another. External at build; it must resolve at aggregation, and is never renamed or guessed to make it resolve.
|
|
4087
|
+
- name: kind
|
|
4088
|
+
type: string
|
|
4089
|
+
nullable: false
|
|
4090
|
+
description: Bound to repo_dependency_kind.
|
|
4091
|
+
- name: version
|
|
4092
|
+
type: string
|
|
4093
|
+
nullable: true
|
|
4094
|
+
description: The version or reference depended on, where the source states one; empty otherwise. The declared source wins over the derived one.
|
|
4095
|
+
- name: declared_in
|
|
4096
|
+
type: string
|
|
4097
|
+
nullable: false
|
|
4098
|
+
description: The file the row came from, relative to the SDD root; the declared source when both name the dependency.
|
|
4099
|
+
- name: source_sdd_id
|
|
4100
|
+
type: string
|
|
4101
|
+
nullable: true
|
|
4102
|
+
description: Originating SDD slug. Populated by the aggregator per M-ADR 0134.
|
|
4103
|
+
- name: platform_sdd_id
|
|
4104
|
+
type: string
|
|
4105
|
+
nullable: true
|
|
4106
|
+
description: The platform this row belongs to — sdd_id of its platform-root. Stamped by the aggregator (M-ADR 0208 §4.4).
|
|
4107
|
+
description: "Edge: a repository depends on another repository's code without either being a running service — a library linked at build time, or a template family it was generated from (M-ADR 0217). The depended-on repository may belong to another SDD, which describes it; this row only references it."
|
|
4108
|
+
foreign_keys:
|
|
4109
|
+
- column: from_repo_id
|
|
4110
|
+
references: repositories.id
|
|
4111
|
+
nullable: false
|
|
4112
|
+
- column: to_repo_id
|
|
4113
|
+
references: repositories.id
|
|
4114
|
+
nullable: false
|
|
4115
|
+
external: true
|
|
4116
|
+
column_enums:
|
|
4117
|
+
kind: enums.repo_dependency_kind
|
|
@@ -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
|
|
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 {
|
|
9
|
-
export const CLI_VERSION =
|
|
8
|
+
import { getSddCliVersion } from "../package-version.js";
|
|
9
|
+
export const CLI_VERSION = getSddCliVersion();
|
|
10
10
|
//# sourceMappingURL=cli-version.js.map
|
|
@@ -215,6 +215,9 @@ const ManifestSchema = z.object({
|
|
|
215
215
|
// application-product. Absent reads as a real SDD, the unmarked norm, with no
|
|
216
216
|
// finding. Only `true` is meaningful; `false` is redundant but accepted.
|
|
217
217
|
exemplar: z.boolean().optional(),
|
|
218
|
+
// Shared-library trait (sdd-manifest-schema 1.12.0, M-ADR 0217 §4.4). Profile-agnostic,
|
|
219
|
+
// like `exemplar`; absent is the unmarked norm, no finding.
|
|
220
|
+
shared_library: z.boolean().optional(),
|
|
218
221
|
// Application-product archetype trait (M-ADR 0191, sdd-manifest-schema
|
|
219
222
|
// 1.2.0). RESOLVED enum, not fixed: the loader validates only the slug
|
|
220
223
|
// shape here. Whether the value resolves against the active vocabulary
|
|
@@ -374,6 +377,7 @@ export async function loadSddManifest(sddPath, options) {
|
|
|
374
377
|
...(parsed.data.surface !== undefined ? { surface: parsed.data.surface } : {}),
|
|
375
378
|
...(parsed.data.packaging !== undefined ? { packaging: parsed.data.packaging } : {}),
|
|
376
379
|
...(parsed.data.exemplar !== undefined ? { exemplar: parsed.data.exemplar } : {}),
|
|
380
|
+
...(parsed.data.shared_library !== undefined ? { sharedLibrary: parsed.data.shared_library } : {}),
|
|
377
381
|
...(parsed.data.application_archetype !== undefined
|
|
378
382
|
? { applicationArchetype: parsed.data.application_archetype }
|
|
379
383
|
: {}),
|
|
@@ -859,3 +859,13 @@ coverage:
|
|
|
859
859
|
repo_id:
|
|
860
860
|
class: xref
|
|
861
861
|
impact_flow: toward-source
|
|
862
|
+
repo_dependency:
|
|
863
|
+
kind: edge
|
|
864
|
+
scoring: predicate
|
|
865
|
+
class: dependency
|
|
866
|
+
impact_flow: toward-source
|
|
867
|
+
instrument: "FK resolution against repositories at aggregation: a dependency naming no repository fails like any other foreign key (M-ADR 0217 §4.1)"
|
|
868
|
+
repo_dependency_kind:
|
|
869
|
+
kind: enum
|
|
870
|
+
scoring: not-applicable
|
|
871
|
+
reason: closed vocabulary, not an artefact
|
package/dist/lib/lock-file.js
CHANGED
|
@@ -62,6 +62,9 @@ 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.ciWorkflowBlockSha256 !== undefined ? [`ci_workflow_block_sha256: "${lock.ciWorkflowBlockSha256}"`] : []),
|
|
67
|
+
...(lock.ciModuleSha256 !== undefined ? [`ci_module_sha256: "${lock.ciModuleSha256}"`] : []),
|
|
65
68
|
"bundles:",
|
|
66
69
|
];
|
|
67
70
|
for (const b of lock.bundles) {
|
|
@@ -109,6 +112,9 @@ function stripQuotes(value) {
|
|
|
109
112
|
function parseVersioned(raw) {
|
|
110
113
|
let methodRepo;
|
|
111
114
|
let lockVersion;
|
|
115
|
+
let ciWorkflowBlockSha256;
|
|
116
|
+
let ciBinding;
|
|
117
|
+
let ciModuleSha256;
|
|
112
118
|
const bundles = [];
|
|
113
119
|
let inBundles = false;
|
|
114
120
|
let inPaths = false;
|
|
@@ -170,6 +176,12 @@ function parseVersioned(raw) {
|
|
|
170
176
|
methodRepo = stripQuotes(value);
|
|
171
177
|
else if (key === "lock_version")
|
|
172
178
|
lockVersion = Number(stripQuotes(value));
|
|
179
|
+
else if (key === "ci_workflow_block_sha256")
|
|
180
|
+
ciWorkflowBlockSha256 = stripQuotes(value);
|
|
181
|
+
else if (key === "ci_binding")
|
|
182
|
+
ciBinding = stripQuotes(value);
|
|
183
|
+
else if (key === "ci_module_sha256")
|
|
184
|
+
ciModuleSha256 = stripQuotes(value);
|
|
173
185
|
}
|
|
174
186
|
continue;
|
|
175
187
|
}
|
|
@@ -289,7 +301,14 @@ function parseVersioned(raw) {
|
|
|
289
301
|
? ` — the lock was written by a newer sdd-cli; upgrade rather than downgrading the lock.`
|
|
290
302
|
: `.`));
|
|
291
303
|
}
|
|
292
|
-
return {
|
|
304
|
+
return {
|
|
305
|
+
methodRepo,
|
|
306
|
+
lockVersion: LOCK_VERSION,
|
|
307
|
+
bundles,
|
|
308
|
+
...(ciBinding !== undefined ? { ciBinding } : {}),
|
|
309
|
+
...(ciWorkflowBlockSha256 !== undefined ? { ciWorkflowBlockSha256 } : {}),
|
|
310
|
+
...(ciModuleSha256 !== undefined ? { ciModuleSha256 } : {}),
|
|
311
|
+
};
|
|
293
312
|
}
|
|
294
313
|
function migrateV1(raw, legacyBundleName) {
|
|
295
314
|
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 {
|
|
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 ${
|
|
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
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { dirname, resolve, sep } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
/**
|
|
5
5
|
* Read the package version from package.json at runtime so the various
|
|
@@ -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;
|
|
@@ -22,4 +41,52 @@ export function getPackageVersion() {
|
|
|
22
41
|
cached = pkg.version;
|
|
23
42
|
return cached;
|
|
24
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The @sdd-method/verify-cli version released WITH this sdd-cli (M-ADR 0212
|
|
46
|
+
* §6.1 Phase 1). The method-managed CI workflow installs exactly this version,
|
|
47
|
+
* so the CLI that syncs a repository decides which verifier judges it.
|
|
48
|
+
*
|
|
49
|
+
* Read from dist/verify-version.json, which the build copies from
|
|
50
|
+
* scripts/verify-version.json. Never from scripts/ in a built layout: the global
|
|
51
|
+
* `sdd-cli` on the method's machines is a link into the checkout, so reading the
|
|
52
|
+
* committed file live would render a version bumped in source but not yet
|
|
53
|
+
* published into every workflow synced that day, while dist/ still lagged.
|
|
54
|
+
* Source layout (tests) has no copy and reads scripts/ directly.
|
|
55
|
+
*
|
|
56
|
+
* Throws rather than defaults: a missing version must not render a floating install.
|
|
57
|
+
*/
|
|
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;
|
|
69
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
70
|
+
const fromSource = here.endsWith(`${sep}src${sep}lib`);
|
|
71
|
+
const file = fromSource
|
|
72
|
+
? resolve(here, "..", "..", "scripts", `${persona}-version.json`)
|
|
73
|
+
: resolve(here, "..", `${persona}-version.json`);
|
|
74
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
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`);
|
|
77
|
+
}
|
|
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");
|
|
91
|
+
}
|
|
25
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 {
|
|
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:
|
|
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
|
package/dist/verbs/validate.js
CHANGED
|
@@ -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 } : {}),
|
package/dist/verify-program.js
CHANGED
|
@@ -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}' ${
|
|
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}' ${
|
|
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
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdd-method/verify-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "1.1.0-rc.0",
|
|
4
|
+
"sddCliVersion": "1.42.0",
|
|
5
|
+
"description": "Verify an SDD repository without changing it \u2014 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",
|
|
7
8
|
"bin": {
|