@dzhechkov/skills-feature-adr 1.3.63 → 1.3.65
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 -40
- package/README.md +33 -2
- package/bin/cli.js +0 -0
- package/package.json +5 -6
- package/sbom.json +7 -87
- package/templates/.claude/skills/explore/SKILL.md +1 -1
- package/templates/.claude/skills/feature-adr/SKILL.md +1 -1
- package/templates/.claude/skills/frontend-design/SKILL.md +1 -1
- package/templates/.claude/skills/problem-solver-enhanced/SKILL.md +1 -1
- package/templates/.claude/workflows/feature-adr.js +226 -14
package/.dz-manifest.json
CHANGED
|
@@ -3,38 +3,6 @@
|
|
|
3
3
|
"version": 1,
|
|
4
4
|
"pack": "skills-feature-adr",
|
|
5
5
|
"files": [
|
|
6
|
-
{
|
|
7
|
-
"path": ".agentic-qe/brain.rvf",
|
|
8
|
-
"sha256": "70f89fc8d429547e83bfae570908ab29d03310d0ea6f665041e9d234a613c9b3"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"path": ".agentic-qe/brain.rvf.lock",
|
|
12
|
-
"sha256": "dabbafbdcc8ebd25620e26be72e0416225740d0706e6d955d19d31beb5179a49"
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"path": ".agentic-qe/memory.db",
|
|
16
|
-
"sha256": "a9a1c9c9a16a4928935c1b9683ac06dab3d9a74dd4084e11c48f81dd95f9d9b6"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"path": ".agentic-qe/memory.db-shm",
|
|
20
|
-
"sha256": "e0d6e3d8739fca2b4e1d7c8f2c2287ca1e997f25278e57ca51b6fc8928404d06"
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"path": ".agentic-qe/memory.db-wal",
|
|
24
|
-
"sha256": "f4ffd1e7c99d271374117717ff4d5206c620180e3c36de6a6d445ff513b8bd05"
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
"path": ".agentic-qe/patterns.rvf",
|
|
28
|
-
"sha256": "69cfe471991b1d375cfbdcdb2df88226f86195659f7aa761581ca4d5f46ff24a"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"path": ".agentic-qe/patterns.rvf.idmap.json",
|
|
32
|
-
"sha256": "1f8838c958b471fe5d819d681d29b1965a75470a9e4d6a2171d6c7e620ba223e"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"path": ".agentic-qe/patterns.rvf.lock",
|
|
36
|
-
"sha256": "f7c13d34313c97974dc8ba868859d68201a2d6a2dcba6670d2e52ec66d010081"
|
|
37
|
-
},
|
|
38
6
|
{
|
|
39
7
|
"path": "CHANGELOG.md",
|
|
40
8
|
"sha256": "e4f50d4f757f877b54c8d25dfc1321776545be4cb814d5f17ee1128ee2320982"
|
|
@@ -45,7 +13,7 @@
|
|
|
45
13
|
},
|
|
46
14
|
{
|
|
47
15
|
"path": "README.md",
|
|
48
|
-
"sha256": "
|
|
16
|
+
"sha256": "4a887b8fe2a52c75be7189ddeef4393dc2a8a872d747bdbde3caba94488960dd"
|
|
49
17
|
},
|
|
50
18
|
{
|
|
51
19
|
"path": "bin/cli.js",
|
|
@@ -57,7 +25,7 @@
|
|
|
57
25
|
},
|
|
58
26
|
{
|
|
59
27
|
"path": "package.json",
|
|
60
|
-
"sha256": "
|
|
28
|
+
"sha256": "1f95669ddfb76990bfeb0e1be603c27651f47a9233020ed10c37e8705543b688"
|
|
61
29
|
},
|
|
62
30
|
{
|
|
63
31
|
"path": "scripts/sync-templates.js",
|
|
@@ -141,7 +109,7 @@
|
|
|
141
109
|
},
|
|
142
110
|
{
|
|
143
111
|
"path": "templates/.claude/skills/explore/SKILL.md",
|
|
144
|
-
"sha256": "
|
|
112
|
+
"sha256": "2473e7961845db0d64566cd42627b374d1830664b9e302058a1b1aa466bc7361"
|
|
145
113
|
},
|
|
146
114
|
{
|
|
147
115
|
"path": "templates/.claude/skills/explore/references/questioning-techniques.md",
|
|
@@ -153,7 +121,7 @@
|
|
|
153
121
|
},
|
|
154
122
|
{
|
|
155
123
|
"path": "templates/.claude/skills/feature-adr/SKILL.md",
|
|
156
|
-
"sha256": "
|
|
124
|
+
"sha256": "c422d1e2b50cf7b8bd56cd5d886d96af30d8bf33a468b925396f395ebf32de53"
|
|
157
125
|
},
|
|
158
126
|
{
|
|
159
127
|
"path": "templates/.claude/skills/feature-adr/examples/sample-feature-output.md",
|
|
@@ -293,7 +261,7 @@
|
|
|
293
261
|
},
|
|
294
262
|
{
|
|
295
263
|
"path": "templates/.claude/skills/frontend-design/SKILL.md",
|
|
296
|
-
"sha256": "
|
|
264
|
+
"sha256": "c9e93c1b95b99a9e120b93d7683afa804713f98ce44bf29ba6d24e9ea1b44aee"
|
|
297
265
|
},
|
|
298
266
|
{
|
|
299
267
|
"path": "templates/.claude/skills/knowledge-extractor/SKILL.md",
|
|
@@ -337,7 +305,7 @@
|
|
|
337
305
|
},
|
|
338
306
|
{
|
|
339
307
|
"path": "templates/.claude/skills/problem-solver-enhanced/SKILL.md",
|
|
340
|
-
"sha256": "
|
|
308
|
+
"sha256": "031378aa2e10c98928e35de07d454135e73550cd0c49f0cd886d53e4208d6b9f"
|
|
341
309
|
},
|
|
342
310
|
{
|
|
343
311
|
"path": "templates/.claude/skills/system-grill/JOURNAL-TEMPLATE.md",
|
|
@@ -349,7 +317,7 @@
|
|
|
349
317
|
},
|
|
350
318
|
{
|
|
351
319
|
"path": "templates/.claude/workflows/feature-adr.js",
|
|
352
|
-
"sha256": "
|
|
320
|
+
"sha256": "8afa77bb421ea4052eec0feab5f49edc1f2b1cea69238971f8ef1b3c8328a483"
|
|
353
321
|
},
|
|
354
322
|
{
|
|
355
323
|
"path": "templates/lib/memory-protocol.md",
|
|
@@ -377,5 +345,5 @@
|
|
|
377
345
|
}
|
|
378
346
|
]
|
|
379
347
|
},
|
|
380
|
-
"signature": "+
|
|
348
|
+
"signature": "3OT+p3Tm5tBnn9T2RKEPlCnhCiaemKAOraeoPA4zYR28p1MpACnkeEmVyXPpwnqhG/a6uD/AjC/DrRuihMdHCw=="
|
|
381
349
|
}
|
package/README.md
CHANGED
|
@@ -106,6 +106,32 @@ ARCHITECTURE → IMPLEMENTATION → CODE → QE → FLEET QE
|
|
|
106
106
|
# Full protocols + 6 extra skills, up to 7 fleet QE agents
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
### Durable checkpoints + resume (v1.3.64)
|
|
110
|
+
|
|
111
|
+
Every expensive stage (router / design / plan / code / qe / fleet) checkpoints its result into
|
|
112
|
+
`features/<slug>/.fa-state/checkpoints.jsonl`. Re-invoking the workflow with the SAME slug resumes
|
|
113
|
+
completed stages instead of re-running them — covering both a **killed run** (previously a total
|
|
114
|
+
restart: the exact failure mode that motivated usage-adaptive routing) and the **standard L/XL
|
|
115
|
+
two-phase flow** (the `stopAfter: 'plan'` → `stopAfter: 'none'` re-invoke now resumes
|
|
116
|
+
router+design+plan for free).
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
// run 1 (dies mid-code, or stops at the L/XL plan checkpoint)…
|
|
120
|
+
Workflow({ scriptPath: '.claude/workflows/feature-adr.js', args: { slug: 'add-x', description: '…' } })
|
|
121
|
+
// run 2 — same slug: completed stages resume, only the unfinished work runs
|
|
122
|
+
Workflow({ scriptPath: '.claude/workflows/feature-adr.js', args: { slug: 'add-x', description: '…', stopAfter: 'none' } })
|
|
123
|
+
// → log: "checkpoint: router RESUMED … design RESUMED … plan RESUMED"; result carries resumedStages
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Knobs: `args.resume: 'auto'` (default — resume only on input-hash match AND every tier-required
|
|
127
|
+
artifact present) · `'never'` (ignore recorded state) · `'force'` (trust the hash, skip artifact
|
|
128
|
+
probes); `args.checkpoints: false` disables the layer entirely. **Honest scope:** resume proves the
|
|
129
|
+
run *inputs* are unchanged and artifacts exist — it does NOT fingerprint the working tree; after
|
|
130
|
+
manual edits mid-feature pass `resume: 'never'` (or delete `.fa-state/`) and re-QE. A stale-input
|
|
131
|
+
checkpoint never resumes in any mode. RU: упавший L/XL-ран продолжает с места смерти, а не
|
|
132
|
+
пере-тратит выполненные стадии; штатный двухфазный L/XL-флоу тоже дешевеет — re-invoke после
|
|
133
|
+
план-чекпоинта возобновляет router+design+plan из чекпоинтов.
|
|
134
|
+
|
|
109
135
|
### Codex model routing (optional)
|
|
110
136
|
|
|
111
137
|
If [Codex](https://developers.openai.com/codex) is installed + logged in, the pipeline can route work to
|
|
@@ -248,8 +274,13 @@ Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
|
|
|
248
274
|
|
|
249
275
|
> Note: under the override, coder **and** QE both run on Codex — the cross-model self-QE guard is
|
|
250
276
|
> consciously suspended (a Claude QE reviewer is exactly the agent that dies under limit pressure). The
|
|
251
|
-
> ` (usage-switched)` suffix + `usageEvents` keep it auditable
|
|
252
|
-
>
|
|
277
|
+
> ` (usage-switched)` suffix + `usageEvents` keep it auditable — and since v1.3.65 the suspension is a
|
|
278
|
+
> **machine debt**, not a note: the run writes `features/<slug>/.fa-state/reqe-due.json` (idempotent,
|
|
279
|
+
> run-stamped, crash-resume-safe) and returns `reqeDue: true`; after limits reset, `dz usage` shows a
|
|
280
|
+
> `re-QE due:` line, `dz reqe --slug <s>` prints the cross-family review brief, and
|
|
281
|
+
> `dz reqe --slug <s> --done --report <f>` settles FAIL-CLOSED — only against an existing graded
|
|
282
|
+
> report, never the run's own 08_qe_report.md (real-path and inode compared). RU: «перепроверь другой
|
|
283
|
+
> семьёй потом» стало долгом на диске с полным жизненным циклом.
|
|
253
284
|
|
|
254
285
|
---
|
|
255
286
|
|
package/bin/cli.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/skills-feature-adr",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.65",
|
|
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"
|
|
@@ -15,10 +15,6 @@
|
|
|
15
15
|
".dz-manifest.json",
|
|
16
16
|
"sbom.json"
|
|
17
17
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"test": "node --test \"test/**/*.test.js\"",
|
|
20
|
-
"prepack": "node -e \"const fs=require('fs');const bad=['.claude','.skills-feature-adr.json'].filter(p=>fs.existsSync(p));if(bad.length){console.error('prepack guard: stray init artifacts in package dir: '+bad.join(', ')+' — remove before packing');process.exit(1)}\""
|
|
21
|
-
},
|
|
22
18
|
"keywords": [
|
|
23
19
|
"claude",
|
|
24
20
|
"claude-code",
|
|
@@ -63,5 +59,8 @@
|
|
|
63
59
|
},
|
|
64
60
|
"publishConfig": {
|
|
65
61
|
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"test": "node --test \"test/**/*.test.js\""
|
|
66
65
|
}
|
|
67
|
-
}
|
|
66
|
+
}
|
package/sbom.json
CHANGED
|
@@ -9,86 +9,6 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"components": [
|
|
12
|
-
{
|
|
13
|
-
"type": "file",
|
|
14
|
-
"name": ".agentic-qe/brain.rvf",
|
|
15
|
-
"hashes": [
|
|
16
|
-
{
|
|
17
|
-
"alg": "SHA-256",
|
|
18
|
-
"content": "70f89fc8d429547e83bfae570908ab29d03310d0ea6f665041e9d234a613c9b3"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"type": "file",
|
|
24
|
-
"name": ".agentic-qe/brain.rvf.lock",
|
|
25
|
-
"hashes": [
|
|
26
|
-
{
|
|
27
|
-
"alg": "SHA-256",
|
|
28
|
-
"content": "dabbafbdcc8ebd25620e26be72e0416225740d0706e6d955d19d31beb5179a49"
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"type": "file",
|
|
34
|
-
"name": ".agentic-qe/memory.db",
|
|
35
|
-
"hashes": [
|
|
36
|
-
{
|
|
37
|
-
"alg": "SHA-256",
|
|
38
|
-
"content": "a9a1c9c9a16a4928935c1b9683ac06dab3d9a74dd4084e11c48f81dd95f9d9b6"
|
|
39
|
-
}
|
|
40
|
-
]
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"type": "file",
|
|
44
|
-
"name": ".agentic-qe/memory.db-shm",
|
|
45
|
-
"hashes": [
|
|
46
|
-
{
|
|
47
|
-
"alg": "SHA-256",
|
|
48
|
-
"content": "e0d6e3d8739fca2b4e1d7c8f2c2287ca1e997f25278e57ca51b6fc8928404d06"
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"type": "file",
|
|
54
|
-
"name": ".agentic-qe/memory.db-wal",
|
|
55
|
-
"hashes": [
|
|
56
|
-
{
|
|
57
|
-
"alg": "SHA-256",
|
|
58
|
-
"content": "f4ffd1e7c99d271374117717ff4d5206c620180e3c36de6a6d445ff513b8bd05"
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"type": "file",
|
|
64
|
-
"name": ".agentic-qe/patterns.rvf",
|
|
65
|
-
"hashes": [
|
|
66
|
-
{
|
|
67
|
-
"alg": "SHA-256",
|
|
68
|
-
"content": "69cfe471991b1d375cfbdcdb2df88226f86195659f7aa761581ca4d5f46ff24a"
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
"type": "file",
|
|
74
|
-
"name": ".agentic-qe/patterns.rvf.idmap.json",
|
|
75
|
-
"hashes": [
|
|
76
|
-
{
|
|
77
|
-
"alg": "SHA-256",
|
|
78
|
-
"content": "1f8838c958b471fe5d819d681d29b1965a75470a9e4d6a2171d6c7e620ba223e"
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"type": "file",
|
|
84
|
-
"name": ".agentic-qe/patterns.rvf.lock",
|
|
85
|
-
"hashes": [
|
|
86
|
-
{
|
|
87
|
-
"alg": "SHA-256",
|
|
88
|
-
"content": "f7c13d34313c97974dc8ba868859d68201a2d6a2dcba6670d2e52ec66d010081"
|
|
89
|
-
}
|
|
90
|
-
]
|
|
91
|
-
},
|
|
92
12
|
{
|
|
93
13
|
"type": "file",
|
|
94
14
|
"name": "CHANGELOG.md",
|
|
@@ -115,7 +35,7 @@
|
|
|
115
35
|
"hashes": [
|
|
116
36
|
{
|
|
117
37
|
"alg": "SHA-256",
|
|
118
|
-
"content": "
|
|
38
|
+
"content": "4a887b8fe2a52c75be7189ddeef4393dc2a8a872d747bdbde3caba94488960dd"
|
|
119
39
|
}
|
|
120
40
|
]
|
|
121
41
|
},
|
|
@@ -145,7 +65,7 @@
|
|
|
145
65
|
"hashes": [
|
|
146
66
|
{
|
|
147
67
|
"alg": "SHA-256",
|
|
148
|
-
"content": "
|
|
68
|
+
"content": "1f95669ddfb76990bfeb0e1be603c27651f47a9233020ed10c37e8705543b688"
|
|
149
69
|
}
|
|
150
70
|
]
|
|
151
71
|
},
|
|
@@ -355,7 +275,7 @@
|
|
|
355
275
|
"hashes": [
|
|
356
276
|
{
|
|
357
277
|
"alg": "SHA-256",
|
|
358
|
-
"content": "
|
|
278
|
+
"content": "2473e7961845db0d64566cd42627b374d1830664b9e302058a1b1aa466bc7361"
|
|
359
279
|
}
|
|
360
280
|
]
|
|
361
281
|
},
|
|
@@ -385,7 +305,7 @@
|
|
|
385
305
|
"hashes": [
|
|
386
306
|
{
|
|
387
307
|
"alg": "SHA-256",
|
|
388
|
-
"content": "
|
|
308
|
+
"content": "c422d1e2b50cf7b8bd56cd5d886d96af30d8bf33a468b925396f395ebf32de53"
|
|
389
309
|
}
|
|
390
310
|
]
|
|
391
311
|
},
|
|
@@ -735,7 +655,7 @@
|
|
|
735
655
|
"hashes": [
|
|
736
656
|
{
|
|
737
657
|
"alg": "SHA-256",
|
|
738
|
-
"content": "
|
|
658
|
+
"content": "c9e93c1b95b99a9e120b93d7683afa804713f98ce44bf29ba6d24e9ea1b44aee"
|
|
739
659
|
}
|
|
740
660
|
]
|
|
741
661
|
},
|
|
@@ -845,7 +765,7 @@
|
|
|
845
765
|
"hashes": [
|
|
846
766
|
{
|
|
847
767
|
"alg": "SHA-256",
|
|
848
|
-
"content": "
|
|
768
|
+
"content": "031378aa2e10c98928e35de07d454135e73550cd0c49f0cd886d53e4208d6b9f"
|
|
849
769
|
}
|
|
850
770
|
]
|
|
851
771
|
},
|
|
@@ -875,7 +795,7 @@
|
|
|
875
795
|
"hashes": [
|
|
876
796
|
{
|
|
877
797
|
"alg": "SHA-256",
|
|
878
|
-
"content": "
|
|
798
|
+
"content": "8afa77bb421ea4052eec0feab5f49edc1f2b1cea69238971f8ef1b3c8328a483"
|
|
879
799
|
}
|
|
880
800
|
]
|
|
881
801
|
},
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
provide solutions until task is fully explored.
|
|
11
11
|
trust_tier: 1
|
|
12
12
|
trust_tier_label: "Structured"
|
|
13
|
-
trust_tier_path: "Run
|
|
13
|
+
trust_tier_path: "Run a BTO evaluation (see the skills-bto package) to promote to Tier 2"
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Explore: Adaptive Task Clarification
|
|
@@ -11,7 +11,7 @@ description: >
|
|
|
11
11
|
"implement feature", "/feature-adr".
|
|
12
12
|
trust_tier: 0
|
|
13
13
|
trust_tier_label: "Advisory"
|
|
14
|
-
trust_tier_path: "Run
|
|
14
|
+
trust_tier_path: "Run a BTO evaluation (see the skills-bto package) to promote to Tier 1"
|
|
15
15
|
agentic_qe_version: "7.5.1"
|
|
16
16
|
agentic_qe_source: "https://github.com/proffesor-for-testing/agentic-qe"
|
|
17
17
|
agentic_qe_skills_core: 9
|
|
@@ -4,7 +4,7 @@ description: Create distinctive, production-grade frontend interfaces with high
|
|
|
4
4
|
license: Complete terms in LICENSE.txt
|
|
5
5
|
trust_tier: 0
|
|
6
6
|
trust_tier_label: "Advisory"
|
|
7
|
-
trust_tier_path: "Add references/ directory and run
|
|
7
|
+
trust_tier_path: "Add references/ directory and run a BTO evaluation (see the skills-bto package) to promote to Tier 1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
|
@@ -3,7 +3,7 @@ name: problem-solver-enhanced
|
|
|
3
3
|
description: Elite problem-solving system combining first principles thinking, game theory, root cause analysis, systematic frameworks AND TRIZ inventive principles for solving complex challenges. Use when facing complex challenges with unclear solutions, strategic decisions with multiple stakeholders, problems requiring breakthrough (not incremental) solutions, trade-offs that seem impossible to resolve, competitive scenarios requiring game-theoretic analysis, system design problems, or any situation requiring both analytical rigor and inventive thinking beyond surface-level solutions.
|
|
4
4
|
trust_tier: 1
|
|
5
5
|
trust_tier_label: "Structured"
|
|
6
|
-
trust_tier_path: "Run
|
|
6
|
+
trust_tier_path: "Run a BTO evaluation (see the skills-bto package) to promote to Tier 2"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Elite Problem Solver (Enhanced with TRIZ)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
name: 'feature-adr',
|
|
3
|
-
description: 'Canonical /feature-adr --full-qe-extended pipeline as a reusable workflow: router+RECALL then design(ADR, applies learned patterns) then plan then code then agentic-qe QE+TEACH, producing features/<slug>/00-09 artifacts. MANDATORY in-process self-learning loop (Step-0 recall, apply, Step-8 teach). OPTIONAL Codex routing: args.planner=codex (Step-6), args.coder/qeReviewer=codex-fallback (Step-7/8 fall back to Codex when Claude limits exhaust; args.codexModel default auto, Codex self-selects top). Hybrid checkpoints (S/M autonomous; L/XL stop-after-plan).',
|
|
3
|
+
description: 'Canonical /feature-adr --full-qe-extended pipeline as a reusable workflow: router+RECALL then design(ADR, applies learned patterns) then plan then code then agentic-qe QE+TEACH, producing features/<slug>/00-09 artifacts. MANDATORY in-process self-learning loop (Step-0 recall, apply, Step-8 teach). OPTIONAL Codex routing: args.planner=codex (Step-6), args.coder/qeReviewer=codex-fallback (Step-7/8 fall back to Codex when Claude limits exhaust; args.codexModel default auto, Codex self-selects top). Hybrid checkpoints (S/M autonomous; L/XL stop-after-plan). DURABLE per-stage checkpoints in features/<slug>/.fa-state/ (args.resume auto|never|force, args.checkpoints:false to disable): a dead run or an L/XL re-invoke resumes completed stages instead of re-spending them.',
|
|
4
4
|
whenToUse: 'ultracode + a feature implementation. Invoke via Workflow({scriptPath:".claude/workflows/feature-adr.js", args:{slug, description, code, tier, stopAfter, planner, coder, qeReviewer, codexModel, brain}}) instead of an ad-hoc orchestration, so every feature ships with an ADR + inline agentic-qe QE + self-learning. args.brain pins the self-learning loop (recall/teach) to ONE canonical brain store (default = the workspace root) so lessons never fragment into a target repo when the coder cd`s away.',
|
|
5
5
|
phases: [
|
|
6
6
|
{ title: 'Router', detail: 'Step 0 - classify + self-learning recall' },
|
|
@@ -100,6 +100,110 @@ const DZ_RECALL = (terms) => 'cd ' + BRAIN + ' && ' + DZ + ' recall "' + terms +
|
|
|
100
100
|
const DZ_TEACH = (lesson, reward, domain) =>
|
|
101
101
|
'cd ' + BRAIN + ' && ' + DZ + ' teach "' + lesson + '" --reward ' + reward + ' --domain ' + domain + ' --project ' + BRAIN
|
|
102
102
|
|
|
103
|
+
// ── Durable checkpoints + resume (backlog 49e4a95b) — inline mirror of ──
|
|
104
|
+
// ── harness-core/src/feature-adr-checkpoints.ts (the workflow is self-contained, no imports) ──
|
|
105
|
+
// After each expensive stage a cheap effort-low agent appends {stage, inputHash, result} to
|
|
106
|
+
// features/<slug>/.fa-state/checkpoints.jsonl (the sandbox has no fs — the agent IS the fs). On the
|
|
107
|
+
// next run with the same slug, a stage is SKIPPED only when its recorded inputHash matches AND its
|
|
108
|
+
// expected artifact is still on disk. This covers BOTH the crash case (a dead L/XL session used to
|
|
109
|
+
// re-spend every completed stage) and the STANDARD L/XL two-phase flow (stop-after-plan → re-invoke
|
|
110
|
+
// used to re-run router+design+plan wholesale). Granularity is per-STAGE — a death mid-code re-runs
|
|
111
|
+
// the code stage only, never Steps 0–6. args.checkpoints:false disables everything; args.resume: 'auto'(default)
|
|
112
|
+
// | 'never' (ignore recorded state) | 'force' (trust the hash, skip the artifact probe). A STALE
|
|
113
|
+
// inputHash NEVER resumes in any mode — 'force' relaxes only the artifact probe (load-bearing,
|
|
114
|
+
// tested in feature-adr-checkpoints.test.ts).
|
|
115
|
+
const CHECKPOINTS_ON = A.checkpoints !== false
|
|
116
|
+
const RESUME_MODE = A.resume === 'never' ? 'never' : (A.resume === 'force' ? 'force' : 'auto')
|
|
117
|
+
const CKPT_FILE = FDIR + '/.fa-state/checkpoints.jsonl'
|
|
118
|
+
const CKPT_LS_SENTINEL = '---FA-CKPT-LS---'
|
|
119
|
+
const CKPT_MAX = 12000
|
|
120
|
+
// SALTED into every hash: bump when stage semantics/prompts/composite shapes change → every old
|
|
121
|
+
// checkpoint hashes stale and re-runs (never resumes into new logic).
|
|
122
|
+
const CKPT_SCHEMA_VERSION = 'fa-ckpt-2'
|
|
123
|
+
// HONEST SCOPE (the narrowed promise): the hash proves the run INPUTS are unchanged; the probe
|
|
124
|
+
// proves the stage artifacts EXIST. Neither fingerprints the working tree — a crash-resume
|
|
125
|
+
// legitimately sees the dead run's uncommitted writes, so a tree hash would invalidate every real
|
|
126
|
+
// resume. After MANUAL edits: resume:'never' or delete .fa-state/, and re-QE independently.
|
|
127
|
+
const STAGE_ARTIFACTS_CKPT = { router: null, design: '01_requirements.md', plan: '06_implementation_plan.md', code: '07_code_changes/change_manifest.md', qe: '08_qe_report.md', fleet: '09_fleet_qe_assessment.md' }
|
|
128
|
+
function fnv1a(str) { let h = 0x811c9dc5; for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0 } return h.toString(16).padStart(8, '0') }
|
|
129
|
+
// 64 bits from two FNV passes — a single 32-bit hash admits findable collisions (Codex QE #9).
|
|
130
|
+
function fnv1a64(str) { return fnv1a(str) + fnv1a('fa-ckpt-salt' + str) }
|
|
131
|
+
function ckptHash(stage, parts) { return fnv1a64(JSON.stringify([CKPT_SCHEMA_VERSION, stage].concat(parts.map(function (p) { return p === undefined ? null : p })))) }
|
|
132
|
+
function decideCheckpointResume(mode, entry, inputHash, artifactRel, listing) {
|
|
133
|
+
if (mode === 'never') return { resume: false, reason: 'mode-never' }
|
|
134
|
+
if (!entry || entry.result === null || entry.result === undefined) return { resume: false, reason: 'no-checkpoint' }
|
|
135
|
+
if (entry.inputHash !== inputHash) return { resume: false, reason: 'stale-input' }
|
|
136
|
+
if (mode === 'force') return { resume: true, reason: 'resumed-force' }
|
|
137
|
+
const required = artifactRel === null ? [] : (typeof artifactRel === 'string' ? [artifactRel] : artifactRel)
|
|
138
|
+
for (const rel of required) { if (!listing.has(rel)) return { resume: false, reason: 'artifact-missing' } }
|
|
139
|
+
return { resume: true, reason: 'resumed' }
|
|
140
|
+
}
|
|
141
|
+
let CKPT_ENTRIES = {}
|
|
142
|
+
let CKPT_LISTING = new Set()
|
|
143
|
+
const resumedStages = []
|
|
144
|
+
async function loadCheckpoints(phaseName) {
|
|
145
|
+
if (!CHECKPOINTS_ON) return
|
|
146
|
+
const readCmd = 'cat ' + shq(CKPT_FILE) + ' 2>/dev/null || true; echo ' + shq(CKPT_LS_SENTINEL) + '; cd ' + shq(FDIR) + ' 2>/dev/null && find . -maxdepth 2 -type f 2>/dev/null | sed "s|^\\./||" || true'
|
|
147
|
+
const readOut = await agent('Run EXACTLY this via Bash and return its stdout VERBATIM (it may be empty) with NO code fences and NO commentary: ' + readCmd, { label: 'ckpt:read', phase: phaseName, effort: 'low' })
|
|
148
|
+
const raw = String(readOut == null ? '' : readOut)
|
|
149
|
+
// LINE-ANCHORED sentinel: a sentinel string INSIDE a recorded result shares its line with JSON
|
|
150
|
+
// syntax (stringify never emits raw newlines) and can never split the stream (Codex QE #10).
|
|
151
|
+
const allLines = raw.split('\n')
|
|
152
|
+
const sentinelAt = allLines.findIndex(function (l) { return l.trim() === CKPT_LS_SENTINEL })
|
|
153
|
+
const body = sentinelAt === -1 ? allLines : allLines.slice(0, sentinelAt)
|
|
154
|
+
const ls = sentinelAt === -1 ? [] : allLines.slice(sentinelAt + 1)
|
|
155
|
+
let malformed = 0
|
|
156
|
+
for (const line of body) {
|
|
157
|
+
const t = line.trim()
|
|
158
|
+
if (t === '') continue
|
|
159
|
+
try {
|
|
160
|
+
const e = JSON.parse(t)
|
|
161
|
+
if (e && typeof e === 'object' && typeof e.stage === 'string' && typeof e.inputHash === 'string' && ('result' in e) && e.result !== null && e.result !== undefined) CKPT_ENTRIES[e.stage] = e
|
|
162
|
+
else { if (e && typeof e === 'object' && typeof e.stage === 'string') delete CKPT_ENTRIES[e.stage]; malformed++ }
|
|
163
|
+
} catch (err) { malformed++ }
|
|
164
|
+
}
|
|
165
|
+
for (const line of ls) { const t = line.trim(); if (t !== '') CKPT_LISTING.add(t) }
|
|
166
|
+
const found = Object.keys(CKPT_ENTRIES)
|
|
167
|
+
if (malformed > 0) log('checkpoints: ' + malformed + ' malformed/null line(s) skipped (named, never silent — an all-malformed file resumes nothing)')
|
|
168
|
+
if (found.length > 0) log('checkpoints: ' + found.length + ' recorded stage(s) [' + found.join(', ') + '] (resume=' + RESUME_MODE + '; resume verifies INPUTS + artifact presence, NOT current-tree equivalence — after manual edits use resume:"never")')
|
|
169
|
+
}
|
|
170
|
+
async function withCheckpoint(stage, phaseName, inputHash, runFn, ckptOpts) {
|
|
171
|
+
const o = ckptOpts || {}
|
|
172
|
+
// artifacts override (Codex QE #2): tier-dependent stages pass EVERY artifact the tier requires
|
|
173
|
+
const artifactRel = (o.artifacts !== undefined) ? o.artifacts : ((stage in STAGE_ARTIFACTS_CKPT) ? STAGE_ARTIFACTS_CKPT[stage] : null)
|
|
174
|
+
let entry = CKPT_ENTRIES[stage]
|
|
175
|
+
// composite-shape validation (Codex QE #8): an old/partial composite must read as no-checkpoint
|
|
176
|
+
if (entry && typeof o.validate === 'function' && !o.validate(entry.result)) {
|
|
177
|
+
log('checkpoint: ' + stage + ' recorded result has an unexpected shape — treating as no checkpoint')
|
|
178
|
+
entry = undefined
|
|
179
|
+
}
|
|
180
|
+
const d = CHECKPOINTS_ON ? decideCheckpointResume(RESUME_MODE, entry, inputHash, artifactRel, CKPT_LISTING) : { resume: false, reason: 'checkpoints-off' }
|
|
181
|
+
if (d.resume) {
|
|
182
|
+
resumedStages.push(stage)
|
|
183
|
+
log('checkpoint: ' + stage + ' RESUMED (' + d.reason + ') — stage skipped, recorded result restored (inputs+artifacts verified; current-tree equivalence is NOT — the named limitation)')
|
|
184
|
+
return entry.result
|
|
185
|
+
}
|
|
186
|
+
if (CHECKPOINTS_ON && CKPT_ENTRIES[stage] && (d.reason === 'stale-input' || d.reason === 'artifact-missing')) log('checkpoint: ' + stage + ' NOT resumed (' + d.reason + ') — running live')
|
|
187
|
+
const result = await runFn()
|
|
188
|
+
// never checkpoint a dead/partial stage: null/undefined, or a parallel() array holding any null
|
|
189
|
+
const partial = Array.isArray(result) && result.some(function (x) { return x === null || x === undefined })
|
|
190
|
+
const persistable = (typeof o.persist === 'function') ? (result !== null && result !== undefined && o.persist(result)) : true
|
|
191
|
+
if (CHECKPOINTS_ON && result !== null && result !== undefined && !partial && persistable) {
|
|
192
|
+
let line = null
|
|
193
|
+
try { line = JSON.stringify({ stage: stage, inputHash: inputHash, result: result }) } catch (err) { line = null }
|
|
194
|
+
if (line && line.length <= CKPT_MAX) {
|
|
195
|
+
await agent('Run EXACTLY this one shell command via your Bash tool and reply with only OK: mkdir -p ' + shq(FDIR + '/.fa-state') + " && printf '%s\\n' " + shq(line) + ' >> ' + shq(CKPT_FILE), { label: 'ckpt:write:' + stage, phase: phaseName, effort: 'low' })
|
|
196
|
+
} else if (line) {
|
|
197
|
+
log('checkpoint: ' + stage + ' result oversize (' + line.length + ' > ' + CKPT_MAX + ' chars) — not checkpointed; the stage will re-run on resume (honest cost, never truncated state)')
|
|
198
|
+
} else {
|
|
199
|
+
log('checkpoint: ' + stage + ' result not serializable — not checkpointed')
|
|
200
|
+
}
|
|
201
|
+
} else if (CHECKPOINTS_ON && result !== null && result !== undefined && !partial && !persistable) {
|
|
202
|
+
log('checkpoint: ' + stage + ' result NOT persisted (persist predicate refused — e.g. a not-landed codex code stage must never resume as landed)')
|
|
203
|
+
}
|
|
204
|
+
return result
|
|
205
|
+
}
|
|
206
|
+
|
|
103
207
|
// ── Codex-routing knobs (hoisted so the routing block below can fold them) ──
|
|
104
208
|
// CODER/QE_REVIEWER ∈ 'claude'|'codex'|'codex-fallback'. On 'codex-fallback' the Claude agent runs
|
|
105
209
|
// FIRST; if it returns null (e.g. the Claude Code session limit is exhausted mid-code/mid-QE), the SAME
|
|
@@ -575,12 +679,18 @@ const AMENDMENT_GATE = 'AMENDMENT GATE (P2): read the `## Amendments` sections o
|
|
|
575
679
|
|
|
576
680
|
// Step 0: Router + MANDATORY self-learning recall
|
|
577
681
|
phase('Router')
|
|
682
|
+
await loadCheckpoints('Router')
|
|
578
683
|
await usageProbe('Router')
|
|
579
684
|
const routerPrompt = 'You are Step 0 (Complexity Router) of the /feature-adr pipeline. TWO jobs. (1) MANDATORY SELF-LEARNING RECALL (never skip — run BOTH Bash commands VERBATIM, do not summarize instead of running them): the learned patterns live in the CANONICAL BRAIN store at `' + BRAIN + '` — pin every recall to it. Via your Bash tool run EXACTLY `' + DZ_RECALL('<the key domain terms of this feature>') + '` (and `' + DZ_RECALL('<the key domain terms of this feature>') + ' --all` if narrow) to load relevant LEARNED PATTERNS from the brain. Preserve recalled pattern TEXT, reward, domain, and any visible id in the rationale as a concrete list so Step 8 can compare candidate lessons against it. Then run `dz statusline --fa-record --slug ' + SLUG + ' --step "Step 0 recall" --recalled <count> --mode ' + MODE + ' --project ' + REPO + '`. Summarize the top 3 applicable patterns in the rationale. (2) Classify S/M/L/XL + active steps. Feature: "' + DESC + '". Code: ' + CODE_HINT + '. S=1-3 files (0,1,6,7,8; if an ADR is explicitly forced, use Nygard as the lightweight fallback); M=4-10 (0,1,3,3.5,5,6,7,8; Nygard/ITD-light ADR); L=11-30 (all+9; MADR+Confirmation ADRs); XL=30+ (full+9; MADR+Confirmation ADRs). ADR template-weight rule: S/M -> Nygard/ITD-light; L/XL -> MADR + NHS Wales Confirmation, while every generated ADR still carries the invariant core. Return {tier, activeSteps, rationale} with the recalled patterns folded into rationale.'
|
|
580
685
|
const routerModel = resolveStageModel('router')
|
|
581
686
|
const routerOpts = mergeOpts({ label: stageLabel('router+recall', routerModel), phase: 'Router', schema: ROUTER, effort: 'low' }, routerModel)
|
|
582
687
|
modelsUsed.router = modelLabel(routerOpts)
|
|
583
|
-
|
|
688
|
+
// router checkpoint: its result (tier + recalled-pattern rationale) seeds every downstream hash.
|
|
689
|
+
// The Step-0 RECALL is part of the stage — a resumed router restores the SAME recalled patterns the
|
|
690
|
+
// original run applied (fresh lessons taught since then enter on the next live run, not mid-resume).
|
|
691
|
+
const routerHash = ckptHash('router', [DESC, CODE_HINT, MODE, A.tier === undefined ? null : A.tier, BRAIN, MODELS.router === undefined ? null : MODELS.router, CODEX_MODEL, usageOverride])
|
|
692
|
+
const router = await withCheckpoint('router', 'Router', routerHash, async () => agent(routerPrompt + codexEffortHint(routerOpts), routerOpts))
|
|
693
|
+
if (resumedStages.indexOf('router') !== -1) modelsUsed.router = modelsUsed.router + ' (resumed)'
|
|
584
694
|
let tier = A.tier || (router ? router.tier : 'M')
|
|
585
695
|
const LEARNED = router ? router.rationale : 'none recalled'
|
|
586
696
|
const isMplus = tier === 'M' || tier === 'L' || tier === 'XL'
|
|
@@ -620,7 +730,7 @@ if (autoCostStages.length > 0) {
|
|
|
620
730
|
// GUARANTEED fa-panel write (the router, being low-effort + multi-job, tends to skip the fa-record
|
|
621
731
|
// Bash call). A dedicated single-command agent reliably lights up the live /feature-adr panel at the
|
|
622
732
|
// most visible moment. Uses the workspace bin (PATH-independent). Best-effort — never blocks.
|
|
623
|
-
await agent('Run EXACTLY this one shell command via your Bash tool and report its stdout verbatim — do nothing else, do not summarize: ' + DZ + ' statusline --fa-record --slug ' + SLUG + ' --step "Step 0 recall" --recalled 3 --stored 0 --mode ' + MODE + ' --project ' + REPO, { label: 'fa-record:step0', phase: 'Router', effort: 'low' })
|
|
733
|
+
if (resumedStages.indexOf('router') === -1) await agent('Run EXACTLY this one shell command via your Bash tool and report its stdout verbatim — do nothing else, do not summarize: ' + DZ + ' statusline --fa-record --slug ' + SLUG + ' --step "Step 0 recall" --recalled 3 --stored 0 --mode ' + MODE + ' --project ' + REPO, { label: 'fa-record:step0', phase: 'Router', effort: 'low' })
|
|
624
734
|
|
|
625
735
|
// R1 product-architecture-lens (ADR-001 Decision 3): forward-looking сверка of THIS feature vs the LIVE
|
|
626
736
|
// product map + vision. NON-BLOCKING/soft by design — it LOGS {signal,confidence} so a real command
|
|
@@ -672,7 +782,17 @@ if (isMplus) {
|
|
|
672
782
|
const archExtra = isLplus ? ' Also ' + FDIR + '/04_domain_model.md (DDD).' : ''
|
|
673
783
|
designThunks.push(() => designStage((isLplus ? 'Step 4 (DDD) + ' : '') + 'Step 5 (Architecture) of /feature-adr for "' + DESC + '" (' + SLUG + '). READ the code. Write ' + FDIR + '/05_architecture.md (components, data flow, integration points, the emit/merge/wiring shape).' + archExtra + ' Return wrote[] + summary.', archOpts, FDIR + '/05_architecture.md', 'architecture'))
|
|
674
784
|
}
|
|
675
|
-
|
|
785
|
+
// design checkpoint: ONE composite entry for the whole parallel design fan (requirements/ADR/QCSD/
|
|
786
|
+
// architecture). A partial fan (any null) is never checkpointed — resume must not restore half a
|
|
787
|
+
// design. The hash carries EVERY input that steers the fan (Codex QE #4): tier, models, the recalled
|
|
788
|
+
// patterns, the project-skills manifest state, the usage override, and CODEX_MODEL (a bare 'codex'
|
|
789
|
+
// model spec resolves through it). The artifact probe requires EVERY tier-active design artifact
|
|
790
|
+
// (Codex QE #2 — a one-file probe accepted a design missing its ADR/architecture).
|
|
791
|
+
const designHash = ckptHash('design', [tier, DESC, CODE_HINT, fnv1a64(String(LEARNED)), MODELS.requirements === undefined ? null : MODELS.requirements, MODELS.adr === undefined ? null : MODELS.adr, MODELS.ideation === undefined ? null : MODELS.ideation, MODELS.architecture === undefined ? null : MODELS.architecture, POLY.hasManifest, fnv1a64(String(POLY.report || '')), usageOverride, CODEX_MODEL])
|
|
792
|
+
const designArtifacts = ['01_requirements.md']
|
|
793
|
+
if (isMplus) designArtifacts.push('03_adr/001-' + SLUG + '.md', '03.5_ideation_report.md', '05_architecture.md')
|
|
794
|
+
if (isLplus) designArtifacts.push('02_research.md', '04_domain_model.md')
|
|
795
|
+
const design = await withCheckpoint('design', 'Design', designHash, async () => parallel(designThunks), { artifacts: designArtifacts })
|
|
676
796
|
|
|
677
797
|
// Step 6: Plan — optionally routed to Codex's top model (opt-in via args.planner='codex').
|
|
678
798
|
// The user opts in at pre-flight ('use the top Codex model for planning?'); we route the Plan step to
|
|
@@ -685,6 +805,11 @@ const planPrompt = 'Step 6 (SPARC-GOAP implementation plan) of /feature-adr for
|
|
|
685
805
|
// DEFAULT_MODELS/coder-fold) or the DEFAULT_MODELS.plan ('sonnet') under routing; else {} (BC).
|
|
686
806
|
const planModel = resolveStageModel('plan')
|
|
687
807
|
const planIsCodex = (planModel.agentType === 'codex:codex-rescue') || (MODELS.plan === undefined && PLANNER === 'codex')
|
|
808
|
+
// plan checkpoint: keyed on the design fan's RESULT (a stale design invalidates the plan) + the
|
|
809
|
+
// planner spec. Covers the standard L/XL two-phase flow: the stop-after-plan re-invoke resumes
|
|
810
|
+
// router+design+plan instead of re-running them.
|
|
811
|
+
const planHash = ckptHash('plan', [tier, DESC, fnv1a64(JSON.stringify(design === undefined ? null : design)), PLANNER, MODELS.plan === undefined ? null : MODELS.plan, CODEX_MODEL, usageOverride])
|
|
812
|
+
let plan = await withCheckpoint('plan', 'Plan', planHash, async () => {
|
|
688
813
|
let plan = null
|
|
689
814
|
if (planIsCodex) {
|
|
690
815
|
const planCodexLabelOpts = (planModel.agentType === 'codex:codex-rescue') ? planModel : specToOpts('codex:' + CODEX_MODEL + ':high')
|
|
@@ -709,6 +834,9 @@ if (plan === null) {
|
|
|
709
834
|
const claudePlan = await agent(planPrompt, claudePlanOpts)
|
|
710
835
|
plan = claudePlan ? { wrote: claudePlan.wrote, summary: claudePlan.summary, planner: planIsCodex ? 'claude-fallback' : 'claude' } : null
|
|
711
836
|
}
|
|
837
|
+
return plan
|
|
838
|
+
})
|
|
839
|
+
if (resumedStages.indexOf('plan') !== -1) modelsUsed.plan = (plan && plan.planner ? String(plan.planner) : 'plan') + ' (resumed)'
|
|
712
840
|
|
|
713
841
|
// ── R6 challenge panel: adversarial plan-gate at the checkpoint (ADVISE, never block) ──
|
|
714
842
|
// The panel is NEVER the plan's own author (ADR §1 hard invariant): author=Claude → a cross-family Codex
|
|
@@ -794,7 +922,7 @@ if (stopHere) {
|
|
|
794
922
|
// P4 (checkpoint-gate-line): a DERIVED gates map — each entry comes from machine state (artifact/verdict
|
|
795
923
|
// presence), never from prose, so a skipped gate shows as 'not-run' instead of being silently forgotten.
|
|
796
924
|
const planGates = { plan: (plan ? 'produced' : 'missing'), challengePanel: (challengeVerdict ? 'ran' : 'not-run'), code: 'not-run', qe: 'not-run' }
|
|
797
|
-
return { tier: tier, phase: 'checkpoint-after-plan', artifactsDir: FDIR, planner: (plan ? plan.planner : null), plan: (plan ? plan.summary : null), modelsUsed: plannedModels, challengeVerdict: challengeVerdict, gates: planGates, usageEvents: usageEvents, usageThreshold: USAGE_THRESHOLD, polymorphism: POLY.hasManifest ? POLY.report : null, note: 'L/XL checkpoint - review the ADR + plan (+ the planned code/qe/fleet models) + the challenge panel verdict (advisory) + the gates line, then re-invoke with args.stopAfter="none" to implement + QE. Present the gates map as a `🚦 Gates:` line in the checkpoint banner.' }
|
|
925
|
+
return { tier: tier, phase: 'checkpoint-after-plan', artifactsDir: FDIR, planner: (plan ? plan.planner : null), plan: (plan ? plan.summary : null), modelsUsed: plannedModels, challengeVerdict: challengeVerdict, gates: planGates, resumedStages: resumedStages, checkpointing: CHECKPOINTS_ON ? RESUME_MODE : 'off', usageEvents: usageEvents, usageThreshold: USAGE_THRESHOLD, polymorphism: POLY.hasManifest ? POLY.report : null, note: 'L/XL checkpoint - review the ADR + plan (+ the planned code/qe/fleet models) + the challenge panel verdict (advisory) + the gates line, then re-invoke with args.stopAfter="none" to implement + QE (durable checkpoints make the re-invoke resume router+design+plan instead of re-running them). Present the gates map as a `🚦 Gates:` line in the checkpoint banner.' }
|
|
798
926
|
}
|
|
799
927
|
|
|
800
928
|
// Step 7: Code (optional Codex fallback on Claude-limit exhaustion)
|
|
@@ -809,6 +937,12 @@ const codeModel = resolveStageModel('code')
|
|
|
809
937
|
const codeIsCodexFirst = (MODELS.code !== undefined) ? (codeModel.agentType === 'codex:codex-rescue') : (CODER === 'codex')
|
|
810
938
|
const codeClaudeModel = codeIsCodexFirst ? {} : (codeModel.agentType ? {} : codeModel)
|
|
811
939
|
const codeClaudeOpts = mergeOpts({ label: stageLabel('code', codeClaudeModel), phase: 'Code', schema: ARTIFACT, effort: 'high' }, codeClaudeModel)
|
|
940
|
+
// code checkpoint: COMPOSITE — the branchy claude/codex/fallback block sets three interdependent
|
|
941
|
+
// values (code result, coderUsed, codexCodeText); resume must restore all of them together or the
|
|
942
|
+
// barrier/QE/auto-cost logic downstream would see an inconsistent trio. codexCodeText is capped for
|
|
943
|
+
// the checkpoint (it only feeds the expected-targets parse, already consumed by the original run).
|
|
944
|
+
const codeHash = ckptHash('code', [tier, DESC, fnv1a64(JSON.stringify(plan === undefined ? null : plan)), CODER, MODELS.code === undefined ? null : MODELS.code, CODEX_MODEL, POLY.hasManifest, fnv1a64(String(POLY.report || '')), usageOverride])
|
|
945
|
+
const codeStage = await withCheckpoint('code', 'Code', codeHash, async () => {
|
|
812
946
|
let code = null
|
|
813
947
|
let coderUsed = 'claude'
|
|
814
948
|
let codexCodeText = ''
|
|
@@ -825,11 +959,10 @@ if (code === null && (codeIsCodexFirst || CODER === 'codex-fallback')) {
|
|
|
825
959
|
const codexCode = await safeCodexAgent(codePrompt + CODEX_HINT + codexEffortHint(codeCodexOpts) + codexExpectedTargetsHint, codeCodexOpts)
|
|
826
960
|
if (codexCode) { codexCodeText = String(codexCode); code = { wrote: [FDIR + '/07_code_changes/change_manifest.md'], summary: codexCodeText.slice(0, 500) }; coderUsed = codeIsCodexFirst ? 'codex' : 'codex-fallback'; modelsUsed.code = modelLabel(codeCodexLabelOpts) }
|
|
827
961
|
}
|
|
828
|
-
|
|
829
|
-
// Step 7.5
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
// known. Claude-coded runs are synchronous → this barrier is skipped with zero target parsing/probe work.
|
|
962
|
+
if (code === null) return null
|
|
963
|
+
// Step 7.5 landing barrier runs INSIDE the checkpointed stage (Codex QE #3): the checkpoint may
|
|
964
|
+
// only exist AFTER the barrier confirmed the out-of-band Codex edits landed — a crash between
|
|
965
|
+
// code-return and barrier must leave NO resumable code checkpoint.
|
|
833
966
|
let landedNote = ''
|
|
834
967
|
if (needsCodeLandedBarrier(coderUsed)) {
|
|
835
968
|
const expectedCodeTargets = extractExpectedCodeTargets(A.expectedCodeTargets, codexCodeText)
|
|
@@ -838,6 +971,23 @@ if (needsCodeLandedBarrier(coderUsed)) {
|
|
|
838
971
|
const probe = await agent('Confirm the Codex Step-7 edits have LANDED in the working tree BEFORE QE runs (Codex writes out-of-band). Expected-file mode must be satisfied by one of the declared expected paths; unrelated dirty files do not count in that mode. Run EXACTLY this via Bash and return its stdout verbatim, nothing else:\n' + barrierCmd, { label: 'code:confirm-landed', phase: 'Code' })
|
|
839
972
|
landedNote = '\n\nCODEX-CODED (out-of-band): Step 7.5 landing barrier used mode=' + barrierPlan.mode + ', window=' + barrierPlan.pollWindowSeconds + 's. Review the signal below. If status=landed, read the listed files and do NOT report "Step 7 never ran". Only status=genuinely-not-landed with "' + barrierPlan.emptySignal + '" means the bounded barrier found no intended code after the full window.\nExpected code targets: ' + (barrierPlan.expectedPaths.length ? barrierPlan.expectedPaths.join(', ') : '(none declared; fallback accepts any non-pipeline code change)') + '\n' + String(probe || '(landed-probe failed)').slice(0, 1500)
|
|
840
973
|
}
|
|
974
|
+
return { code: code, coderUsed: coderUsed, codexCodeText: String(codexCodeText).slice(0, 4000), modelUsed: modelsUsed.code, landedNote: landedNote }
|
|
975
|
+
}, { validate: function (r) { return !!(r && typeof r === 'object' && r.code && typeof r.code === 'object' && typeof r.coderUsed === 'string' && typeof r.landedNote === 'string') }, persist: function (r) { return !/genuinely-not-landed/.test(String(r.landedNote || '')) } })
|
|
976
|
+
let code = codeStage ? codeStage.code : null
|
|
977
|
+
let coderUsed = codeStage ? codeStage.coderUsed : 'claude'
|
|
978
|
+
let codexCodeText = codeStage ? codeStage.codexCodeText : ''
|
|
979
|
+
if (codeStage && codeStage.modelUsed) modelsUsed.code = codeStage.modelUsed + (resumedStages.indexOf('code') !== -1 ? ' (resumed)' : '')
|
|
980
|
+
|
|
981
|
+
// Step 7.5: Codex-landed barrier. Codex applies edits OUT-OF-BAND via its own runtime; without this,
|
|
982
|
+
// Step-8 QE can read the tree before the async write flushes and false-grade "Step 7 never ran" on real
|
|
983
|
+
// completed code. Poll a bounded 120s backing-off window, preferring declared expected code targets when
|
|
984
|
+
// known. Claude-coded runs are synchronous → this barrier is skipped with zero target parsing/probe work.
|
|
985
|
+
// landedNote comes FROM the checkpointed composite (the barrier ran inside the code stage — QE #3):
|
|
986
|
+
// on resume the ORIGINAL barrier signal is restored verbatim with a resumed marker prepended.
|
|
987
|
+
let landedNote = codeStage ? String(codeStage.landedNote || '') : ''
|
|
988
|
+
if (resumedStages.indexOf('code') !== -1 && landedNote !== '') {
|
|
989
|
+
landedNote = '\n\n[RESUMED from checkpoint — the landing barrier below ran in the ORIGINAL run; the change-manifest artifact was re-verified present by the resume probe]' + landedNote
|
|
990
|
+
}
|
|
841
991
|
|
|
842
992
|
// Step 8: QE (brutal-honesty, agentic-qe) + MANDATORY teach
|
|
843
993
|
phase('QE')
|
|
@@ -852,9 +1002,18 @@ const qeModel = resolveStageModel('qe')
|
|
|
852
1002
|
// Single tested source of truth (feature-adr-routing.ts:qeShouldUseCodex) — closes the self-QE hole where
|
|
853
1003
|
// the legacy qeReviewer='codex' knob used to re-route QE back to codex even when the CODER was codex.
|
|
854
1004
|
if (MODELS.qe === undefined && QE_REVIEWER === 'codex' && coderIsCodex()) log('QE: coder is codex — enforcing cross-model Claude QE (ignoring qeReviewer=codex to avoid self-review)')
|
|
855
|
-
|
|
1005
|
+
// reqe QE #3: the usage override can resolve the QE stage to codex EVEN when qeShouldUseCodex()
|
|
1006
|
+
// (which reads the pre-override config) says claude — dispatching that through the "claude" branch
|
|
1007
|
+
// would run the stub wrapper AND mislabel qeReviewerUsed='claude' (a codex-on-codex review recorded
|
|
1008
|
+
// as cross-family). The resolved agentType is the truth; either signal routes the codex branch.
|
|
1009
|
+
const qeIsCodex = qeShouldUseCodex() || (qeModel && qeModel.agentType === 'codex:codex-rescue')
|
|
856
1010
|
const qeClaudeModel = qeIsCodex ? {} : qeModel
|
|
857
1011
|
const qeClaudeOpts = mergeOpts({ label: stageLabel('qe:brutal', qeClaudeModel), phase: 'QE', agentType: 'qe-code-reviewer', schema: QE }, qeClaudeModel)
|
|
1012
|
+
// qe checkpoint: COMPOSITE (verdict + reviewer identity) keyed on the CODE stage's result — a re-coded
|
|
1013
|
+
// feature always re-QEs. The teach/fa-record side effects belong to the stage: a resumed QE does not
|
|
1014
|
+
// re-teach (the original run already stored its lessons — replaying teach would double-store).
|
|
1015
|
+
const qeHash = ckptHash('qe', [fnv1a64(JSON.stringify(codeStage === undefined ? null : codeStage)), tier, DESC, QE_REVIEWER, MODELS.qe === undefined ? null : MODELS.qe, CODEX_MODEL, coderUsed, POLY.hasManifest, fnv1a64(String(POLY.report || '')), usageOverride])
|
|
1016
|
+
const qeStage = await withCheckpoint('qe', 'QE', qeHash, async () => {
|
|
858
1017
|
let qe = null
|
|
859
1018
|
let qeReviewerUsed = 'claude'
|
|
860
1019
|
if (!qeIsCodex) {
|
|
@@ -890,17 +1049,63 @@ if (qe === null && qeIsCodex) {
|
|
|
890
1049
|
qe = await agent(qePrompt, qeBeltOpts)
|
|
891
1050
|
if (qe) { qeReviewerUsed = 'claude'; modelsUsed.qe = modelLabel(qeBeltOpts) }
|
|
892
1051
|
}
|
|
1052
|
+
if (qe === null) return null
|
|
1053
|
+
return { qe: qe, qeReviewerUsed: qeReviewerUsed, modelUsed: modelsUsed.qe }
|
|
1054
|
+
}, { validate: function (r) { return !!(r && typeof r === 'object' && r.qe && typeof r.qe === 'object' && typeof r.qeReviewerUsed === 'string') } })
|
|
1055
|
+
let qe = qeStage ? qeStage.qe : null
|
|
1056
|
+
let qeReviewerUsed = qeStage ? qeStage.qeReviewerUsed : 'claude'
|
|
1057
|
+
if (qeStage && qeStage.modelUsed) modelsUsed.qe = qeStage.modelUsed + (resumedStages.indexOf('qe') !== -1 ? ' (resumed)' : '')
|
|
893
1058
|
|
|
894
1059
|
// Step 8 claim-gate: fold the QE agent's reported claim-check counts into an additive result field.
|
|
895
1060
|
const claimGate = step8ClaimGate(qe && qe.claimCheck ? qe.claimCheck : null)
|
|
896
1061
|
log(claimGate.note)
|
|
897
1062
|
|
|
1063
|
+
// ── re-QE debt emission (backlog 6b40e667) — mirror of harness-core/src/reqe.ts ──
|
|
1064
|
+
// The cross-model guard was consciously SUSPENDED when the usage override made coder and QE the
|
|
1065
|
+
// same family (FR-2.9). Record that as a machine DEBT (features/<slug>/.fa-state/reqe-due.json) so
|
|
1066
|
+
// `dz reqe` / `dz usage` surface it after limits reset — a doc instruction on the weakest detection
|
|
1067
|
+
// layer becomes a fact on disk. Emitted ONLY for the actual same-family-under-override case: a
|
|
1068
|
+
// switch that kept cross-family QE, or the codex-unavailable Claude belt (no override), creates no
|
|
1069
|
+
// debt. A RESUMED qe never re-emits (the original run emitted; a settlement must not be clobbered).
|
|
1070
|
+
let reqeDue = false
|
|
1071
|
+
{
|
|
1072
|
+
const reqeFamOf = function (s) { return /codex|gpt|openai/i.test(String(s || '')) ? 'openai' : 'claude' }
|
|
1073
|
+
const qeLabel = String(modelsUsed.qe || '')
|
|
1074
|
+
if (/\(usage-switched\)/.test(qeLabel) && reqeFamOf(coderUsed) === reqeFamOf(qeReviewerUsed)) {
|
|
1075
|
+
reqeDue = true
|
|
1076
|
+
const reqeDebt = { schema: 'reqe-due-1', slug: SLUG, coderFamily: reqeFamOf(coderUsed), qeFamily: reqeFamOf(qeReviewerUsed), qeGrade: (qe && qe.grade) ? String(qe.grade) : null, reason: 'usage-switched self-review: Step-8 QE ran on the coder’s own family under the limit override (FR-2.9)', emittedAt: null, runStamp: qeHash }
|
|
1077
|
+
// IDEMPOTENT + VERIFIED (reqe QE #2 + r2 #2/#3): a RESUMED qe re-runs this block (the original
|
|
1078
|
+
// run may have died between the qe checkpoint and this write), but: an existing due file is
|
|
1079
|
+
// never clobbered; a settlement blocks re-emission ONLY when it carries THIS run's runStamp (an
|
|
1080
|
+
// OLD settlement must not immunize the slug against a FRESH run's debt); the write itself is
|
|
1081
|
+
// noclobber `set -C` (O_EXCL — a concurrent emitter or a planted/dangling symlink cannot be
|
|
1082
|
+
// written through). The agent echoes the written file back; an unverifiable write is logged
|
|
1083
|
+
// LOUDLY, and reqeDue stays true in the result either way.
|
|
1084
|
+
const dueQ = shq(FDIR + '/.fa-state/reqe-due.json')
|
|
1085
|
+
const emitCmd = 'if [ -e ' + dueQ + ' ]; then echo REQE-EXISTS; elif grep -qs ' + shq(qeHash) + ' ' + shq(FDIR + '/.fa-state') + '/reqe-settled*.json 2>/dev/null; then echo REQE-SETTLED-THIS-RUN; else mkdir -p ' + shq(FDIR + '/.fa-state') + ' && printf %s ' + shq(JSON.stringify(reqeDebt)) + ' | sed "s/\\"emittedAt\\":null/\\"emittedAt\\":\\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\\"/" | (set -C; cat > ' + dueQ + ') && cat ' + dueQ + '; fi'
|
|
1086
|
+
const emitOut = await agent('Run EXACTLY this one shell command via your Bash tool and return its stdout VERBATIM, nothing else: ' + emitCmd, { label: 'reqe:emit', phase: 'QE', effort: 'low' })
|
|
1087
|
+
const emitText = String(emitOut || '')
|
|
1088
|
+
if (/REQE-SETTLED-THIS-RUN/.test(emitText)) log('re-QE debt: THIS run’s debt was already settled — not re-opened')
|
|
1089
|
+
else if (/REQE-EXISTS/.test(emitText)) log('re-QE debt: already recorded for ' + SLUG + ' — not overwritten')
|
|
1090
|
+
else if (emitText.indexOf('reqe-due-1') !== -1) log('re-QE DEBT recorded: Step-8 ran same-family under the usage override — after limits reset run `dz reqe --slug ' + SLUG + '` for the independent cross-family pass')
|
|
1091
|
+
else log('re-QE debt write UNVERIFIED (agent returned no readback) — the debt may be missing on disk; reqeDue=true is still reported, record it manually via features/' + SLUG + '/.fa-state/reqe-due.json')
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
898
1095
|
// ── AUTO-COST outcome recording (feature learned-cost-routing) ──
|
|
899
1096
|
// The two-phase label lands here: every auto-cost stage that produced an artifact records a PROVISIONAL (i);
|
|
900
1097
|
// the CODE stage's authoritative gate (ii) is the Step-8 QE grade (A/B = pass) — a produced-but-gate-FAILED
|
|
901
1098
|
// run is recorded as a FAILURE, down-ranking that model for the next run (ADR §2). Byte-identical no-op when
|
|
902
1099
|
// nothing was auto-cost. Gate-less stages get weak provisional credit; richer per-stage gates are Phase-2.
|
|
903
|
-
|
|
1100
|
+
// checkpoint guard, AT-MOST-ONCE by choice: a resumed QE skips recording. NAMED window (Codex QE #6):
|
|
1101
|
+
// if the original run died AFTER the qe checkpoint landed but BEFORE recording finished, that run's
|
|
1102
|
+
// routing sample is LOST — accepted, because the alternative (re-recording on every resume) would
|
|
1103
|
+
// double-count outcomes and silently skew the auto-cost ranking. A lost sample under-informs; a
|
|
1104
|
+
// double-counted one misinforms.
|
|
1105
|
+
if (Object.keys(AUTOCOST).length > 0 && resumedStages.indexOf('qe') !== -1) {
|
|
1106
|
+
log('auto-cost: qe stage was RESUMED — outcome recording skipped (already recorded by the original run)')
|
|
1107
|
+
}
|
|
1108
|
+
if (Object.keys(AUTOCOST).length > 0 && resumedStages.indexOf('qe') === -1) {
|
|
904
1109
|
const recPhase = isLplus ? 'FleetQE' : 'QE'
|
|
905
1110
|
// Record ONLY stages that ACTUALLY RAN and produced an artifact (QE finding: a blanket loop over configured
|
|
906
1111
|
// auto-cost stages credited skipped/failed/fallback stages). `code`/`plan` are guarded by their result var.
|
|
@@ -957,8 +1162,12 @@ if (isLplus) {
|
|
|
957
1162
|
() => agent('Step 9 fleet-QE (requirements traceability + risk) for ' + SLUG + ': map ADR decisions to code to tests; flag orphans + high risk. Write ' + FDIR + '/09_fleet_qe_assessment.md.' + codexEffortHint(fleetTraceOpts), fleetTraceOpts),
|
|
958
1163
|
() => agent('Step 9 fleet-QE (coverage + regression) for ' + SLUG + ': risk-weighted coverage gaps + regression selection for the changed files. Append to ' + FDIR + '/09_fleet_qe_assessment.md.' + codexEffortHint(fleetCovOpts), fleetCovOpts),
|
|
959
1164
|
]
|
|
960
|
-
|
|
961
|
-
fleet = '
|
|
1165
|
+
const fleetHash = ckptHash('fleet', [fnv1a64(JSON.stringify(qeStage === undefined ? null : qeStage)), tier, MODELS.fleet === undefined ? null : MODELS.fleet, CODEX_MODEL, usageOverride])
|
|
1166
|
+
fleet = await withCheckpoint('fleet', 'FleetQE', fleetHash, async () => {
|
|
1167
|
+
const fleetRuns = await parallel(fleetThunks)
|
|
1168
|
+
return fleetRuns.every(function (x) { return x !== null && x !== undefined }) ? 'run' : null
|
|
1169
|
+
})
|
|
1170
|
+
if (fleet === null) fleet = 'failed (a fleet agent died — not checkpointed)'
|
|
962
1171
|
}
|
|
963
1172
|
|
|
964
1173
|
// ── Step 10 (OPT-IN): Delivery Gate — post-implementation full review of the LANDED feature ──
|
|
@@ -1100,6 +1309,9 @@ return {
|
|
|
1100
1309
|
usageEvents: usageEvents,
|
|
1101
1310
|
usageThreshold: USAGE_THRESHOLD,
|
|
1102
1311
|
selfLearning: 'recall@Step0 + teach@Step8 (mandatory)',
|
|
1312
|
+
resumedStages: resumedStages,
|
|
1313
|
+
checkpointing: CHECKPOINTS_ON ? RESUME_MODE : 'off',
|
|
1314
|
+
reqeDue: reqeDue,
|
|
1103
1315
|
brain: BRAIN,
|
|
1104
1316
|
polymorphism: POLY.hasManifest ? POLY.report : null,
|
|
1105
1317
|
claimGate: claimGate,
|