@dzhechkov/skills-feature-adr 1.4.0 → 1.5.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 +8 -8
- package/CHANGELOG.md +140 -0
- package/README.md +164 -0
- package/package.json +1 -1
- package/sbom.json +7 -7
- package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +7 -0
- package/templates/.claude/skills/feature-adr/modules/08-qe.md +26 -12
- package/templates/.claude/skills/feature-adr/scripts/check-plan-completeness.mjs +146 -9
- package/templates/.claude/workflows/feature-adr.js +763 -88
package/.dz-manifest.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "CHANGELOG.md",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "88e0389016f697b90731ee8743a35e523f0ccb079426d59861d894f33e516392"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "LICENSE",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "README.md",
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "47f9402d4c3954d6d5a75ad906ec7b592010fdfd80f347d4a87dd14dd9e300ce"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "bin/cli.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": "package.json",
|
|
28
|
-
"sha256": "
|
|
28
|
+
"sha256": "3fbe9ef86c9f6fa7c86bbe7760729094e80edb67577e397678b2d98dca1c2034"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "scripts/sync-templates.js",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
"path": "templates/.claude/skills/feature-adr/modules/06-implementation-plan.md",
|
|
160
|
-
"sha256": "
|
|
160
|
+
"sha256": "3903e2694ad47782914b35869bbf19c5f0803b6dafdcfb629df343a5ff00d1b9"
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"path": "templates/.claude/skills/feature-adr/modules/07-code.md",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"path": "templates/.claude/skills/feature-adr/modules/08-qe.md",
|
|
168
|
-
"sha256": "
|
|
168
|
+
"sha256": "bd5b5809142c12fa1378de446b68027a0c342a7a91ad17e4fb3395ca8f6a8508"
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
"path": "templates/.claude/skills/feature-adr/modules/09-fleet-qe.md",
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
},
|
|
258
258
|
{
|
|
259
259
|
"path": "templates/.claude/skills/feature-adr/scripts/check-plan-completeness.mjs",
|
|
260
|
-
"sha256": "
|
|
260
|
+
"sha256": "2e1afeb8cbc08ce872fc8280a0809e7712ee76879d08b48caa5a0d4082cb8647"
|
|
261
261
|
},
|
|
262
262
|
{
|
|
263
263
|
"path": "templates/.claude/skills/frontend-design/LICENSE.txt",
|
|
@@ -321,7 +321,7 @@
|
|
|
321
321
|
},
|
|
322
322
|
{
|
|
323
323
|
"path": "templates/.claude/workflows/feature-adr.js",
|
|
324
|
-
"sha256": "
|
|
324
|
+
"sha256": "0c74e8995d82bb986ef3233e91beebfa0e761aae4434f24554077bb05d2199e9"
|
|
325
325
|
},
|
|
326
326
|
{
|
|
327
327
|
"path": "templates/lib/memory-protocol.md",
|
|
@@ -349,5 +349,5 @@
|
|
|
349
349
|
}
|
|
350
350
|
]
|
|
351
351
|
},
|
|
352
|
-
"signature": "
|
|
352
|
+
"signature": "9WVoqK2D8eKSnmhhMqHPt31NedxwZCHiHyxJ5VLMxSVMecMqyXgrCJ2GQ3OOfIeiquB2D01bh7QjZW/EMVM9Aw=="
|
|
353
353
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,145 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.1] - 2026-08-21
|
|
4
|
+
|
|
5
|
+
### Changed — the Step-8 amendment gate is a COMMAND, and the durable writers are witnessed
|
|
6
|
+
|
|
7
|
+
- `AMENDMENT_GATE` no longer asks the QE agent to judge whether every `AM-N` row names a real test.
|
|
8
|
+
It runs `dz amendment-check --slug <slug> --json` and reports the parsed verdict; the old judgement
|
|
9
|
+
wording is REMOVED from both workflow copies and all four `modules/08-qe.md` copies, not
|
|
10
|
+
accompanied. MEASURED 2026-08-21: `features/qe-scoped-review` shipped with five named amendment
|
|
11
|
+
test ids of which none existed, while its plan recorded `## Amendments: None`.
|
|
12
|
+
- the run-cost ledger and the training-pair capture (including backfill) stop handing a subagent a
|
|
13
|
+
pre-baked shell string carrying their payload — the shape a security classifier blocked nine times
|
|
14
|
+
in one run. Both now call `dz feature-adr-record`. The `sed` that rewrote `"date":null` / `"ts":null`
|
|
15
|
+
inside an already-serialised document is gone: the command stamps before serialising.
|
|
16
|
+
- a record failure still NEVER fails the run, but now survives it: `recordFailures` is returned from
|
|
17
|
+
every exit instead of scrolling past inside one log line.
|
|
18
|
+
|
|
19
|
+
Requires `@dzhechkov/harness-core >= 0.6.1`.
|
|
20
|
+
|
|
21
|
+
## [1.5.0] - 2026-08-21
|
|
22
|
+
|
|
23
|
+
### Changed — feature `qe-scoped-review`
|
|
24
|
+
|
|
25
|
+
### Changed — Step-8 Codex QE is SCOPED, and every fallback names its cause
|
|
26
|
+
|
|
27
|
+
Not published. Behaviour change to the Step-8 QE dispatch only; routing, the Claude belt, checkpoints
|
|
28
|
+
and training-pair capture are untouched.
|
|
29
|
+
|
|
30
|
+
- **The measurement that forced it** (same question, same model `gpt-5.6-sol` at effort `high`,
|
|
31
|
+
2026-08-21): an UNSCOPED `codex exec` QE prompt of 19 038 chars spent 280 s, exited 124 and produced
|
|
32
|
+
416 KB of exploration with NO verdict — and did it again under a 1500 s ceiling. The same question
|
|
33
|
+
scoped to two named files answered in 41 s with `Grade: B`; `codex review --commit` answered in
|
|
34
|
+
146 s with the scope derived from the diff. The budget went on RECONNAISSANCE, so raising the
|
|
35
|
+
timeout buys more of it, and the prompt ceiling was never the binding constraint (19 038 < 24 000,
|
|
36
|
+
with ~5 000 chars of headroom). The harm was not the minutes: on timeout the dispatch returns null,
|
|
37
|
+
the belt runs a Claude reviewer, and cross-family QE is lost SILENTLY on exactly the large features
|
|
38
|
+
that need it most.
|
|
39
|
+
- **Mode A — `codex review`** is now the primary Step-8 Codex pass; the diff defines the scope.
|
|
40
|
+
Default `--uncommitted`, overridable with `args.qeScope` / `args.qeScopeRef`. The builder can never
|
|
41
|
+
emit `-m` and never appends a positional prompt: MEASURED, `codex review` rejects `-m` with exit 2,
|
|
42
|
+
and EVERY scope flag (`--commit`, `--base`, `--uncommitted`) rejects `[PROMPT]` with exit 2. Either
|
|
43
|
+
mistake would present as a review that silently did not happen.
|
|
44
|
+
- **Mode B — a NARROWED `codex exec`** carries our own questions over at most 3 named files and ends
|
|
45
|
+
with `Grade: <A|B|C|D>`. An unscoped mode-B dispatch is not constructible: an empty file list yields
|
|
46
|
+
`''`, and `codexExecPlan` now refuses an unscoped `qe` prompt outright.
|
|
47
|
+
- **A locked decline taxonomy** — `timeout | no-verdict | tool-error | unusable-output | unavailable |
|
|
48
|
+
over-ceiling` — replaces the single generic reason. A timeout ("narrow the scope") and a broken
|
|
49
|
+
invocation ("fix the command") can no longer render the same string inside
|
|
50
|
+
`opus (cross-family QE DID NOT happen — …)`. An unknown kind throws rather than rendering something
|
|
51
|
+
plausible.
|
|
52
|
+
- **A machine sentinel** (`CODEX-QE-SIGNAL exit=… elapsed=…s bytes=…`, the Step-7.5 landing-signal
|
|
53
|
+
grammar) carries the exit code past the shell agent, so a timeout is knowable without asking a model
|
|
54
|
+
to self-report its own failure. A MISSING sentinel is classified `tool-error` on the pipeline path —
|
|
55
|
+
never a pass.
|
|
56
|
+
- **Zero findings is never an `A`.** Mode A cannot be asked for a letter, so its grade is derived from
|
|
57
|
+
the severities the reviewer reported and labelled `gradeSource: 'derived-from-findings'`; an empty or
|
|
58
|
+
unparseable finding set yields `null` and declines. MEASURED: `codex review` on a clean tree exits 0
|
|
59
|
+
with a well-formed, entirely empty review — the exact input that a default letter would have turned
|
|
60
|
+
into a clean bill of health.
|
|
61
|
+
- **`qe` now returns `gaps` from the reviewer's actual findings**, plus `gradeSource` and
|
|
62
|
+
`qeScope {mode, ref, files}`; the review scope enters the `qe` checkpoint hash so a resume cannot
|
|
63
|
+
present a mode-B verdict as a mode-A one.
|
|
64
|
+
- **`CODEX_EXEC_PROMPT_CEILING_CHARS` is retained but DEMOTED** — it is a sanity bound on an absurd
|
|
65
|
+
payload, and is no longer documented as the thing that prevents a stall. The defence is scope.
|
|
66
|
+
- **Unchanged on purpose:** `resolveQeSpec` / `coderIsCodex` / `qeShouldUseCodex` (verified correct by
|
|
67
|
+
direct call), the `crossFamilyQe` label format, and the Claude belt — the pipeline still never blocks
|
|
68
|
+
on Codex.
|
|
69
|
+
|
|
70
|
+
### Fixed — the K2 plan gate could not complete a run on a non-JS repository
|
|
71
|
+
|
|
72
|
+
- **C2 recognises test paths in every ecosystem.** The gate demanded a `.test.(ts|mjs|js)` suffix, so
|
|
73
|
+
a Python/Go/Rust/JVM/.NET feature could never satisfy it (measured: 4/4 JS forms matched, 0/11
|
|
74
|
+
non-JS). Replaced by a two-stage predicate — a language-neutral candidate-path extractor plus one
|
|
75
|
+
`$`-anchored rule per ecosystem (JS/TS, pytest `test_*.py` + `*_test.py`, Go, Rust `tests/*.rs` +
|
|
76
|
+
`tests.rs`, JVM, .NET). Widening it to "contains the word test" was rejected: `docs/testing.md`,
|
|
77
|
+
`src/latest.rs` and prose are still refused. An unknown ecosystem stays a FAIL, never a WARN.
|
|
78
|
+
- **New opt-in `testPathRules`** in the existing `architecture/project-skills.json`: additive rules,
|
|
79
|
+
each anchored by wrapping so an alternation cannot leak, capped at 200 characters, and FATAL when
|
|
80
|
+
malformed (`NOT-ESTABLISHED`, exit 3) rather than silently falling back to the built-ins.
|
|
81
|
+
- **The gate script is resolved from the WORKSPACE first.** The command `cd`s into the target repo,
|
|
82
|
+
so the repo-relative path died with `Cannot find module` on any repo without its own feature-adr
|
|
83
|
+
install. Order: `args.gateScript` (new, absolute, validated) → workspace copy → repo copy, with
|
|
84
|
+
`K2_GATE_SCRIPT=` / `K2_GATE_TRIED=` echoed for audit. Nothing found ⇒ reason `tooling-missing`
|
|
85
|
+
(a NOT-ESTABLISHED, never a skip), and the operator note now says the gate could not be RUN and
|
|
86
|
+
that this is not a plan defect — it no longer tells you to fix a plan that is fine.
|
|
87
|
+
- **A relative `args.dzBin` is pinned to the workspace root once**, before any `cd`, so the six
|
|
88
|
+
commands that splice it stop resolving against three different bases (a null usage probe was being
|
|
89
|
+
read upstream as "the Claude limit was hit").
|
|
90
|
+
|
|
91
|
+
### Changed — `EXPECTED_CODE_TARGETS` admissibility (C3)
|
|
92
|
+
|
|
93
|
+
- Dotfile targets (`.claude/…`, `.github/workflows/ci.yml`, `.gitignore`, `.env.example`) are
|
|
94
|
+
admissible — previously feature-adr could not name its own files or any CI config, in any language.
|
|
95
|
+
- Directory-shaped targets are now REJECTED (`trailing slash — names a directory, not a file`): a bare
|
|
96
|
+
directory gives plan-vs-diff matching nothing concrete to verify.
|
|
97
|
+
- Every refusal names its own defect instead of one catch-all: `path traversal ('..' segment)`,
|
|
98
|
+
`degenerate path segment`, `empty path segment`, `path segment ends with '.'`,
|
|
99
|
+
`illegal character '<c>'`, `empty stem after the leading dot`. The traversal check is a SEGMENT
|
|
100
|
+
test now, so the ordinary filename `foo..bar.ts` is no longer called a path traversal.
|
|
101
|
+
|
|
102
|
+
### Requires
|
|
103
|
+
|
|
104
|
+
- Nothing new at runtime: the workflow INLINES the gate command builder and its parser (the sandbox
|
|
105
|
+
cannot import), so this release is self-contained. The pure halves it mirrors live in
|
|
106
|
+
`@dzhechkov/harness-core` (`planCompletenessGateCmd` gained an optional 4th argument;
|
|
107
|
+
`refusalNoteFor` and `normalizeDzBin` are new) and a drift test pins the two copies together.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Fixed — Step 0 wrote nothing down, so the tier was unreadable and the acid check had no input
|
|
111
|
+
|
|
112
|
+
- **Step 0 now WRITES `00_complexity_assessment.md`** before returning: the tier with its decisive
|
|
113
|
+
criterion, the active steps, the recalled patterns, and an acid-case table whose rows are pinned to
|
|
114
|
+
the exact `| A<n> | <bad input> | <what must happen> |` shape the K2 gate parses. MEASURED
|
|
115
|
+
2026-08-21: 66 of 199 features carried that file and the last four in a row did not. Two silent
|
|
116
|
+
consequences — the tier was recorded NOWHERE while a run was alive (the checkpoint lands at phase
|
|
117
|
+
end, the result object only at the very end), and the C4 acid check quietly switched itself off,
|
|
118
|
+
including for the features that introduced it. An honest no-acid-cases run still writes prose and
|
|
119
|
+
no table; the gate now tells an ABSENT file from a deliberate skip.
|
|
120
|
+
- **A caller-forced tier is the tier of record.** `tier = args.tier || router.tier` means the run
|
|
121
|
+
executes the override while Step 0 wrote down its own classification. The artifact now carries
|
|
122
|
+
`Effective tier: <forced> (forced by the caller)` alongside the router's own recommendation, and
|
|
123
|
+
sizes the acid table for the effective tier. With no override the prompt is byte-identical.
|
|
124
|
+
- **A pre-contract router checkpoint no longer resumes into the new contract** — the resume gate only
|
|
125
|
+
asks whether the artifact is PRESENT, and any of the 66 features with a tableless file satisfied it.
|
|
126
|
+
|
|
127
|
+
### Fixed — the Step-8 QE stage measured the wrong thing in four different ways
|
|
128
|
+
|
|
129
|
+
- the change set is the run's DELTA (pre-code baseline vs after), not the working tree's current dirt;
|
|
130
|
+
- a finding whose location cannot be parsed is `unlocatable` and stays in the graded set — it used to
|
|
131
|
+
be filed as someone else's dirt, i.e. unknown counted as clean;
|
|
132
|
+
- mode B refuses a scope built from unlanded code, an unmeasured change set or an empty intersection;
|
|
133
|
+
- the QE scribe is WITNESSED: the report is re-hashed before and after, so an existing file from an
|
|
134
|
+
earlier run can no longer stand in for one this run never wrote.
|
|
135
|
+
|
|
136
|
+
### Fixed — durable state is written by a command, not by hand
|
|
137
|
+
|
|
138
|
+
- checkpoint lines now go through `dz feature-adr-checkpoint`. The subagent RUNS a command instead of
|
|
139
|
+
hand-writing state into a file: a security classifier blocked NINE consecutive checkpoint writes in
|
|
140
|
+
one run, `.fa-state/checkpoints.jsonl` was never created, resume was silently dead, and the run
|
|
141
|
+
reported success.
|
|
142
|
+
|
|
3
143
|
## [1.4.0] - 2026-08-20
|
|
4
144
|
|
|
5
145
|
### Changed — the composite design checkpoint
|
package/README.md
CHANGED
|
@@ -106,6 +106,127 @@ ARCHITECTURE → IMPLEMENTATION → CODE → QE → FLEET QE
|
|
|
106
106
|
# Full protocols + 6 extra skills, up to 7 fleet QE agents
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
### The K2 plan gate runs on ANY repo, not just JS/TS ones (v1.5.0)
|
|
110
|
+
|
|
111
|
+
Between Step 6 and Step 7 the pipeline runs a machine plan-completeness gate (K2). Until v1.5.0 it
|
|
112
|
+
could not finish a run on a non-JS repository — three separate defects, all closed here.
|
|
113
|
+
|
|
114
|
+
**1. Test paths in every ecosystem.** C2 checks that each ADR's Confirmation names a test file and
|
|
115
|
+
that the plan names the same path. It used to demand a `.test.(ts|mjs|js)` suffix, so on a Python,
|
|
116
|
+
Go, Rust, JVM or .NET repo it found nothing and failed the plan (measured: 4/4 JS forms matched,
|
|
117
|
+
0/11 non-JS forms did). It now uses a two-stage predicate — candidate paths are extracted from the
|
|
118
|
+
Confirmation text, then matched against one anchored rule per ecosystem:
|
|
119
|
+
|
|
120
|
+
| Ecosystem | Recognised |
|
|
121
|
+
|---|---|
|
|
122
|
+
| JS/TS | `*.test.*` / `*.spec.*` (`ts tsx mts cts mjs cjs js jsx`) |
|
|
123
|
+
| pytest | `test_*.py` **and** `*_test.py` |
|
|
124
|
+
| Go | `*_test.go` |
|
|
125
|
+
| Rust | `tests/*.rs` **and** `tests.rs` |
|
|
126
|
+
| JVM | `*Test|Tests|IT|Spec.(java kt kts scala groovy)` |
|
|
127
|
+
| .NET | `*Test|Tests.(cs fs vb)` |
|
|
128
|
+
|
|
129
|
+
Deliberately NOT "any path containing the word test": `docs/testing.md`, `src/latest.rs`,
|
|
130
|
+
`contests/results.py` and a sentence like *"the staleness test will verify this"* are all refused. A
|
|
131
|
+
gate that passes prose is worse than one that fails Python. An unknown ecosystem is still a FAIL,
|
|
132
|
+
never a warning — the message names the recognised set and points at the override below.
|
|
133
|
+
|
|
134
|
+
**Extending the vocabulary — `testPathRules`.** Add rules to the manifest the pipeline already
|
|
135
|
+
reads, `architecture/project-skills.json` (no second dotfile):
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"testPathRules": [
|
|
140
|
+
{ "ecosystem": "elixir", "pattern": "(?:^|/)test/[^/]+_test\\.exs" }
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Rules are ADDED to the built-ins, never replace them, and each pattern is anchored by wrapping
|
|
146
|
+
(`(?:<pattern>)$`) so every branch of an alternation is anchored. A malformed `testPathRules`
|
|
147
|
+
(bad JSON, not an array, missing `ecosystem`/`pattern`, an invalid regex, over 200 characters, or a
|
|
148
|
+
nested quantifier) is **NOT-ESTABLISHED, exit 3** — never a quiet fall-back to the built-ins.
|
|
149
|
+
|
|
150
|
+
**2. The gate script is found where it is installed — `args.gateScript`.** The skill lives in your
|
|
151
|
+
WORKSPACE; the gate command `cd`s into the TARGET repo, so a repo-relative lookup died with
|
|
152
|
+
`Cannot find module` on every repo that is not itself a feature-adr install. The resolution order is
|
|
153
|
+
now:
|
|
154
|
+
|
|
155
|
+
1. `args.gateScript` — an explicit ABSOLUTE path (validated: absolute, no `..`, or the run fails loudly);
|
|
156
|
+
2. the WORKSPACE copy (`$WS/.claude/skills/feature-adr/scripts/check-plan-completeness.mjs`);
|
|
157
|
+
3. the target-repo copy.
|
|
158
|
+
|
|
159
|
+
Workspace before repo on purpose: the verdict contract is defined by the parser inside the running
|
|
160
|
+
workflow, so only that installation's own copy is known to speak it. Every run echoes
|
|
161
|
+
`K2_GATE_SCRIPT=` (which copy ran) and `K2_GATE_TRIED=` (all candidates) for audit. If none exists
|
|
162
|
+
the run refuses with reason `tooling-missing` — a NOT-ESTABLISHED, never a skip — and the operator
|
|
163
|
+
message says the gate could not be RUN and that this is **not** a plan defect.
|
|
164
|
+
|
|
165
|
+
**3. Dotfile targets are admissible; directory-shaped ones are not.** `EXPECTED_CODE_TARGETS` may
|
|
166
|
+
now name `.claude/…`, `.github/workflows/ci.yml`, `.gitignore`, `.env.example`. What is refused is
|
|
167
|
+
refused BY NAME: `path traversal ('..' segment)`, `degenerate path segment`, `empty path segment`,
|
|
168
|
+
`trailing slash — names a directory, not a file`, `path segment ends with '.'`,
|
|
169
|
+
`illegal character '<c>'`, `empty stem after the leading dot`. The old substring traversal test also
|
|
170
|
+
rejected the ordinary filename `foo..bar.ts`; it is a segment test now.
|
|
171
|
+
|
|
172
|
+
### The Step-8 amendment gate runs a command instead of judging (v1.5.1)
|
|
173
|
+
|
|
174
|
+
Every `AM-N` row must resolve to a test found INSIDE the file the row names, and the check is
|
|
175
|
+
`dz amendment-check --slug <slug> --json` rather than a paragraph asking the reviewer to confirm it.
|
|
176
|
+
MEASURED 2026-08-21: `features/qe-scoped-review` shipped with five named amendment test ids of which
|
|
177
|
+
none existed, while its plan recorded `## Amendments: None`. The gate did not fail — a prompt cannot.
|
|
178
|
+
|
|
179
|
+
The PLAN is authoritative when it carries rows (Step 6 owes "carry AM-N into the plan verbatim"), and
|
|
180
|
+
the rule that keeps that honest is coverage: an amendment the plan drops — or rewords under the same
|
|
181
|
+
id — is a failure. Renaming the TEST stays legitimate; tests are named later than ideation guesses.
|
|
182
|
+
|
|
183
|
+
The durable writers were migrated in the same release: the run-cost ledger and the training-pair
|
|
184
|
+
capture no longer hand a subagent a pre-baked shell string with their payload baked in — the shape a
|
|
185
|
+
security classifier blocked nine times in one run. Both call `dz feature-adr-record`, which refuses
|
|
186
|
+
before writing and verifies the append by re-reading the tail. A record failure still never fails the
|
|
187
|
+
run; it now survives it in `recordFailures`.
|
|
188
|
+
|
|
189
|
+
Requires `@dzhechkov/harness-core >= 0.6.1`.
|
|
190
|
+
|
|
191
|
+
### Step 0 writes the assessment down, and the acid check gets its input back (v1.5.0)
|
|
192
|
+
|
|
193
|
+
Step 0 classifies the feature and now **writes `00_complexity_assessment.md` before it returns** — the
|
|
194
|
+
tier with the criterion that decided it, the active steps, the recalled patterns, and an acid-case
|
|
195
|
+
table. MEASURED 2026-08-21: 66 of 199 features carried that file and the last four in a row did not,
|
|
196
|
+
with two silent consequences. The tier was recorded NOWHERE while a run was alive (the router
|
|
197
|
+
checkpoint lands at phase end and the result object only at the very end), so mid-run it had to be
|
|
198
|
+
guessed from which artifacts happened to exist. And the K2 gate reads its acid corpus from that file,
|
|
199
|
+
so the C4 check quietly switched itself off — including for the features that introduced it.
|
|
200
|
+
|
|
201
|
+
Acid rows are pinned to the exact shape the gate parses, `| A<n> | <the bad input> | <what must
|
|
202
|
+
happen> |`; a loose shape disables the check just as silently as a missing file. A feature with no
|
|
203
|
+
acid cases still says so in prose and writes no table — the gate now tells an ABSENT file (a warning
|
|
204
|
+
that names the missing artifact) from a deliberate skip.
|
|
205
|
+
|
|
206
|
+
Two ways the tier could still go unrecorded, both closed here:
|
|
207
|
+
|
|
208
|
+
- a **pre-contract router checkpoint** used to resume into the new contract. The resume gate only asks
|
|
209
|
+
whether the artifact is PRESENT, and any of the 66 features carrying a tableless file satisfied it,
|
|
210
|
+
so Step 0 never re-ran. A router-scoped hash token now makes those entries re-run — once, and only
|
|
211
|
+
the router, rather than every in-flight stage.
|
|
212
|
+
- a **caller-forced `args.tier`** is now the tier of record. The run executes the override, so the
|
|
213
|
+
file carries `Effective tier: <forced> (forced by the caller)` alongside the router's own
|
|
214
|
+
recommendation, and sizes the acid table for the effective tier. With no override the prompt is
|
|
215
|
+
byte-identical to before.
|
|
216
|
+
|
|
217
|
+
Requires `@dzhechkov/harness-core >= 0.6.0`.
|
|
218
|
+
|
|
219
|
+
### The Step-8 QE stage measures the run, not the room (v1.5.0)
|
|
220
|
+
|
|
221
|
+
Four separate ways the QE stage graded the wrong thing, all closed: the change set is this run's DELTA
|
|
222
|
+
(a pre-code baseline against the state after) instead of the working tree's current dirt, so an
|
|
223
|
+
unrelated dirty file can no longer grade your feature; a finding whose location cannot be parsed is
|
|
224
|
+
`unlocatable` and stays in the graded set, where it used to be filed as someone else's dirt — unknown
|
|
225
|
+
counted as clean, inside the function whose job is honest attribution; mode B refuses a scope built
|
|
226
|
+
from unlanded code, an unmeasured change set, or an empty intersection; and the QE scribe is
|
|
227
|
+
WITNESSED — the report is re-hashed before and after, so a file left by an earlier run can no longer
|
|
228
|
+
stand in for one this run never wrote.
|
|
229
|
+
|
|
109
230
|
### The design fan resumes per sibling, and an incomplete design is REFUSED (v1.4.0)
|
|
110
231
|
|
|
111
232
|
Step 1–5 run as one parallel fan — requirements, ADR, QCSD, architecture. Until v1.4.0 the whole fan
|
|
@@ -237,6 +358,49 @@ family than the coder** — a model that writes code must not also self-QE; inde
|
|
|
237
358
|
catches what self-review misses. coder=Codex ⇒ QE=Claude (`opus`); coder=Claude ⇒ QE=Codex
|
|
238
359
|
(`codex:<top>:high`), or a Claude reviewer if Codex is unavailable (never blocks).
|
|
239
360
|
|
|
361
|
+
### Step 8 on Codex is SCOPED — two modes, and every fallback names its cause
|
|
362
|
+
|
|
363
|
+
When QE routes to Codex, Step 8 no longer hands it one unscoped `codex exec` prompt. Why, MEASURED
|
|
364
|
+
2026-08-21 — same question, same model (`gpt-5.6-sol`, effort `high`), three dispatches:
|
|
365
|
+
|
|
366
|
+
| dispatch | prompt | wall time | exit | verdict? |
|
|
367
|
+
|---|---|---|---|---|
|
|
368
|
+
| `codex exec`, unscoped | 19 038 chars | 280 s (and again under a 1500 s ceiling) | 124 | **none** — 416 KB of exploration |
|
|
369
|
+
| `codex exec`, scoped (*"read ONLY these two files"*) | 1 461 chars | 41 s | 0 | `Grade: B` + findings |
|
|
370
|
+
| `codex review --commit <SHA>` | scope from the diff | 146 s | 0 | verdict + findings |
|
|
371
|
+
|
|
372
|
+
The unscoped run spent its budget on **reconnaissance of the tree**, not on reasoning about the change.
|
|
373
|
+
So raising the timeout does not help, and the prompt-length ceiling was never the binding constraint
|
|
374
|
+
(19 038 sat under `CODEX_EXEC_PROMPT_CEILING_CHARS` = 24 000 with ~5 000 to spare). The cost is not the
|
|
375
|
+
wasted minutes: on timeout the dispatch returns `null`, the belt runs a Claude reviewer, and
|
|
376
|
+
cross-family QE is silently lost — on exactly the big features that need it most.
|
|
377
|
+
|
|
378
|
+
- **Mode A (primary): `codex review`.** The scope comes from the DIFF, so Codex computes for free the
|
|
379
|
+
thing we were paying a model to do badly. Default scope `--uncommitted`; override with
|
|
380
|
+
`args.qeScope: 'commit' | 'base'` + `args.qeScopeRef`. Two CLI facts are encoded as refusals, both
|
|
381
|
+
measured: `codex review` rejects `-m` (exit 2 — the model goes through `-c model=`), and **every**
|
|
382
|
+
scope flag rejects a positional prompt (`--commit`, `--base` and `--uncommitted` alike, exit 2). So
|
|
383
|
+
mode A runs on Codex's built-in review instructions and cannot be asked our questions.
|
|
384
|
+
- **Mode B (follow-up): a NARROWED `codex exec`.** Carries our own questions over at most 3 named
|
|
385
|
+
files with the load-bearing clause *"read ONLY these files, do not explore the repository"*, and
|
|
386
|
+
ends with `Grade: <A|B|C|D>`. An unscoped mode-B prompt is not constructible: an empty file list
|
|
387
|
+
returns `''` and `codexExecPlan` refuses an unscoped `qe` prompt outright.
|
|
388
|
+
- **The verdict is parsed, never synthesised.** A `Grade: D` is a SUCCESSFUL cross-family review; a
|
|
389
|
+
review with no grade is a FAILED one. Mode A cannot state a letter, so its grade is DERIVED from the
|
|
390
|
+
severities it reported and labelled `gradeSource: 'derived-from-findings'` — and **zero findings
|
|
391
|
+
yields `null`, never `A`** (measured: `codex review` on a clean tree exits 0 with a polite, entirely
|
|
392
|
+
empty review; calling that an `A` would be a review of nothing reported as a pass).
|
|
393
|
+
- **Every fallback names its cause.** The reason carried into
|
|
394
|
+
`opus (cross-family QE DID NOT happen — …)` comes from a locked taxonomy —
|
|
395
|
+
`timeout` (narrow the scope) · `no-verdict` · `tool-error` (fix the invocation) · `unusable-output` ·
|
|
396
|
+
`unavailable` (fix the account/model) · `over-ceiling`. A timeout and an unusable output can never
|
|
397
|
+
render the same string, because the operator's next move differs.
|
|
398
|
+
- **The pipeline still never blocks on Codex.** Both modes fail into the same Claude belt as before.
|
|
399
|
+
|
|
400
|
+
After a Codex verdict a cheap Claude agent transcribes it into `08_qe_report.md` (mode A takes no
|
|
401
|
+
prompt, so the reviewer cannot be asked to write anything). It is a scribe, not a second reviewer: the
|
|
402
|
+
grade is Codex's and is stated as final.
|
|
403
|
+
|
|
240
404
|
**Precedence:** `args.models[stage]` wins; the legacy `planner`/`coder`/`qeReviewer`/`codexModel` knobs
|
|
241
405
|
fill only unspecified stages. `codexModel` seeds the id for a bare `'codex'` spec. gpt-5.6-ready: a new
|
|
242
406
|
Codex id is a data-only allowlist edit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/skills-feature-adr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Adaptive Feature Development skill pack for Claude Code — 11-step pipeline with Complexity Router (S/M/L/XL), ADR-driven architecture, 15 agentic-qe skills, multi-agent fleet QE. Supports --full-qe, --full-qe-extended, --with-learning, and --knowledge-extractor modes.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skills-feature-adr": "./bin/cli.js"
|
package/sbom.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"hashes": [
|
|
16
16
|
{
|
|
17
17
|
"alg": "SHA-256",
|
|
18
|
-
"content": "
|
|
18
|
+
"content": "88e0389016f697b90731ee8743a35e523f0ccb079426d59861d894f33e516392"
|
|
19
19
|
}
|
|
20
20
|
]
|
|
21
21
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"hashes": [
|
|
36
36
|
{
|
|
37
37
|
"alg": "SHA-256",
|
|
38
|
-
"content": "
|
|
38
|
+
"content": "47f9402d4c3954d6d5a75ad906ec7b592010fdfd80f347d4a87dd14dd9e300ce"
|
|
39
39
|
}
|
|
40
40
|
]
|
|
41
41
|
},
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"hashes": [
|
|
66
66
|
{
|
|
67
67
|
"alg": "SHA-256",
|
|
68
|
-
"content": "
|
|
68
|
+
"content": "3fbe9ef86c9f6fa7c86bbe7760729094e80edb67577e397678b2d98dca1c2034"
|
|
69
69
|
}
|
|
70
70
|
]
|
|
71
71
|
},
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
"hashes": [
|
|
396
396
|
{
|
|
397
397
|
"alg": "SHA-256",
|
|
398
|
-
"content": "
|
|
398
|
+
"content": "3903e2694ad47782914b35869bbf19c5f0803b6dafdcfb629df343a5ff00d1b9"
|
|
399
399
|
}
|
|
400
400
|
]
|
|
401
401
|
},
|
|
@@ -415,7 +415,7 @@
|
|
|
415
415
|
"hashes": [
|
|
416
416
|
{
|
|
417
417
|
"alg": "SHA-256",
|
|
418
|
-
"content": "
|
|
418
|
+
"content": "bd5b5809142c12fa1378de446b68027a0c342a7a91ad17e4fb3395ca8f6a8508"
|
|
419
419
|
}
|
|
420
420
|
]
|
|
421
421
|
},
|
|
@@ -645,7 +645,7 @@
|
|
|
645
645
|
"hashes": [
|
|
646
646
|
{
|
|
647
647
|
"alg": "SHA-256",
|
|
648
|
-
"content": "
|
|
648
|
+
"content": "2e1afeb8cbc08ce872fc8280a0809e7712ee76879d08b48caa5a0d4082cb8647"
|
|
649
649
|
}
|
|
650
650
|
]
|
|
651
651
|
},
|
|
@@ -805,7 +805,7 @@
|
|
|
805
805
|
"hashes": [
|
|
806
806
|
{
|
|
807
807
|
"alg": "SHA-256",
|
|
808
|
-
"content": "
|
|
808
|
+
"content": "0c74e8995d82bb986ef3233e91beebfa0e761aae4434f24554077bb05d2199e9"
|
|
809
809
|
}
|
|
810
810
|
]
|
|
811
811
|
},
|
|
@@ -198,6 +198,13 @@ After the plan is written, run the gate. It is a script, not a judgement call:
|
|
|
198
198
|
node .claude/skills/feature-adr/scripts/check-plan-completeness.mjs features/<slug>
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
+
The path above is relative to the workspace the skill is INSTALLED in. Working inside a target repo
|
|
202
|
+
that has no feature-adr install of its own? Pass the absolute path to the script instead — the
|
|
203
|
+
ultracode workflow does this for you (it tries `args.gateScript`, then the workspace copy, then the
|
|
204
|
+
target-repo copy, and refuses with `tooling-missing` if none exists). The gate is language-neutral:
|
|
205
|
+
C2 recognises JS/TS, pytest, Go, Rust, JVM and .NET test paths, extensible per project with
|
|
206
|
+
`testPathRules` in `architecture/project-skills.json`.
|
|
207
|
+
|
|
201
208
|
| Exit | Verdict | What you do |
|
|
202
209
|
|------|---------|-------------|
|
|
203
210
|
| `0` | PASS | proceed to Step 7 |
|
|
@@ -160,18 +160,32 @@ The Confirmation-to-test link is load-bearing. If absent, grade no better than C
|
|
|
160
160
|
|
|
161
161
|
### 3b. Amendment Gate (P2 — amendments inherit the Confirmation discipline)
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
**Do not judge this yourself — RUN the check.** Resolution of every `AM-N` row to a real test is a
|
|
164
|
+
deterministic question, and a deterministic question must not live in prose (cost-of-detection ladder).
|
|
165
|
+
It used to live here, and `features/qe-scoped-review` shipped with five named amendment test ids of
|
|
166
|
+
which none existed, while its plan recorded `## Amendments: None`.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
dz amendment-check --slug <feature-slug> --json # or --feature-dir <abs path>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
| Outcome | Exit | What it means for the gate |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `pass` | 0 | every row resolves to a test found inside the file the row names |
|
|
175
|
+
| `skip` | 0 | no `## Amendments` section — an absence, stated, never a silent pass |
|
|
176
|
+
| `fail` | 1 | a HIGH gap: quote every reason verbatim into the QE report |
|
|
177
|
+
| `not-established` | 3 | the grammar matched nothing, or the inputs were unreadable — **never a pass** |
|
|
178
|
+
|
|
179
|
+
Empty stdout, a crash, or a missing `dz` is `not-established`, not a clean gate.
|
|
180
|
+
|
|
181
|
+
**What the command does NOT prove:** that a resolved test discriminates. Vacuity stays yours, in the
|
|
182
|
+
SAME `dz discrimination-check --test <f1,f2,...> --base HEAD --json` run as the ADR property test — an
|
|
183
|
+
amendment test that stays green at pre-feature base proves nothing about its amendment. A **safeguard**
|
|
184
|
+
amendment additionally needs its test to prove the safeguard FIRES on a real input: a structurally-dead
|
|
185
|
+
safeguard passes an existence check and never fires in production.
|
|
186
|
+
|
|
187
|
+
This closes the distance between "we caught the design flaw" (challenge-panel/QCSD) and "we proved the
|
|
188
|
+
fix works".
|
|
175
189
|
|
|
176
190
|
### 3c. I/O-on-pure-path + fixture-swap hunt (P5)
|
|
177
191
|
|