@sdd-method/verify-cli 0.1.0 → 0.2.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/baseline-check.js +17 -2
- package/dist/lib/catalogue/canonical-schema.yaml +476 -10
- package/dist/lib/catalogue/coverage-render.js +30 -0
- package/dist/lib/catalogue/engine-validator.js +17 -0
- package/dist/lib/catalogue/graph-projection.js +17 -0
- package/dist/lib/catalogue/impact-run.js +2 -2
- package/dist/lib/catalogue/manifest-loader.js +9 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +91 -2
- package/dist/lib/catalogue/scores-coverage.js +38 -0
- package/dist/lib/catalogue/validate.js +33 -9
- package/dist/lib/validate/aggregate.js +31 -15
- package/dist/lib/validate/bundle-coherence.js +7 -2
- package/dist/lib/validate/dispatch.js +25 -0
- package/dist/lib/validate/index.js +32 -10
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/verbs/catalogue-verify.js +1 -1
- package/package.json +2 -1
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -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
|
|
@@ -57,7 +57,7 @@ export function registerCatalogueImpact(catalogue) {
|
|
|
57
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
58
|
.requiredOption("--of <node-id>", 'Node to compute impact of, in the projection\'s "<catalogue>/<id>" form (e.g. contracts/orders-api)')
|
|
59
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)")
|
|
60
|
+
.option("--catalogues-dir <dir>", "Catalogues directory relative to the SDD root (default: catalogues, or catalogues-aggregated at --tier aggregate)")
|
|
61
61
|
.option("--tier <tier>", "Tier, explicit never inferred (member | aggregate; default member)")
|
|
62
62
|
.action(async (opts) => {
|
|
63
63
|
if (opts.tier !== undefined &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdd-method/verify-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"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
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
+
"scripts",
|
|
28
29
|
"README.md",
|
|
29
30
|
"LICENSE",
|
|
30
31
|
"NOTICE"
|