@dzhechkov/skills-feature-adr 1.3.67 → 1.5.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/.dz-manifest.json +7 -7
- package/CHANGELOG.md +157 -0
- package/README.md +175 -0
- package/package.json +1 -1
- package/sbom.json +6 -6
- package/templates/.claude/skills/feature-adr/modules/06-implementation-plan.md +7 -0
- package/templates/.claude/skills/feature-adr/scripts/check-plan-completeness.mjs +146 -9
- package/templates/.claude/workflows/feature-adr.js +907 -53
package/.dz-manifest.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "CHANGELOG.md",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "59592f0b4612a14bdec61c96c20655644701cd77a0bf9d68315e1c98461900ff"
|
|
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": "8df52c2c7d7c70ef29670339ac01a6c0b0b6a36bb51a0be15dcc03357097cca8"
|
|
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": "0fc1ae047774c2c67c6acf77a88eef8c9532c609bfc68b9e9cb37591cb61158f"
|
|
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",
|
|
@@ -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": "3c8838c6a5107ea8a4b909cd9ccfa4d416324bb4ed9341f50b8e23f1b569d244"
|
|
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": "8JbfbAiP55HvylGRoFfLwEtYCoo02jM9PwfOcTiYWQXfN/Utcg+Wc7uFo08yfqPUyONXsMCcEfqB7Y5eKWIvDg=="
|
|
353
353
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,162 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.0] - 2026-08-21
|
|
4
|
+
|
|
5
|
+
### Changed — feature `qe-scoped-review`
|
|
6
|
+
|
|
7
|
+
### Changed — Step-8 Codex QE is SCOPED, and every fallback names its cause
|
|
8
|
+
|
|
9
|
+
Not published. Behaviour change to the Step-8 QE dispatch only; routing, the Claude belt, checkpoints
|
|
10
|
+
and training-pair capture are untouched.
|
|
11
|
+
|
|
12
|
+
- **The measurement that forced it** (same question, same model `gpt-5.6-sol` at effort `high`,
|
|
13
|
+
2026-08-21): an UNSCOPED `codex exec` QE prompt of 19 038 chars spent 280 s, exited 124 and produced
|
|
14
|
+
416 KB of exploration with NO verdict — and did it again under a 1500 s ceiling. The same question
|
|
15
|
+
scoped to two named files answered in 41 s with `Grade: B`; `codex review --commit` answered in
|
|
16
|
+
146 s with the scope derived from the diff. The budget went on RECONNAISSANCE, so raising the
|
|
17
|
+
timeout buys more of it, and the prompt ceiling was never the binding constraint (19 038 < 24 000,
|
|
18
|
+
with ~5 000 chars of headroom). The harm was not the minutes: on timeout the dispatch returns null,
|
|
19
|
+
the belt runs a Claude reviewer, and cross-family QE is lost SILENTLY on exactly the large features
|
|
20
|
+
that need it most.
|
|
21
|
+
- **Mode A — `codex review`** is now the primary Step-8 Codex pass; the diff defines the scope.
|
|
22
|
+
Default `--uncommitted`, overridable with `args.qeScope` / `args.qeScopeRef`. The builder can never
|
|
23
|
+
emit `-m` and never appends a positional prompt: MEASURED, `codex review` rejects `-m` with exit 2,
|
|
24
|
+
and EVERY scope flag (`--commit`, `--base`, `--uncommitted`) rejects `[PROMPT]` with exit 2. Either
|
|
25
|
+
mistake would present as a review that silently did not happen.
|
|
26
|
+
- **Mode B — a NARROWED `codex exec`** carries our own questions over at most 3 named files and ends
|
|
27
|
+
with `Grade: <A|B|C|D>`. An unscoped mode-B dispatch is not constructible: an empty file list yields
|
|
28
|
+
`''`, and `codexExecPlan` now refuses an unscoped `qe` prompt outright.
|
|
29
|
+
- **A locked decline taxonomy** — `timeout | no-verdict | tool-error | unusable-output | unavailable |
|
|
30
|
+
over-ceiling` — replaces the single generic reason. A timeout ("narrow the scope") and a broken
|
|
31
|
+
invocation ("fix the command") can no longer render the same string inside
|
|
32
|
+
`opus (cross-family QE DID NOT happen — …)`. An unknown kind throws rather than rendering something
|
|
33
|
+
plausible.
|
|
34
|
+
- **A machine sentinel** (`CODEX-QE-SIGNAL exit=… elapsed=…s bytes=…`, the Step-7.5 landing-signal
|
|
35
|
+
grammar) carries the exit code past the shell agent, so a timeout is knowable without asking a model
|
|
36
|
+
to self-report its own failure. A MISSING sentinel is classified `tool-error` on the pipeline path —
|
|
37
|
+
never a pass.
|
|
38
|
+
- **Zero findings is never an `A`.** Mode A cannot be asked for a letter, so its grade is derived from
|
|
39
|
+
the severities the reviewer reported and labelled `gradeSource: 'derived-from-findings'`; an empty or
|
|
40
|
+
unparseable finding set yields `null` and declines. MEASURED: `codex review` on a clean tree exits 0
|
|
41
|
+
with a well-formed, entirely empty review — the exact input that a default letter would have turned
|
|
42
|
+
into a clean bill of health.
|
|
43
|
+
- **`qe` now returns `gaps` from the reviewer's actual findings**, plus `gradeSource` and
|
|
44
|
+
`qeScope {mode, ref, files}`; the review scope enters the `qe` checkpoint hash so a resume cannot
|
|
45
|
+
present a mode-B verdict as a mode-A one.
|
|
46
|
+
- **`CODEX_EXEC_PROMPT_CEILING_CHARS` is retained but DEMOTED** — it is a sanity bound on an absurd
|
|
47
|
+
payload, and is no longer documented as the thing that prevents a stall. The defence is scope.
|
|
48
|
+
- **Unchanged on purpose:** `resolveQeSpec` / `coderIsCodex` / `qeShouldUseCodex` (verified correct by
|
|
49
|
+
direct call), the `crossFamilyQe` label format, and the Claude belt — the pipeline still never blocks
|
|
50
|
+
on Codex.
|
|
51
|
+
|
|
52
|
+
### Fixed — the K2 plan gate could not complete a run on a non-JS repository
|
|
53
|
+
|
|
54
|
+
- **C2 recognises test paths in every ecosystem.** The gate demanded a `.test.(ts|mjs|js)` suffix, so
|
|
55
|
+
a Python/Go/Rust/JVM/.NET feature could never satisfy it (measured: 4/4 JS forms matched, 0/11
|
|
56
|
+
non-JS). Replaced by a two-stage predicate — a language-neutral candidate-path extractor plus one
|
|
57
|
+
`$`-anchored rule per ecosystem (JS/TS, pytest `test_*.py` + `*_test.py`, Go, Rust `tests/*.rs` +
|
|
58
|
+
`tests.rs`, JVM, .NET). Widening it to "contains the word test" was rejected: `docs/testing.md`,
|
|
59
|
+
`src/latest.rs` and prose are still refused. An unknown ecosystem stays a FAIL, never a WARN.
|
|
60
|
+
- **New opt-in `testPathRules`** in the existing `architecture/project-skills.json`: additive rules,
|
|
61
|
+
each anchored by wrapping so an alternation cannot leak, capped at 200 characters, and FATAL when
|
|
62
|
+
malformed (`NOT-ESTABLISHED`, exit 3) rather than silently falling back to the built-ins.
|
|
63
|
+
- **The gate script is resolved from the WORKSPACE first.** The command `cd`s into the target repo,
|
|
64
|
+
so the repo-relative path died with `Cannot find module` on any repo without its own feature-adr
|
|
65
|
+
install. Order: `args.gateScript` (new, absolute, validated) → workspace copy → repo copy, with
|
|
66
|
+
`K2_GATE_SCRIPT=` / `K2_GATE_TRIED=` echoed for audit. Nothing found ⇒ reason `tooling-missing`
|
|
67
|
+
(a NOT-ESTABLISHED, never a skip), and the operator note now says the gate could not be RUN and
|
|
68
|
+
that this is not a plan defect — it no longer tells you to fix a plan that is fine.
|
|
69
|
+
- **A relative `args.dzBin` is pinned to the workspace root once**, before any `cd`, so the six
|
|
70
|
+
commands that splice it stop resolving against three different bases (a null usage probe was being
|
|
71
|
+
read upstream as "the Claude limit was hit").
|
|
72
|
+
|
|
73
|
+
### Changed — `EXPECTED_CODE_TARGETS` admissibility (C3)
|
|
74
|
+
|
|
75
|
+
- Dotfile targets (`.claude/…`, `.github/workflows/ci.yml`, `.gitignore`, `.env.example`) are
|
|
76
|
+
admissible — previously feature-adr could not name its own files or any CI config, in any language.
|
|
77
|
+
- Directory-shaped targets are now REJECTED (`trailing slash — names a directory, not a file`): a bare
|
|
78
|
+
directory gives plan-vs-diff matching nothing concrete to verify.
|
|
79
|
+
- Every refusal names its own defect instead of one catch-all: `path traversal ('..' segment)`,
|
|
80
|
+
`degenerate path segment`, `empty path segment`, `path segment ends with '.'`,
|
|
81
|
+
`illegal character '<c>'`, `empty stem after the leading dot`. The traversal check is a SEGMENT
|
|
82
|
+
test now, so the ordinary filename `foo..bar.ts` is no longer called a path traversal.
|
|
83
|
+
|
|
84
|
+
### Requires
|
|
85
|
+
|
|
86
|
+
- Nothing new at runtime: the workflow INLINES the gate command builder and its parser (the sandbox
|
|
87
|
+
cannot import), so this release is self-contained. The pure halves it mirrors live in
|
|
88
|
+
`@dzhechkov/harness-core` (`planCompletenessGateCmd` gained an optional 4th argument;
|
|
89
|
+
`refusalNoteFor` and `normalizeDzBin` are new) and a drift test pins the two copies together.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
### Fixed — Step 0 wrote nothing down, so the tier was unreadable and the acid check had no input
|
|
93
|
+
|
|
94
|
+
- **Step 0 now WRITES `00_complexity_assessment.md`** before returning: the tier with its decisive
|
|
95
|
+
criterion, the active steps, the recalled patterns, and an acid-case table whose rows are pinned to
|
|
96
|
+
the exact `| A<n> | <bad input> | <what must happen> |` shape the K2 gate parses. MEASURED
|
|
97
|
+
2026-08-21: 66 of 199 features carried that file and the last four in a row did not. Two silent
|
|
98
|
+
consequences — the tier was recorded NOWHERE while a run was alive (the checkpoint lands at phase
|
|
99
|
+
end, the result object only at the very end), and the C4 acid check quietly switched itself off,
|
|
100
|
+
including for the features that introduced it. An honest no-acid-cases run still writes prose and
|
|
101
|
+
no table; the gate now tells an ABSENT file from a deliberate skip.
|
|
102
|
+
- **A caller-forced tier is the tier of record.** `tier = args.tier || router.tier` means the run
|
|
103
|
+
executes the override while Step 0 wrote down its own classification. The artifact now carries
|
|
104
|
+
`Effective tier: <forced> (forced by the caller)` alongside the router's own recommendation, and
|
|
105
|
+
sizes the acid table for the effective tier. With no override the prompt is byte-identical.
|
|
106
|
+
- **A pre-contract router checkpoint no longer resumes into the new contract** — the resume gate only
|
|
107
|
+
asks whether the artifact is PRESENT, and any of the 66 features with a tableless file satisfied it.
|
|
108
|
+
|
|
109
|
+
### Fixed — the Step-8 QE stage measured the wrong thing in four different ways
|
|
110
|
+
|
|
111
|
+
- the change set is the run's DELTA (pre-code baseline vs after), not the working tree's current dirt;
|
|
112
|
+
- a finding whose location cannot be parsed is `unlocatable` and stays in the graded set — it used to
|
|
113
|
+
be filed as someone else's dirt, i.e. unknown counted as clean;
|
|
114
|
+
- mode B refuses a scope built from unlanded code, an unmeasured change set or an empty intersection;
|
|
115
|
+
- the QE scribe is WITNESSED: the report is re-hashed before and after, so an existing file from an
|
|
116
|
+
earlier run can no longer stand in for one this run never wrote.
|
|
117
|
+
|
|
118
|
+
### Fixed — durable state is written by a command, not by hand
|
|
119
|
+
|
|
120
|
+
- checkpoint lines now go through `dz feature-adr-checkpoint`. The subagent RUNS a command instead of
|
|
121
|
+
hand-writing state into a file: a security classifier blocked NINE consecutive checkpoint writes in
|
|
122
|
+
one run, `.fa-state/checkpoints.jsonl` was never created, resume was silently dead, and the run
|
|
123
|
+
reported success.
|
|
124
|
+
|
|
125
|
+
## [1.4.0] - 2026-08-20
|
|
126
|
+
|
|
127
|
+
### Changed — the composite design checkpoint
|
|
128
|
+
|
|
129
|
+
- The Step 1–5 design fan is checkpointed **per sibling** instead of as one composite entry, so one
|
|
130
|
+
dead agent no longer discards three finished siblings. Each sibling's resume key carries its own
|
|
131
|
+
steering inputs, including its prompt text: fixing one step's instructions invalidates that step
|
|
132
|
+
and nothing else.
|
|
133
|
+
- An incomplete fan is **refused** at the Step-5/6 boundary (`phase: 'design-incomplete'`) instead of
|
|
134
|
+
being handed to the planner. Previously the completeness verdict was computed and only logged, and
|
|
135
|
+
Step 6 planned off a null design. The refusal captures training pairs and appends a cost-ledger row
|
|
136
|
+
on the way out, so a stopped run is neither silent nor invisible to cost analysis.
|
|
137
|
+
- Three named reasons with distinct repairs — `substage-missing`, `artifact-missing`,
|
|
138
|
+
`probe-not-established`. Under `resume:'force'` the artifact-missing repair says `resume:'never'`,
|
|
139
|
+
because force skips artifact probes and a plain re-invoke would loop forever.
|
|
140
|
+
- The artifact check no longer lists a directory: it runs `[ -f <exact path> ]` per required
|
|
141
|
+
artifact. A listing is a list of filenames, and a file whose NAME ends in a newline was measured
|
|
142
|
+
satisfying the requirement for the real file.
|
|
143
|
+
- The probe transcript is validated strictly rather than scanned. It is relayed by an agent, not read
|
|
144
|
+
from a pipe, and an agent that narrates the expected output emits the token byte-identically —
|
|
145
|
+
which produced a false pass on a missing artifact before this release. Inconclusive is never a pass.
|
|
146
|
+
|
|
147
|
+
### Requires
|
|
148
|
+
|
|
149
|
+
- `@dzhechkov/harness-core >= 0.5.4`. The checkpoint schema is now `fa-ckpt-3`: every existing
|
|
150
|
+
`.fa-state/checkpoints.jsonl` reads as no checkpoint, so each in-flight feature re-runs
|
|
151
|
+
router+design+plan once.
|
|
152
|
+
|
|
153
|
+
### Verification
|
|
154
|
+
|
|
155
|
+
Eight cross-family review rounds (Codex `gpt-5.6-sol` at xhigh, each pinned to a sha256 of the
|
|
156
|
+
reviewed files): D → D → D → B → C → C → D → **A**, the last with no new defects. Full ladder in
|
|
157
|
+
`features/feature-adr-hardening/composite-design-checkpoint.md`.
|
|
158
|
+
|
|
159
|
+
|
|
3
160
|
### Follow-up (QE LOW gaps closed before publish)
|
|
4
161
|
- **Upstream deletions**: `update` now removes files the template dropped (manifest-tracked orphans only; user-created untracked files are never touched) — previously `diff.missing` was computed but ignored. Shown in the summary + `--dry-run` (`- DEL`). Tests: Case G/H.
|
|
5
162
|
- Removed a dead `unchanged` branch in the directory update path (unreachable — `diff.modified` guarantees bytes differ).
|
package/README.md
CHANGED
|
@@ -106,6 +106,138 @@ 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
|
+
### Step 0 writes the assessment down, and the acid check gets its input back (v1.5.0)
|
|
173
|
+
|
|
174
|
+
Step 0 classifies the feature and now **writes `00_complexity_assessment.md` before it returns** — the
|
|
175
|
+
tier with the criterion that decided it, the active steps, the recalled patterns, and an acid-case
|
|
176
|
+
table. MEASURED 2026-08-21: 66 of 199 features carried that file and the last four in a row did not,
|
|
177
|
+
with two silent consequences. The tier was recorded NOWHERE while a run was alive (the router
|
|
178
|
+
checkpoint lands at phase end and the result object only at the very end), so mid-run it had to be
|
|
179
|
+
guessed from which artifacts happened to exist. And the K2 gate reads its acid corpus from that file,
|
|
180
|
+
so the C4 check quietly switched itself off — including for the features that introduced it.
|
|
181
|
+
|
|
182
|
+
Acid rows are pinned to the exact shape the gate parses, `| A<n> | <the bad input> | <what must
|
|
183
|
+
happen> |`; a loose shape disables the check just as silently as a missing file. A feature with no
|
|
184
|
+
acid cases still says so in prose and writes no table — the gate now tells an ABSENT file (a warning
|
|
185
|
+
that names the missing artifact) from a deliberate skip.
|
|
186
|
+
|
|
187
|
+
Two ways the tier could still go unrecorded, both closed here:
|
|
188
|
+
|
|
189
|
+
- a **pre-contract router checkpoint** used to resume into the new contract. The resume gate only asks
|
|
190
|
+
whether the artifact is PRESENT, and any of the 66 features carrying a tableless file satisfied it,
|
|
191
|
+
so Step 0 never re-ran. A router-scoped hash token now makes those entries re-run — once, and only
|
|
192
|
+
the router, rather than every in-flight stage.
|
|
193
|
+
- a **caller-forced `args.tier`** is now the tier of record. The run executes the override, so the
|
|
194
|
+
file carries `Effective tier: <forced> (forced by the caller)` alongside the router's own
|
|
195
|
+
recommendation, and sizes the acid table for the effective tier. With no override the prompt is
|
|
196
|
+
byte-identical to before.
|
|
197
|
+
|
|
198
|
+
Requires `@dzhechkov/harness-core >= 0.6.0`.
|
|
199
|
+
|
|
200
|
+
### The Step-8 QE stage measures the run, not the room (v1.5.0)
|
|
201
|
+
|
|
202
|
+
Four separate ways the QE stage graded the wrong thing, all closed: the change set is this run's DELTA
|
|
203
|
+
(a pre-code baseline against the state after) instead of the working tree's current dirt, so an
|
|
204
|
+
unrelated dirty file can no longer grade your feature; a finding whose location cannot be parsed is
|
|
205
|
+
`unlocatable` and stays in the graded set, where it used to be filed as someone else's dirt — unknown
|
|
206
|
+
counted as clean, inside the function whose job is honest attribution; mode B refuses a scope built
|
|
207
|
+
from unlanded code, an unmeasured change set, or an empty intersection; and the QE scribe is
|
|
208
|
+
WITNESSED — the report is re-hashed before and after, so a file left by an earlier run can no longer
|
|
209
|
+
stand in for one this run never wrote.
|
|
210
|
+
|
|
211
|
+
### The design fan resumes per sibling, and an incomplete design is REFUSED (v1.4.0)
|
|
212
|
+
|
|
213
|
+
Step 1–5 run as one parallel fan — requirements, ADR, QCSD, architecture. Until v1.4.0 the whole fan
|
|
214
|
+
was ONE checkpoint entry, so a single dead agent discarded three finished siblings and the next
|
|
215
|
+
invocation paid for all four again. Each sibling is now checkpointed on its own
|
|
216
|
+
(`design:requirements` / `design:adr` / `design:qcsd` / `design:architecture`), keyed on what steers
|
|
217
|
+
**it** — its own prompt text included, so correcting one step's instructions re-runs that step and
|
|
218
|
+
leaves the others alone.
|
|
219
|
+
|
|
220
|
+
The other half is what the pipeline is allowed to CONSUME. An incomplete fan no longer flows into
|
|
221
|
+
Step 6: the run stops at the Step-5/6 boundary and returns `phase: 'design-incomplete'` rather than
|
|
222
|
+
producing a plan with no ADR behind it. Three distinct reasons, each with its own repair:
|
|
223
|
+
|
|
224
|
+
| Reason | What happened | Repair printed |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| `substage-missing` | a sibling agent died (often a Claude limit) | re-invoke — the finished siblings resume free; or route that stage to Codex |
|
|
227
|
+
| `artifact-missing` | every sibling reported success, but a required file is not on disk | under `resume:'force'` it says use `resume:'never'` — force skips artifact probes, so re-invoking would loop |
|
|
228
|
+
| `probe-not-established` | the check could not be trusted at all | inconclusive is never a pass — refuse, then re-run |
|
|
229
|
+
|
|
230
|
+
The artifact check never lists a directory. It asks `[ -f <exact path> ]` per required artifact,
|
|
231
|
+
because a listing is a list of filenames and the data can impersonate the frame: measured, a file
|
|
232
|
+
whose NAME ends in a newline satisfied the requirement for the real file. And since the check is
|
|
233
|
+
relayed by an agent rather than read from a pipe, the whole transcript is validated — an agent that
|
|
234
|
+
merely *narrates* the expected output emits the token byte-identically, and that produced a false
|
|
235
|
+
pass before this release.
|
|
236
|
+
|
|
237
|
+
Requires `@dzhechkov/harness-core >= 0.5.4`. **One-time cost:** the checkpoint schema moved to
|
|
238
|
+
`fa-ckpt-3`, so every existing `.fa-state/checkpoints.jsonl` reads as no checkpoint and each
|
|
239
|
+
in-flight feature re-runs router+design+plan once.
|
|
240
|
+
|
|
109
241
|
### Durable checkpoints + resume (v1.3.64)
|
|
110
242
|
|
|
111
243
|
Every expensive stage (router / design / plan / code / qe / fleet) checkpoints its result into
|
|
@@ -207,6 +339,49 @@ family than the coder** — a model that writes code must not also self-QE; inde
|
|
|
207
339
|
catches what self-review misses. coder=Codex ⇒ QE=Claude (`opus`); coder=Claude ⇒ QE=Codex
|
|
208
340
|
(`codex:<top>:high`), or a Claude reviewer if Codex is unavailable (never blocks).
|
|
209
341
|
|
|
342
|
+
### Step 8 on Codex is SCOPED — two modes, and every fallback names its cause
|
|
343
|
+
|
|
344
|
+
When QE routes to Codex, Step 8 no longer hands it one unscoped `codex exec` prompt. Why, MEASURED
|
|
345
|
+
2026-08-21 — same question, same model (`gpt-5.6-sol`, effort `high`), three dispatches:
|
|
346
|
+
|
|
347
|
+
| dispatch | prompt | wall time | exit | verdict? |
|
|
348
|
+
|---|---|---|---|---|
|
|
349
|
+
| `codex exec`, unscoped | 19 038 chars | 280 s (and again under a 1500 s ceiling) | 124 | **none** — 416 KB of exploration |
|
|
350
|
+
| `codex exec`, scoped (*"read ONLY these two files"*) | 1 461 chars | 41 s | 0 | `Grade: B` + findings |
|
|
351
|
+
| `codex review --commit <SHA>` | scope from the diff | 146 s | 0 | verdict + findings |
|
|
352
|
+
|
|
353
|
+
The unscoped run spent its budget on **reconnaissance of the tree**, not on reasoning about the change.
|
|
354
|
+
So raising the timeout does not help, and the prompt-length ceiling was never the binding constraint
|
|
355
|
+
(19 038 sat under `CODEX_EXEC_PROMPT_CEILING_CHARS` = 24 000 with ~5 000 to spare). The cost is not the
|
|
356
|
+
wasted minutes: on timeout the dispatch returns `null`, the belt runs a Claude reviewer, and
|
|
357
|
+
cross-family QE is silently lost — on exactly the big features that need it most.
|
|
358
|
+
|
|
359
|
+
- **Mode A (primary): `codex review`.** The scope comes from the DIFF, so Codex computes for free the
|
|
360
|
+
thing we were paying a model to do badly. Default scope `--uncommitted`; override with
|
|
361
|
+
`args.qeScope: 'commit' | 'base'` + `args.qeScopeRef`. Two CLI facts are encoded as refusals, both
|
|
362
|
+
measured: `codex review` rejects `-m` (exit 2 — the model goes through `-c model=`), and **every**
|
|
363
|
+
scope flag rejects a positional prompt (`--commit`, `--base` and `--uncommitted` alike, exit 2). So
|
|
364
|
+
mode A runs on Codex's built-in review instructions and cannot be asked our questions.
|
|
365
|
+
- **Mode B (follow-up): a NARROWED `codex exec`.** Carries our own questions over at most 3 named
|
|
366
|
+
files with the load-bearing clause *"read ONLY these files, do not explore the repository"*, and
|
|
367
|
+
ends with `Grade: <A|B|C|D>`. An unscoped mode-B prompt is not constructible: an empty file list
|
|
368
|
+
returns `''` and `codexExecPlan` refuses an unscoped `qe` prompt outright.
|
|
369
|
+
- **The verdict is parsed, never synthesised.** A `Grade: D` is a SUCCESSFUL cross-family review; a
|
|
370
|
+
review with no grade is a FAILED one. Mode A cannot state a letter, so its grade is DERIVED from the
|
|
371
|
+
severities it reported and labelled `gradeSource: 'derived-from-findings'` — and **zero findings
|
|
372
|
+
yields `null`, never `A`** (measured: `codex review` on a clean tree exits 0 with a polite, entirely
|
|
373
|
+
empty review; calling that an `A` would be a review of nothing reported as a pass).
|
|
374
|
+
- **Every fallback names its cause.** The reason carried into
|
|
375
|
+
`opus (cross-family QE DID NOT happen — …)` comes from a locked taxonomy —
|
|
376
|
+
`timeout` (narrow the scope) · `no-verdict` · `tool-error` (fix the invocation) · `unusable-output` ·
|
|
377
|
+
`unavailable` (fix the account/model) · `over-ceiling`. A timeout and an unusable output can never
|
|
378
|
+
render the same string, because the operator's next move differs.
|
|
379
|
+
- **The pipeline still never blocks on Codex.** Both modes fail into the same Claude belt as before.
|
|
380
|
+
|
|
381
|
+
After a Codex verdict a cheap Claude agent transcribes it into `08_qe_report.md` (mode A takes no
|
|
382
|
+
prompt, so the reviewer cannot be asked to write anything). It is a scribe, not a second reviewer: the
|
|
383
|
+
grade is Codex's and is stated as final.
|
|
384
|
+
|
|
210
385
|
**Precedence:** `args.models[stage]` wins; the legacy `planner`/`coder`/`qeReviewer`/`codexModel` knobs
|
|
211
386
|
fill only unspecified stages. `codexModel` seeds the id for a bare `'codex'` spec. gpt-5.6-ready: a new
|
|
212
387
|
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.0",
|
|
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": "59592f0b4612a14bdec61c96c20655644701cd77a0bf9d68315e1c98461900ff"
|
|
19
19
|
}
|
|
20
20
|
]
|
|
21
21
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"hashes": [
|
|
36
36
|
{
|
|
37
37
|
"alg": "SHA-256",
|
|
38
|
-
"content": "
|
|
38
|
+
"content": "8df52c2c7d7c70ef29670339ac01a6c0b0b6a36bb51a0be15dcc03357097cca8"
|
|
39
39
|
}
|
|
40
40
|
]
|
|
41
41
|
},
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"hashes": [
|
|
66
66
|
{
|
|
67
67
|
"alg": "SHA-256",
|
|
68
|
-
"content": "
|
|
68
|
+
"content": "0fc1ae047774c2c67c6acf77a88eef8c9532c609bfc68b9e9cb37591cb61158f"
|
|
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
|
},
|
|
@@ -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": "3c8838c6a5107ea8a4b909cd9ccfa4d416324bb4ed9341f50b8e23f1b569d244"
|
|
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 |
|