@dzhechkov/harness-cli 0.6.0 → 0.6.1
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/.dz-manifest.json +29 -13
- package/README.md +13 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +243 -3
- package/dist/cli.js.map +1 -1
- package/dist/core-compat.d.ts +1 -1
- package/dist/core-compat.js +1 -1
- package/keys/README.md +44 -5
- package/keys/dz.pub +3 -0
- package/package.json +2 -2
- package/sbom.json +52 -12
- package/src/cli.ts +250 -2
- package/src/core-compat.ts +1 -1
package/.dz-manifest.json
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "CHANGELOG.md",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "b99db6be2d9e6d46f0a9d3ff0877c445efac7b9e90d5fe1db9f5bfa2cb6dc107"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "696aae63a95e8f8726eecc1aa0e68f20d7980e99bb4008b22f2e1058ca67c6c0"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "coverage/coverage-final.json",
|
|
@@ -41,19 +41,19 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "dist/cli.d.ts.map",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "8d15372b27f7d79f54df09671b01d5944e8f3f3d87075ba4ece7f0131fc1103e"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "dist/cli.js",
|
|
48
|
-
"sha256": "
|
|
48
|
+
"sha256": "be3648deb68dca8a48cc323f34f54c77c05f0ddf0d86982ea9d6f2cc8e073349"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "dist/cli.js.map",
|
|
52
|
-
"sha256": "
|
|
52
|
+
"sha256": "47bcecfe2c9f1daeb73056b3a2c1e10e6b77c465c1bb0d6169690f2276a9cc77"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"path": "dist/core-compat.d.ts",
|
|
56
|
-
"sha256": "
|
|
56
|
+
"sha256": "b735a6c18bfbe5adf17bbcfe6e2eeae71477b877e958c36e0a5e73e59f100d6a"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
"path": "dist/core-compat.d.ts.map",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
"path": "dist/core-compat.js",
|
|
64
|
-
"sha256": "
|
|
64
|
+
"sha256": "6d052e0a7352e9824bc1d4a319d03b42320f6551210f7dfd360afbdca4e556f0"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": "dist/core-compat.js.map",
|
|
@@ -85,11 +85,15 @@
|
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "keys/README.md",
|
|
88
|
-
"sha256": "
|
|
88
|
+
"sha256": "142fc5d0d59003476b8e7367d6fc21fe40ce9e0bf70159318a05abdb10502b8a"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"path": "keys/dz.pub",
|
|
92
|
+
"sha256": "353885160582f8e0cfb2a97ef9e51f77997f0120841f92a1ce50d202ebf5dd73"
|
|
89
93
|
},
|
|
90
94
|
{
|
|
91
95
|
"path": "package.json",
|
|
92
|
-
"sha256": "
|
|
96
|
+
"sha256": "a43608842dab18914486c96c1989b294031f35118bb91c1e8bd0807041a10ddd"
|
|
93
97
|
},
|
|
94
98
|
{
|
|
95
99
|
"path": "src/bin.ts",
|
|
@@ -97,11 +101,11 @@
|
|
|
97
101
|
},
|
|
98
102
|
{
|
|
99
103
|
"path": "src/cli.ts",
|
|
100
|
-
"sha256": "
|
|
104
|
+
"sha256": "d39fa1f437401419889965674889df3e5091d4ed76242e36f8e70f18683d4e4a"
|
|
101
105
|
},
|
|
102
106
|
{
|
|
103
107
|
"path": "src/core-compat.ts",
|
|
104
|
-
"sha256": "
|
|
108
|
+
"sha256": "5fef10b3d968d299eb7463157e8b164413872f7cc8bf5e9ca1c605e63569a9f7"
|
|
105
109
|
},
|
|
106
110
|
{
|
|
107
111
|
"path": "src/index.ts",
|
|
@@ -111,13 +115,17 @@
|
|
|
111
115
|
"path": "test/agents-sync.test.ts",
|
|
112
116
|
"sha256": "32606890dc57d58257e6b4a38246acb343eea88b6e4b2f643f061325a34d066a"
|
|
113
117
|
},
|
|
118
|
+
{
|
|
119
|
+
"path": "test/amendment-check-cli.test.ts",
|
|
120
|
+
"sha256": "3dd543fd3032f1fca5c063af98a7501b4cccb9d7e42f6c307074b67f2d55d231"
|
|
121
|
+
},
|
|
114
122
|
{
|
|
115
123
|
"path": "test/cli.test.ts",
|
|
116
124
|
"sha256": "d9adcce84b509e00213bda59a0948eca90d83bdd2fdbbc86aee5ce11d9176940"
|
|
117
125
|
},
|
|
118
126
|
{
|
|
119
127
|
"path": "test/command-count.test.ts",
|
|
120
|
-
"sha256": "
|
|
128
|
+
"sha256": "d9677688116c0a35591cb7e53ab1502566e44336959ac0ea449f5e8a1c5e31e3"
|
|
121
129
|
},
|
|
122
130
|
{
|
|
123
131
|
"path": "test/core-compat-guard.test.ts",
|
|
@@ -139,6 +147,10 @@
|
|
|
139
147
|
"path": "test/epoch-replay-cli.test.ts",
|
|
140
148
|
"sha256": "a5a48585b565288ca1ea96030b02b2ccb37a621ef7b3cfe54cd7aaa7164d6904"
|
|
141
149
|
},
|
|
150
|
+
{
|
|
151
|
+
"path": "test/feature-adr-record-cli.test.ts",
|
|
152
|
+
"sha256": "e1a2687a1ae2bc2d32cbcc852487d30f9451ce1ea4a132e188186dd4251c49d1"
|
|
153
|
+
},
|
|
142
154
|
{
|
|
143
155
|
"path": "test/fixtures/discrimination-check/fake-runner.sh",
|
|
144
156
|
"sha256": "126bbd1ba9c08ef9de826cfa21df3981720e4bc36cd993dff28e0de435cbebae"
|
|
@@ -255,6 +267,10 @@
|
|
|
255
267
|
"path": "test/mutation-registry.json",
|
|
256
268
|
"sha256": "3e6f9bb105154121988da41568ff85573099235857ba25e76c4264c166b414ae"
|
|
257
269
|
},
|
|
270
|
+
{
|
|
271
|
+
"path": "test/packaged-trust-root.test.ts",
|
|
272
|
+
"sha256": "46c0af5494ad717eb4d936489f4bde2199b40cd756ab6c8adb0cd1714a971304"
|
|
273
|
+
},
|
|
258
274
|
{
|
|
259
275
|
"path": "test/parallel-worktree-smoke.test.ts",
|
|
260
276
|
"sha256": "0522b12ccdf3db79215c4c42162ef970f4cba0c0af96208426ca0323c367d0ee"
|
|
@@ -333,5 +349,5 @@
|
|
|
333
349
|
}
|
|
334
350
|
]
|
|
335
351
|
},
|
|
336
|
-
"signature": "
|
|
352
|
+
"signature": "yL9vp8bfrRx+/nOqnZuEZbW2ccdXFsSI5KEjED+FYQZxqCfx3aQ5KD6htiBIADjoOrKOUDVq7sg6ZcxxBsMCBg=="
|
|
337
353
|
}
|
package/README.md
CHANGED
|
@@ -1763,9 +1763,9 @@ runs a command that MEASURES the declared artifacts itself. It refuses a null re
|
|
|
1763
1763
|
never recorded as done), an absent or partially-present artifact set, and a stage that declares nothing
|
|
1764
1764
|
to witness — so a stage that did not happen can no longer be recorded, which the old mechanism allowed.
|
|
1765
1765
|
|
|
1766
|
-
## All Commands (
|
|
1766
|
+
## All Commands (72)
|
|
1767
1767
|
|
|
1768
|
-
*(
|
|
1768
|
+
*(72 MEASURED — reproducer: `grep -c "^ case '" src/cli.ts`, the dispatch cases.)*
|
|
1769
1769
|
|
|
1770
1770
|
```
|
|
1771
1771
|
dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
|
|
@@ -1821,6 +1821,8 @@ dz challenge --plan <plan.md> [--json] [--context-only] [--author <model>] # a
|
|
|
1821
1821
|
dz routing [--stage <s>] [--json] # inspect the learned cost-optimal routing store: what `args.models.<stage>='auto-cost'` believes per (stage, complexity-tier, model) — gated attempts/successes/rate (feeds feature-adr model selection)
|
|
1822
1822
|
dz bto-optimize --split|--plan|--select|--scope-check|--diff [--json] # deterministic engine behind /bto-optimize: hold-out split + hard-capped budget + no-regress-on-holdout winner selection (defeats judge-gaming); prose-only, diff-confirmed, never auto-writes
|
|
1823
1823
|
dz discrimination-check --test <f[,f]> [--base <ref>] [--name <filter>] [--runner <cmd>] [--timeout <ms>] [--json] # §42 test-discrimination gate for feature-adr Step-8: run the ADR's property test in an isolated git worktree at pre-feature base — it MUST go red without the fix. SEVEN verdicts, each gated on EXECUTION evidence: DISCRIMINATES · DISCRIMINATES_VIA_ERROR · NON_DISCRIMINATING (false green) · TEST_FILE_ABSENT · LOAD_ERROR_AT_BOTH_REVS · FAILS_AT_TIP · CANNOT_ISOLATE (+ typed reason). Advisory, never auto-aborts
|
|
1824
|
+
dz amendment-check --slug <slug> | --feature-dir <dir> | --all [--json] (every AM-N amendment row must resolve to a test found INSIDE the file the row names; the PLAN is authoritative when it carries rows, and an ideation amendment the plan drops is a failure; --all is a census that always exits 0; exit 0 pass/skip, 1 fail, 3 NOT-ESTABLISHED — a section that parsed ZERO rows is never a pass)
|
|
1825
|
+
dz feature-adr-record --kind ledger|training-pair --stage <s> [--slug <s>] [--row|--pair <json>] [--mark <n>] [--once] [--json] (the witnessed writer: payload as an ARGUMENT never as shell, refused before any write, timestamp stamped before serialising, append verified by re-reading the tail; exit 0 written|duplicate|skipped, 2 refused, 3 not-verified, never blocking)
|
|
1824
1826
|
dz mutation-gate [--package <dir>] [--registry <file>] [--test-cmd "<cmd>"] [--only <id[,id]>] [--timeout <ms>] [--rebaseline per-entry|final] [--keep-scratch] [--json] # the mutation gate: for each NAMED protection in a declarative registry, copy the package to a scratch dir (shadow-repo layout, node_modules symlinked, git-initialized), verify the baseline is green, apply the entry's exact {find, replace} mutation, run the suite, REQUIRE red, restore — and require the red to be ATTRIBUTABLE to the protection: a mutated file that no longer parses is MUTATION_UNPARSEABLE, a failing count far above the entry's bound (maxFailing, default from observed) is OVER_FAILING, and a restored tree that does not reproduce green makes the entry INCONCLUSIVE (flaky suite). A mutation that does not apply, a green suite, or an inconclusive run is a FAILURE — never a skip. exit 0 all proven / 1 gate failed / 2 setup error
|
|
1825
1827
|
dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict] [--author <model>] [--json] # portable Step-10 Delivery Gate: the `manual` form that travels to every shell target — prints the 4-plane review brief (regressions ‖ security ‖ code-quality ‖ product-honesty) + artifact probes; --findings classifies a fed-back review into a fail-closed ready|blocked hand-off (only cross-validated BLOCKER/HIGH count) and writes features/<slug>/10_delivery_review.md; --strict exits 1 on blocked
|
|
1826
1828
|
dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json] # does .claude/skills/ actually REGISTER? --static = instant layout scan (CI-safe, no session): flags dirs that can never register; default also starts a real session and reads the authoritative system/init listing. exit 0 pass / 1 fail / 2 inconclusive — an unobservable registration is NEVER a pass
|
|
@@ -4021,7 +4023,15 @@ npx @dzhechkov/p-replicator init
|
|
|
4021
4023
|
|
|
4022
4024
|
## Status
|
|
4023
4025
|
|
|
4024
|
-
`v0.6.
|
|
4026
|
+
`v0.6.1` — two commands that move checks off the model's judgement and onto the disk.
|
|
4027
|
+
`dz amendment-check` resolves every `AM-N` amendment row to a test found INSIDE the file the row names
|
|
4028
|
+
(the plan is authoritative; an amendment it drops or rewords under the same id is a failure; `--all` is
|
|
4029
|
+
a census that never blocks). `dz feature-adr-record` is the witnessed writer for the run-cost ledger
|
|
4030
|
+
and training pairs: the payload arrives as an ARGUMENT rather than baked into a shell pipeline, a
|
|
4031
|
+
malformed or wrong-kind payload is refused before any write, the timestamp is stamped before
|
|
4032
|
+
serialising, and the append is verified by re-reading the tail. `MIN_CORE` moves to `0.6.1`.
|
|
4033
|
+
|
|
4034
|
+
**v0.6.0** — adds `dz feature-adr-checkpoint`, which writes the pipeline's durable checkpoint line as a
|
|
4025
4035
|
COMMAND instead of leaving a subagent to hand-write state into a file: a security classifier blocked
|
|
4026
4036
|
NINE consecutive hand-written checkpoint writes in one run, `.fa-state/checkpoints.jsonl` was never
|
|
4027
4037
|
created, resume was silently dead, and the run still reported success. The command measures every
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAA0B,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKtF,OAAO,EA2CL,iBAAiB,EAmBjB,KAAK,oBAAoB,EA6SzB,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ readBacklogConfig, readIdeas, writeIdeas, ideaId, dedupIdea, readGoalMap, readGo
|
|
|
22
22
|
// qe-bridge (feature qe-bridge-claude, ADR-001): the pure half of the reverse QE bridge.
|
|
23
23
|
KNOWN_CLAUDE, isSafeClaudeId, claudeProbeArgs, claudeReviewArgs, interpretClaudeProbe, modelFamily, buildBridgePrompt, parseBridgeOutput, buildBridgeFailureRecord, buildBridgeSignoffRecord, renderBridgeReport, isSafeSlug, hasUnsafePathChars, hasDotDotSegment, buildReqeBrief, settleReqeDebt, renderReqeList, REQE_SCOPE,
|
|
24
24
|
// Mutation gate (feature ha-mutation-gate) — break each named protection, run the suite, require red.
|
|
25
|
-
parseMutationRegistry, applyMutationToText, countFailingTests, classifyBaseline, classifyRunFailure, classifyMutationOutcome, mutationGateExitCode, summarizeMutationResults, renderMutationReport, TRACE_BUNDLE_LEDGER_PATH, TRACE_BUNDLE_SCHEMA, TRACE_BUNDLE_RUN_META_FILE, buildBundle, serializeBundle, parseBundle, planImport, decideCheckpointWrite, } from '@dzhechkov/harness-core';
|
|
25
|
+
parseMutationRegistry, applyMutationToText, countFailingTests, classifyBaseline, classifyRunFailure, classifyMutationOutcome, mutationGateExitCode, summarizeMutationResults, renderMutationReport, TRACE_BUNDLE_LEDGER_PATH, TRACE_BUNDLE_SCHEMA, TRACE_BUNDLE_RUN_META_FILE, buildBundle, serializeBundle, parseBundle, planImport, decideCheckpointWrite, amendmentSection, planSaysNoAmendments, parseAmendments, resolveAmendments, decideAmendmentOutcome, amendmentVerdictLine, amendmentsMissingFromPlan, AMENDMENT_VACUITY_NOTE, decideRecordWrite, decideReadBack, recordVerdictLine, } from '@dzhechkov/harness-core';
|
|
26
26
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
27
27
|
import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
|
|
28
28
|
const USAGE = `dz - DZ cross-platform harness CLI
|
|
@@ -62,6 +62,8 @@ Usage:
|
|
|
62
62
|
dz epoch-replay --judge <filled-work-order.json> [--out <file>] (blind judge prompts from the filled plans)
|
|
63
63
|
dz epoch-replay --score <judgments.json> --work-order <file> [--slice <name>] [--json] (un-blind against the pre-registered assignment → SUPPORTED only when the two 95% Wilson CIs are DISJOINT, else FALSIFIED / INCONCLUSIVE)
|
|
64
64
|
dz score --slug <feature> [--project <dir>] [--json] (process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination, cross-model QE grade, live verification, README-first, learning loop, amendments — descriptive-only, a low score exits 0)
|
|
65
|
+
dz amendment-check --slug <slug> | --feature-dir <dir> | --all [--json] (the deterministic Step-8 amendment gate: every AM-N row must resolve to a test found INSIDE the file the row names; the PLAN is authoritative when it carries rows, and an ideation amendment the plan drops is a failure. exit 0 pass/skip, 1 fail, 3 NOT-ESTABLISHED — a section that parsed ZERO rows is never a pass. --all is a CENSUS and always exits 0. Does NOT prove non-vacuity — that is dz discrimination-check)
|
|
66
|
+
dz feature-adr-record --kind ledger|training-pair --stage <s> [--slug <s>] [--row|--pair <json>] [--mark <n>] [--once] [--json] (the witnessed writer for the run-cost ledger and training pairs: the payload arrives as an ARGUMENT, never as shell; a malformed or wrong-kind payload is REFUSED before any write; the timestamp is stamped before serialising; the append is verified by re-reading the tail. exit 0 written|duplicate|skipped, 2 refused, 3 not-verified — a record failure is never blocking)
|
|
65
67
|
dz feature-adr-checkpoint (--slug <feature> | --feature-dir <abs>) --stage <s> --input-hash <h> --result <json> [--artifact a,b] [--json] (record a pipeline stage ONLY after measuring its artifacts on disk; refuses a null result, an absent artifact, or a stage that declares none — the subagent runs a COMMAND instead of hand-writing durable state)
|
|
66
68
|
dz reqe [--slug <feature> [--done --report <f>]] [--json] (the re-QE debt ledger: a usage-switched run whose Step-8 QE ran on the coder's OWN family records a debt; list debts, print the cross-family review brief, settle FAIL-CLOSED against a graded report — the settlement lands in 08_qe_report.md)
|
|
67
69
|
dz qe-bridge --family claude --slug <feature> [--coder-family codex|claude] [--model <id>] [--files a,b] [--out <f>] [--timeout <s>] [--allow-same-family] [--json] (the REVERSE QE bridge: run an INDEPENDENT Claude reviewer over a feature's Step-8 artifacts from ANY host — a Codex session included, plain shell, no Claude agent plane needed — and land a PARSED signoff. The reviewer runs ISOLATED: an EMPTY temp cwd plus --safe-mode --strict-mcp-config --tools '' --no-session-persistence, so no CLAUDE.md/skills/plugins/hooks/MCP load, and the verdict is read from the --output-format json RESULT ENVELOPE — text a session customization printed onto the same stdout can never become a signoff. Probes the model before trusting it; sends SCOPED extracts with a loud 200k-char ceiling (never silent truncation); the grade must AGREE across three LAST-anchored channels (terminal marker line, fenced qe-bridge-signoff JSON, the report's own GRADE line) AND the marker must be the FINAL content — empty, gradeless, self-contradicting or miscounted output is one of 18 NAMED failures with an audit record under features/<slug>/.fa-state/qe-bridge/ (runId, resolved executable + binOverride, prompt sha256, channel offsets, requestedOut, reportWritten, retained raw stdout; 0600 files in a 0700 dir), never a clean review. A --coder-family that contradicts the recorded reqe debt is refused. Writes features/<slug>/08b_reqe_report.md, which dz reqe --done settles unchanged. DISCLOSURE: the extracts you scope are sent to the Claude runtime; the bridge cannot classify secrets. DZ_QE_BRIDGE_CLAUDE_BIN is a TEST SEAM, not a flag. exit 0 signoff parsed (ANY grade — it reports, it does not gate) / 1 named failure / 2 usage)
|
|
@@ -4308,7 +4310,7 @@ function cmdPublish(options, flags, cwd, write) {
|
|
|
4308
4310
|
// Reject unknown flags/options so a typo (e.g. `--dry-rum`) can NEVER be
|
|
4309
4311
|
// silently swallowed and flip the command into live-publish mode.
|
|
4310
4312
|
const allowedFlags = new Set(['dry-run', 'no-dry-run', 'yes', 'confirm', 'bump-only', 'help', 'require-signing', 'provenance', 'no-provenance']);
|
|
4311
|
-
const allowedOptions = new Set(['filter', 'claim-check', 'no-guard']);
|
|
4313
|
+
const allowedOptions = new Set(['filter', 'claim-check', 'no-guard', 'sign-key']);
|
|
4312
4314
|
const allowedHelp = ' allowed: --dry-run (default), --yes/--confirm/--no-dry-run (go live), --bump-only, --filter <substr>, --claim-check <off|warn|error>, --no-guard "<reason>" (skip the guard pre-flight; logged)';
|
|
4313
4315
|
for (const flag of flags) {
|
|
4314
4316
|
if (!allowedFlags.has(flag)) {
|
|
@@ -4459,7 +4461,30 @@ function cmdPublish(options, flags, cwd, write) {
|
|
|
4459
4461
|
return 1;
|
|
4460
4462
|
}
|
|
4461
4463
|
}
|
|
4462
|
-
|
|
4464
|
+
// A signed pack must be RE-SIGNED after publish's own bump and README sync (feature
|
|
4465
|
+
// `sign-after-bump`): publish mutates the pack, so any earlier signature describes files that no
|
|
4466
|
+
// longer exist. Default to the same path `dz sign --init` writes, so the ordinary operator needs no
|
|
4467
|
+
// new flag; `--sign-key` overrides it.
|
|
4468
|
+
const signKey = (options.get('sign-key') ?? join(homedir(), '.dz', 'keys', 'dz.key')).trim();
|
|
4469
|
+
const report = publishPackages(cwd, {
|
|
4470
|
+
provenance,
|
|
4471
|
+
dryRun,
|
|
4472
|
+
filter,
|
|
4473
|
+
bumpOnly,
|
|
4474
|
+
claimGate: claimCheckOpt,
|
|
4475
|
+
signKey: signKey === '' ? undefined : resolve(cwd, signKey),
|
|
4476
|
+
reSign: (packDir, keyPath) => {
|
|
4477
|
+
// The same three steps `dz sign` performs, including the SBOM — a manifest refreshed without
|
|
4478
|
+
// its SBOM would leave the two describing different trees.
|
|
4479
|
+
const files = packFiles(packDir);
|
|
4480
|
+
if (files.length === 0)
|
|
4481
|
+
throw new Error(`refusing to sign an empty pack: ${packDir}`);
|
|
4482
|
+
const manifest = buildManifest(packDir, basename(packDir), files);
|
|
4483
|
+
const signed = signManifest(manifest, readFileSync(keyPath, 'utf-8'));
|
|
4484
|
+
writeFileSync(join(packDir, MANIFEST_NAME), `${JSON.stringify(signed, null, 2)}\n`);
|
|
4485
|
+
writeFileSync(join(packDir, SBOM_NAME), `${JSON.stringify(buildSbom(manifest), null, 2)}\n`);
|
|
4486
|
+
},
|
|
4487
|
+
});
|
|
4463
4488
|
write(`\ndz publish${dryRun ? ' --dry-run' : ''}${bumpOnly ? ' --bump-only' : ''}${claimCheckOpt !== 'warn' ? ` --claim-check ${claimCheckOpt}` : ''}`);
|
|
4464
4489
|
write(` Published: ${report.published} Skipped: ${report.skipped} Errors: ${report.errors}\n`);
|
|
4465
4490
|
for (const pkg of report.packages) {
|
|
@@ -8634,6 +8659,217 @@ function scanOneReqeRoot(featuresDir, keyMatchesDir, out, addMalformed) {
|
|
|
8634
8659
|
* It measures `--artifact` paths on disk itself. A caller cannot assert presence; it can only name
|
|
8635
8660
|
* what must be there.
|
|
8636
8661
|
*/
|
|
8662
|
+
/**
|
|
8663
|
+
* `dz amendment-check` — the deterministic half of the Step-8 amendment gate (ADR-001).
|
|
8664
|
+
*
|
|
8665
|
+
* The gate used to be prompt text asking the QE agent to confirm every `AM-N` row names a real test.
|
|
8666
|
+
* That is layer 4 on the cost-of-detection ladder, and `features/qe-scoped-review` shipped with five
|
|
8667
|
+
* dangling ids and a plan recording `## Amendments: None`. This command owns I/O and the exit code;
|
|
8668
|
+
* `harness-core/src/amendment-trace.ts` owns the grammar and the rules.
|
|
8669
|
+
*/
|
|
8670
|
+
/**
|
|
8671
|
+
* `dz feature-adr-record` — the witnessed writer for the run-cost ledger and training pairs
|
|
8672
|
+
* (ADR-001 … ADR-003). The subagent stops being a COURIER handed a shell pipeline and becomes a
|
|
8673
|
+
* CALLER handed arguments: this command owns the paths, the refusal, the append, the READ-BACK and
|
|
8674
|
+
* the exit code. A courier can neither refuse nor verify, which is how four workflow runs finished
|
|
8675
|
+
* with no cost row at all.
|
|
8676
|
+
*/
|
|
8677
|
+
function cmdFeatureAdrRecord(options, flags, cwd, write) {
|
|
8678
|
+
const json = flags.has('json');
|
|
8679
|
+
const kind = (options.get('kind') ?? '').trim();
|
|
8680
|
+
const stage = (options.get('stage') ?? '').trim();
|
|
8681
|
+
const slug = (options.get('slug') ?? '').trim();
|
|
8682
|
+
const payloadRaw = options.get('row') ?? options.get('pair') ?? '';
|
|
8683
|
+
const emit = (d, extra = {}) => {
|
|
8684
|
+
if (json) {
|
|
8685
|
+
write(JSON.stringify({ ok: d.exit === 0, kind, stage, verdict: d.verdict, exit: d.exit, reason: d.reason, blocking: d.blocking, ...extra }));
|
|
8686
|
+
}
|
|
8687
|
+
else {
|
|
8688
|
+
write(recordVerdictLine(kind === 'ledger' || kind === 'training-pair' ? kind : 'ledger', stage, d));
|
|
8689
|
+
}
|
|
8690
|
+
return d.exit;
|
|
8691
|
+
};
|
|
8692
|
+
if (kind !== 'ledger' && kind !== 'training-pair') {
|
|
8693
|
+
write('dz feature-adr-record: --kind must be ledger or training-pair');
|
|
8694
|
+
return 2;
|
|
8695
|
+
}
|
|
8696
|
+
if (payloadRaw === '') {
|
|
8697
|
+
write('dz feature-adr-record: --row (ledger) or --pair (training-pair) is required');
|
|
8698
|
+
return 2;
|
|
8699
|
+
}
|
|
8700
|
+
const repo = (options.get('project') ?? cwd).trim() || cwd;
|
|
8701
|
+
const target = kind === 'ledger'
|
|
8702
|
+
? join(repo, '.dz', 'feature-adr', 'run-cost-ledger.jsonl')
|
|
8703
|
+
: join(repo, '.dz', 'fa-training', slug === '' ? 'unknown' : slug, `${stage.replace(/[^\w.-]/g, '_')}.jsonl`);
|
|
8704
|
+
const markDir = join(repo, '.dz', 'fa-training', '.backfill-marks');
|
|
8705
|
+
const markName = (options.get('mark') ?? '').trim();
|
|
8706
|
+
const markPath = markName === '' ? null : join(markDir, markName.replace(/[^\w.-]/g, '_'));
|
|
8707
|
+
const decision = decideRecordWrite({
|
|
8708
|
+
kind,
|
|
8709
|
+
payloadRaw,
|
|
8710
|
+
stage,
|
|
8711
|
+
stageProducedResult: flags.has('no-result') ? false : true,
|
|
8712
|
+
markExists: markPath !== null && existsSync(markPath),
|
|
8713
|
+
targetExists: existsSync(target),
|
|
8714
|
+
targetHasPair: flags.has('once') && existsSync(target),
|
|
8715
|
+
timestamp: new Date().toISOString(),
|
|
8716
|
+
});
|
|
8717
|
+
if (decision.line === null)
|
|
8718
|
+
return emit(decision);
|
|
8719
|
+
// MEASURE, never assume: an unwritable target is a LOUD refusal, because a swallowed mkdir failure
|
|
8720
|
+
// is exactly how a write becomes a silent no-op (acid case A4).
|
|
8721
|
+
try {
|
|
8722
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
8723
|
+
if (markPath !== null) {
|
|
8724
|
+
mkdirSync(markDir, { recursive: true });
|
|
8725
|
+
mkdirSync(markPath);
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8728
|
+
catch (err) {
|
|
8729
|
+
const code = err.code;
|
|
8730
|
+
if (markPath !== null && code === 'EEXIST') {
|
|
8731
|
+
// A stale mark (target absent) was already decided as writable above; re-taking it is a no-op,
|
|
8732
|
+
// not a duplicate. Only a mark WITH its target means another run got here first.
|
|
8733
|
+
if (decision.staleMark !== true) {
|
|
8734
|
+
return emit({ verdict: 'duplicate', exit: 0, reason: 'a mark for this record already exists — another run captured it first', blocking: false, line: null });
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
return emit({ verdict: 'refused', exit: 2, reason: `the target could not be prepared: ${err.message}`, blocking: false, line: null });
|
|
8738
|
+
}
|
|
8739
|
+
try {
|
|
8740
|
+
appendFileSync(target, `${decision.line}\n`, 'utf-8');
|
|
8741
|
+
}
|
|
8742
|
+
catch (err) {
|
|
8743
|
+
return emit({ verdict: 'refused', exit: 2, reason: `the append failed: ${err.message}`, blocking: false, line: null });
|
|
8744
|
+
}
|
|
8745
|
+
// ADR-002: the write is verified against the disk, never inferred from the absence of an error.
|
|
8746
|
+
let lastLine = null;
|
|
8747
|
+
try {
|
|
8748
|
+
const body = readFileSync(target, 'utf-8');
|
|
8749
|
+
const lines = body.split('\n').filter((l) => l !== '');
|
|
8750
|
+
lastLine = lines.length > 0 ? lines[lines.length - 1] : null;
|
|
8751
|
+
}
|
|
8752
|
+
catch {
|
|
8753
|
+
lastLine = null;
|
|
8754
|
+
}
|
|
8755
|
+
return emit(decideReadBack(decision.line, lastLine), { target });
|
|
8756
|
+
}
|
|
8757
|
+
function cmdAmendmentCheck(options, flags, cwd, write) {
|
|
8758
|
+
const json = flags.has('json');
|
|
8759
|
+
const readOr = (abs) => {
|
|
8760
|
+
try {
|
|
8761
|
+
return readFileSync(abs, 'utf-8');
|
|
8762
|
+
}
|
|
8763
|
+
catch {
|
|
8764
|
+
return null;
|
|
8765
|
+
}
|
|
8766
|
+
};
|
|
8767
|
+
const checkOne = (featureDir) => {
|
|
8768
|
+
const slug = basename(featureDir);
|
|
8769
|
+
const ideation = readOr(join(featureDir, '03.5_ideation_report.md'));
|
|
8770
|
+
const plan = readOr(join(featureDir, '06_implementation_plan.md'));
|
|
8771
|
+
if (ideation === null) {
|
|
8772
|
+
return {
|
|
8773
|
+
slug,
|
|
8774
|
+
resolutions: [],
|
|
8775
|
+
decision: decideAmendmentOutcome({ sectionPresent: false, rows: [], resolutions: [], planSaysNone: false }),
|
|
8776
|
+
};
|
|
8777
|
+
}
|
|
8778
|
+
const sectionPresent = amendmentSection(ideation) !== null;
|
|
8779
|
+
const ideationRows = parseAmendments(ideation);
|
|
8780
|
+
const planRows = plan === null ? [] : parseAmendments(plan);
|
|
8781
|
+
// The PLAN is authoritative when it carries rows: Step 6 owes "carry AM-N into the plan
|
|
8782
|
+
// verbatim", and the ideation report is the historical record — rewriting its rows to match
|
|
8783
|
+
// tests named later would close the trail by falsifying it. Coverage keeps that honest below.
|
|
8784
|
+
const rows = planRows.length > 0 ? planRows : ideationRows;
|
|
8785
|
+
const missingFromPlan = planRows.length > 0 ? amendmentsMissingFromPlan(ideationRows, planRows) : [];
|
|
8786
|
+
// Paths in an amendment row are repo-relative, so they resolve against the repo root — not
|
|
8787
|
+
// against the feature directory, and not against wherever the caller happened to stand.
|
|
8788
|
+
const resolutions = resolveAmendments(rows, { readFile: (rel) => readOr(resolve(cwd, rel)) });
|
|
8789
|
+
const decision = decideAmendmentOutcome({
|
|
8790
|
+
sectionPresent,
|
|
8791
|
+
rows,
|
|
8792
|
+
resolutions,
|
|
8793
|
+
planSaysNone: plan !== null && planSaysNoAmendments(plan),
|
|
8794
|
+
missingFromPlan,
|
|
8795
|
+
});
|
|
8796
|
+
return { slug, decision, resolutions };
|
|
8797
|
+
};
|
|
8798
|
+
// --all is a CENSUS, not a gate (ADR-003): a gate that is red on arrival over 21 historical
|
|
8799
|
+
// features gets disabled, and then the mechanism is gone along with the debt it was to surface.
|
|
8800
|
+
if (flags.has('all')) {
|
|
8801
|
+
const featuresDir = join(cwd, 'features');
|
|
8802
|
+
let slugs = [];
|
|
8803
|
+
try {
|
|
8804
|
+
slugs = readdirSync(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
8805
|
+
}
|
|
8806
|
+
catch {
|
|
8807
|
+
if (json)
|
|
8808
|
+
write(JSON.stringify({ ok: true, mode: 'all', features: [], note: 'no features/ directory' }));
|
|
8809
|
+
else
|
|
8810
|
+
write('amendment traceability census: no features/ directory');
|
|
8811
|
+
return 0;
|
|
8812
|
+
}
|
|
8813
|
+
const rowsOut = [];
|
|
8814
|
+
for (const slug of slugs) {
|
|
8815
|
+
try {
|
|
8816
|
+
const r = checkOne(join(featuresDir, slug));
|
|
8817
|
+
rowsOut.push({ slug, outcome: r.decision.outcome, counts: r.decision.counts, reasons: r.decision.reasons.length });
|
|
8818
|
+
}
|
|
8819
|
+
catch (err) {
|
|
8820
|
+
// AM-6: one unreadable feature becomes its own row. Dropping it silently would make the
|
|
8821
|
+
// census read like coverage it does not have.
|
|
8822
|
+
rowsOut.push({ slug, outcome: 'read-error', error: err.message });
|
|
8823
|
+
}
|
|
8824
|
+
}
|
|
8825
|
+
if (json) {
|
|
8826
|
+
write(JSON.stringify({ ok: true, mode: 'all', note: AMENDMENT_VACUITY_NOTE, features: rowsOut }));
|
|
8827
|
+
}
|
|
8828
|
+
else {
|
|
8829
|
+
const tally = new Map();
|
|
8830
|
+
for (const r of rowsOut)
|
|
8831
|
+
tally.set(String(r['outcome']), (tally.get(String(r['outcome'])) ?? 0) + 1);
|
|
8832
|
+
for (const r of rowsOut) {
|
|
8833
|
+
if (r['outcome'] !== 'pass' && r['outcome'] !== 'skip')
|
|
8834
|
+
write(` [${String(r['outcome'])}] ${String(r['slug'])}`);
|
|
8835
|
+
}
|
|
8836
|
+
write(`amendment traceability census over ${rowsOut.length} feature(s): ${[...tally].map(([k, v]) => `${v} ${k}`).join(', ')}`);
|
|
8837
|
+
write(AMENDMENT_VACUITY_NOTE);
|
|
8838
|
+
}
|
|
8839
|
+
return 0;
|
|
8840
|
+
}
|
|
8841
|
+
const explicitDir = (options.get('feature-dir') ?? '').trim();
|
|
8842
|
+
const slug = (options.get('slug') ?? '').trim();
|
|
8843
|
+
if (explicitDir === '' && slug === '') {
|
|
8844
|
+
write('dz amendment-check: one of --slug / --feature-dir is required (or --all for a census)');
|
|
8845
|
+
return 2;
|
|
8846
|
+
}
|
|
8847
|
+
const featureDir = explicitDir !== '' ? resolve(cwd, explicitDir) : join(cwd, 'features', slug);
|
|
8848
|
+
const { decision, resolutions } = checkOne(featureDir);
|
|
8849
|
+
if (json) {
|
|
8850
|
+
write(JSON.stringify({
|
|
8851
|
+
ok: decision.outcome === 'pass' || decision.outcome === 'skip',
|
|
8852
|
+
slug: basename(featureDir),
|
|
8853
|
+
outcome: decision.outcome,
|
|
8854
|
+
exit: decision.exit,
|
|
8855
|
+
counts: decision.counts,
|
|
8856
|
+
reasons: decision.reasons,
|
|
8857
|
+
rows: resolutions,
|
|
8858
|
+
note: AMENDMENT_VACUITY_NOTE,
|
|
8859
|
+
}));
|
|
8860
|
+
}
|
|
8861
|
+
else {
|
|
8862
|
+
for (const r of resolutions) {
|
|
8863
|
+
if (r.verdict !== 'resolved')
|
|
8864
|
+
write(` [${r.verdict}] ${r.id}${r.testId === null ? '' : ` \`${r.testId}\``} — ${r.detail}`);
|
|
8865
|
+
}
|
|
8866
|
+
write(AMENDMENT_VACUITY_NOTE);
|
|
8867
|
+
// The verdict is the LAST line, in the K2 gate's own shape, so a caller that reads the tail of
|
|
8868
|
+
// the output reads a verdict rather than a finding.
|
|
8869
|
+
write(amendmentVerdictLine(decision));
|
|
8870
|
+
}
|
|
8871
|
+
return decision.exit;
|
|
8872
|
+
}
|
|
8637
8873
|
function cmdFeatureAdrCheckpoint(options, flags, cwd, write) {
|
|
8638
8874
|
const json = flags.has('json');
|
|
8639
8875
|
const emit = (payload, human, code) => {
|
|
@@ -11513,6 +11749,10 @@ export async function runCli(argv, io = {}) {
|
|
|
11513
11749
|
return cmdEpochReplay(options, flags, cwd, write);
|
|
11514
11750
|
case 'score':
|
|
11515
11751
|
return cmdScore(options, flags, cwd, write);
|
|
11752
|
+
case 'feature-adr-record':
|
|
11753
|
+
return cmdFeatureAdrRecord(options, flags, cwd, write);
|
|
11754
|
+
case 'amendment-check':
|
|
11755
|
+
return cmdAmendmentCheck(options, flags, cwd, write);
|
|
11516
11756
|
case 'feature-adr-checkpoint':
|
|
11517
11757
|
return cmdFeatureAdrCheckpoint(options, flags, cwd, write);
|
|
11518
11758
|
case 'reqe':
|