@dzhechkov/skills-feature-adr 1.3.43 → 1.3.45
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/README.md +9 -0
- package/package.json +1 -1
- package/templates/.claude/workflows/feature-adr.js +141 -20
package/README.md
CHANGED
|
@@ -124,6 +124,15 @@ writing code. Without this the run stalls; with `coder: 'codex-fallback'` the pi
|
|
|
124
124
|
unavailable (limit?) — falling back to Codex auto"* and finishes the code + tests on Codex, no
|
|
125
125
|
restart and no lost work.
|
|
126
126
|
|
|
127
|
+
**Codex writes are out-of-band — the Step-7.5 landed barrier waits for them.** Codex applies edits via
|
|
128
|
+
its own runtime, so a naive pipeline runs QE before the async write flushes and false-grades *"Step 7
|
|
129
|
+
never ran"* on real code. For a Codex-coded run the pipeline polls a **bounded 120s backing-off window**
|
|
130
|
+
(`1,2,2,5,5,10,10,15,20,25,25`s), preferring the code stage's *declared expected files* when known, and
|
|
131
|
+
emits an explicit `changed=0 after 120s — genuinely not landed` only when the window truly expires — so
|
|
132
|
+
QE distinguishes "not implemented" from "not yet flushed". A Claude-coded run is synchronous and skips
|
|
133
|
+
the barrier entirely (zero added wait). Note: if Codex flushes slower than 120s, re-verify after the run
|
|
134
|
+
rather than trusting the end-of-run grade.
|
|
135
|
+
|
|
127
136
|
```bash
|
|
128
137
|
# Headless login on a VPS (no browser):
|
|
129
138
|
codex login --device-auth # prints a code + URL you approve on another device
|
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.45",
|
|
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"
|
|
@@ -47,6 +47,12 @@ const DZ_TEACH = (lesson, reward, domain) =>
|
|
|
47
47
|
// ~/.codex/config.toml at pre-flight; the hint below only nudges.
|
|
48
48
|
const CODEX_MODEL = A.codexModel || 'auto'
|
|
49
49
|
const CODEX_HINT = ' (If you are the Codex runtime, prefer the ' + CODEX_MODEL + ' model.)'
|
|
50
|
+
function codexEffortHint(opts) {
|
|
51
|
+
if (opts && opts.agentType === 'codex:codex-rescue' && opts._reasoning) {
|
|
52
|
+
return ' (If you are the Codex runtime, run at --effort ' + opts._reasoning + '.)'
|
|
53
|
+
}
|
|
54
|
+
return ''
|
|
55
|
+
}
|
|
50
56
|
const CODER = (A.coder === 'codex' || A.coder === 'codex-fallback') ? A.coder : 'claude'
|
|
51
57
|
const QE_REVIEWER = (A.qeReviewer === 'codex' || A.qeReviewer === 'codex-fallback') ? A.qeReviewer : 'claude'
|
|
52
58
|
const PLANNER = (A.planner === 'codex') ? 'codex' : 'claude'
|
|
@@ -66,6 +72,7 @@ const PLANNER = (A.planner === 'codex') ? 'codex' : 'claude'
|
|
|
66
72
|
const MODELS = (A.models && typeof A.models === 'object') ? A.models : {}
|
|
67
73
|
const KNOWN_CODEX = { 'auto': 1, 'gpt-5.5': 1, 'gpt-5.6': 1 }
|
|
68
74
|
const CLAUDE_NAMES = { fable: 1, opus: 1, sonnet: 1, haiku: 1 }
|
|
75
|
+
const VALID_REASONING = { none: 1, minimal: 1, low: 1, medium: 1, high: 1, xhigh: 1 }
|
|
69
76
|
const DEFAULT_MODELS = { router: 'fable', requirements: 'sonnet', research: 'sonnet', adr: 'opus', ideation: 'sonnet', ddd: 'opus', architecture: 'opus', plan: 'sonnet', code: null, qe: null, fleet: 'sonnet' }
|
|
70
77
|
const routingRequested = (Object.keys(MODELS).length > 0) || (PLANNER === 'codex') || (CODER === 'codex' || CODER === 'codex-fallback') || (QE_REVIEWER === 'codex' || QE_REVIEWER === 'codex-fallback') || (A.usageAdaptive === true)
|
|
71
78
|
const modelsUsed = {}
|
|
@@ -143,7 +150,8 @@ function specToOpts(spec) {
|
|
|
143
150
|
if (parts[0] === 'codex') {
|
|
144
151
|
let id = parts[1] || CODEX_MODEL
|
|
145
152
|
if (id !== 'auto' && !KNOWN_CODEX[id]) { log('models: unknown codex id ' + id + ' — using ' + CODEX_MODEL); id = CODEX_MODEL }
|
|
146
|
-
|
|
153
|
+
let reasoning = parts[2] || 'high'
|
|
154
|
+
if (!VALID_REASONING[reasoning]) { log('models: unknown reasoning ' + reasoning + ' — using high'); reasoning = 'high' }
|
|
147
155
|
return { agentType: 'codex:codex-rescue', codexModel: id, _reasoning: reasoning }
|
|
148
156
|
}
|
|
149
157
|
if (CLAUDE_NAMES[parts[0]]) return { model: parts[0] }
|
|
@@ -240,6 +248,116 @@ function stageLabel(baseLabel, opts) {
|
|
|
240
248
|
// Codex-QE run, do ZERO extra work (byte-identical to today).
|
|
241
249
|
function needsLandedBarrier(opts) { return !!(opts && opts.agentType === 'codex:codex-rescue') }
|
|
242
250
|
|
|
251
|
+
const DEFAULT_CODE_LANDING_MAX_WAIT_MS = 120000
|
|
252
|
+
const DEFAULT_CODE_LANDING_BACKOFF_MS = [1000, 2000, 2000, 5000, 5000, 10000, 10000, 15000, 20000, 25000, 25000]
|
|
253
|
+
const CODE_LANDED_BARRIER_SLEEPS_SECONDS = DEFAULT_CODE_LANDING_BACKOFF_MS.map((ms) => ms / 1000)
|
|
254
|
+
const CODE_LANDING_PIPELINE_PREFIXES = ['features/', '.dz/', '.agentic-qe/', 'roam/']
|
|
255
|
+
|
|
256
|
+
function codeLandingEmptySignal(seconds) {
|
|
257
|
+
return 'changed=0 after ' + seconds + 's — genuinely not landed'
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function needsCodeLandedBarrier(coderUsed) {
|
|
261
|
+
return coderUsed === 'codex' || coderUsed === 'codex-fallback'
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function normalizeCodeLandingPath(path) {
|
|
265
|
+
let p = String(path || '').trim().replace(/\\/g, '/')
|
|
266
|
+
while (p.indexOf('./') === 0) p = p.slice(2)
|
|
267
|
+
p = p.replace(/\/+/g, '/')
|
|
268
|
+
if (!p) return ''
|
|
269
|
+
if (p[0] === '/') return ''
|
|
270
|
+
if (p === '..' || p.indexOf('../') === 0 || p.indexOf('/../') >= 0 || p.endsWith('/..')) return ''
|
|
271
|
+
if (/[\0\r\n\t "'\x60$;&|<>*?()[\]{}!]/.test(p)) return ''
|
|
272
|
+
if (p.endsWith('/')) return ''
|
|
273
|
+
for (let i = 0; i < CODE_LANDING_PIPELINE_PREFIXES.length; i++) {
|
|
274
|
+
const prefix = CODE_LANDING_PIPELINE_PREFIXES[i]
|
|
275
|
+
const bare = prefix.slice(0, -1)
|
|
276
|
+
if (p === bare || p.indexOf(prefix) === 0) return ''
|
|
277
|
+
}
|
|
278
|
+
return p
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function filterPollableCodePaths(paths) {
|
|
282
|
+
const out = []
|
|
283
|
+
const seen = {}
|
|
284
|
+
for (let i = 0; i < (paths || []).length; i++) {
|
|
285
|
+
const normalized = normalizeCodeLandingPath(paths[i])
|
|
286
|
+
if (!normalized || seen[normalized]) continue
|
|
287
|
+
seen[normalized] = 1
|
|
288
|
+
out.push(normalized)
|
|
289
|
+
}
|
|
290
|
+
return out
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function codeLandedBarrierPlan(coderUsed, expectedPaths) {
|
|
294
|
+
const enabled = needsCodeLandedBarrier(coderUsed)
|
|
295
|
+
const pollWindowSeconds = DEFAULT_CODE_LANDING_MAX_WAIT_MS / 1000
|
|
296
|
+
if (!enabled) {
|
|
297
|
+
return { enabled: false, mode: 'any-code-change', sleepsMs: [], sleepsSeconds: [], pollWindowMs: 0, pollWindowSeconds: 0, expectedPaths: [], emptySignal: '' }
|
|
298
|
+
}
|
|
299
|
+
const filteredExpectedPaths = filterPollableCodePaths(expectedPaths || [])
|
|
300
|
+
return {
|
|
301
|
+
enabled: true,
|
|
302
|
+
mode: filteredExpectedPaths.length > 0 ? 'expected-files' : 'any-code-change',
|
|
303
|
+
sleepsMs: DEFAULT_CODE_LANDING_BACKOFF_MS,
|
|
304
|
+
sleepsSeconds: CODE_LANDED_BARRIER_SLEEPS_SECONDS,
|
|
305
|
+
pollWindowMs: DEFAULT_CODE_LANDING_MAX_WAIT_MS,
|
|
306
|
+
pollWindowSeconds: pollWindowSeconds,
|
|
307
|
+
expectedPaths: filteredExpectedPaths,
|
|
308
|
+
emptySignal: codeLandingEmptySignal(pollWindowSeconds),
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function addExpectedCodeTarget(value, out) {
|
|
313
|
+
if (value === null || value === undefined) return
|
|
314
|
+
if (Array.isArray(value)) {
|
|
315
|
+
for (let i = 0; i < value.length; i++) addExpectedCodeTarget(value[i], out)
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
if (typeof value === 'object') {
|
|
319
|
+
if (Array.isArray(value.wrote)) addExpectedCodeTarget(value.wrote, out)
|
|
320
|
+
if (Array.isArray(value.paths)) addExpectedCodeTarget(value.paths, out)
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
const lines = String(value).split(/\r?\n/)
|
|
324
|
+
for (let i = 0; i < lines.length; i++) {
|
|
325
|
+
const candidate = lines[i].replace(/^[-*]\s+/, '').replace(/^\x60+|\x60+$/g, '').trim()
|
|
326
|
+
if (candidate) out.push(candidate)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function extractExpectedCodeTargetsFromText(text) {
|
|
331
|
+
const out = []
|
|
332
|
+
const lines = String(text || '').split(/\r?\n/)
|
|
333
|
+
let inBlock = false
|
|
334
|
+
for (let i = 0; i < lines.length; i++) {
|
|
335
|
+
const trimmed = lines[i].trim()
|
|
336
|
+
if (/^EXPECTED_CODE_TARGETS:\s*$/i.test(trimmed)) { inBlock = true; continue }
|
|
337
|
+
if (!inBlock) continue
|
|
338
|
+
if (!trimmed) continue
|
|
339
|
+
if (/^[A-Z][A-Z0-9_ -]*:\s*$/.test(trimmed)) break
|
|
340
|
+
out.push(trimmed.replace(/^[-*]\s+/, '').replace(/^\x60+|\x60+$/g, '').trim())
|
|
341
|
+
}
|
|
342
|
+
return out
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function extractExpectedCodeTargets(argTargets, codexText) {
|
|
346
|
+
const out = []
|
|
347
|
+
addExpectedCodeTarget(argTargets, out)
|
|
348
|
+
addExpectedCodeTarget(extractExpectedCodeTargetsFromText(codexText), out)
|
|
349
|
+
return filterPollableCodePaths(out)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function codeLandingShellQuote(value) {
|
|
353
|
+
return "'" + String(value).replace(/'/g, "'\"'\"'") + "'"
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function codeLandingProbeCmd(repo, plan) {
|
|
357
|
+
const expectedList = plan.expectedPaths.length > 0 ? plan.expectedPaths.map(codeLandingShellQuote).join(' ') : "''"
|
|
358
|
+
return 'repo=' + codeLandingShellQuote(repo) + '; sleeps="' + plan.sleepsSeconds.join(' ') + '"; expected_count=' + plan.expectedPaths.length + '; elapsed=0; poll(){ paths=$(git -C "$repo" status --porcelain 2>/dev/null | sed -E "s/^...//" | sed -E "s/.* -> //" | grep -vE "^(features/|[.]dz/|[.]agentic-qe/|roam/)" | sed "/^$/d" | head -200); n=$(printf "%s\\n" "$paths" | sed "/^$/d" | wc -l | tr -d " "); if [ "$expected_count" -gt 0 ]; then matched=""; for p in ' + expectedList + '; do [ -z "$p" ] && continue; if printf "%s\\n" "$paths" | grep -Fx -- "$p" >/dev/null; then matched="$p"; break; fi; done; if [ -n "$matched" ]; then echo "CODEX-LANDING-SIGNAL status=landed changed=1 after=${elapsed}s predicate=expected-path"; echo "matched=$matched"; echo "files:"; printf "%s\\n" "$paths" | head -40; exit 0; fi; else if [ "$n" -gt 0 ]; then echo "CODEX-LANDING-SIGNAL status=landed changed=$n after=${elapsed}s predicate=any-code-change"; echo "files:"; printf "%s\\n" "$paths" | head -40; exit 0; fi; fi; }; poll; for wait in $sleeps; do sleep "$wait"; elapsed=$((elapsed + wait)); poll; done; echo "CODEX-LANDING-SIGNAL status=genuinely-not-landed ' + plan.emptySignal + '"; if [ "$expected_count" -gt 0 ]; then echo "predicate=expected-path observed=$n"; else echo "predicate=any-code-change"; fi; echo "files:"; if [ -n "$paths" ]; then printf "%s\\n" "$paths" | head -40; else echo "(none)"; fi'
|
|
359
|
+
}
|
|
360
|
+
|
|
243
361
|
// A Bash one-liner that waits for a Codex OUT-OF-BAND artifact write to LAND: polls up to ~40s until
|
|
244
362
|
// the file exists, is non-empty, AND its size is stable across two reads (write finished). Assumes a
|
|
245
363
|
// fresh feature slug (no stale same-path artifact) — true for a normal /feature-adr run.
|
|
@@ -257,7 +375,7 @@ async function designStage(promptText, opts, artifactPath, baseLabel) {
|
|
|
257
375
|
if (!needsLandedBarrier(opts)) return await agent(promptText, opts)
|
|
258
376
|
const codexOpts = {}
|
|
259
377
|
for (const k in opts) if (k !== 'schema') codexOpts[k] = opts[k]
|
|
260
|
-
const res = await agent(promptText + ' IMPORTANT: run the Codex task in FOREGROUND (synchronous — do NOT pass --background) so this call blocks until the file is fully written to disk.', codexOpts)
|
|
378
|
+
const res = await agent(promptText + codexEffortHint(codexOpts) + ' IMPORTANT: run the Codex task in FOREGROUND (synchronous — do NOT pass --background) so this call blocks until the file is fully written to disk.', codexOpts)
|
|
261
379
|
const probe = await agent('Confirm a Codex OUT-OF-BAND artifact write has LANDED before the next stage reads it. Run EXACTLY this via Bash and return its stdout verbatim, nothing else:\n' + landedProbeCmd(artifactPath), { label: 'design:confirm-landed', phase: 'Design', effort: 'low' })
|
|
262
380
|
if (res && probe && /landed=/.test(String(probe))) return { wrote: [artifactPath], summary: String(res).slice(0, 300) }
|
|
263
381
|
log('design artifact did not land on codex (' + artifactPath + ') — falling back to Claude')
|
|
@@ -278,7 +396,7 @@ const routerPrompt = 'You are Step 0 (Complexity Router) of the /feature-adr pip
|
|
|
278
396
|
const routerModel = resolveStageModel('router')
|
|
279
397
|
const routerOpts = mergeOpts({ label: stageLabel('router+recall', routerModel), phase: 'Router', schema: ROUTER, effort: 'low' }, routerModel)
|
|
280
398
|
modelsUsed.router = modelLabel(routerOpts)
|
|
281
|
-
const router = await agent(routerPrompt, routerOpts)
|
|
399
|
+
const router = await agent(routerPrompt + codexEffortHint(routerOpts), routerOpts)
|
|
282
400
|
let tier = A.tier || (router ? router.tier : 'M')
|
|
283
401
|
const LEARNED = router ? router.rationale : 'none recalled'
|
|
284
402
|
const isMplus = tier === 'M' || tier === 'L' || tier === 'XL'
|
|
@@ -335,8 +453,8 @@ let plan = null
|
|
|
335
453
|
if (planIsCodex) {
|
|
336
454
|
const planCodexLabelOpts = (planModel.agentType === 'codex:codex-rescue') ? planModel : specToOpts('codex:' + CODEX_MODEL + ':high')
|
|
337
455
|
modelsUsed.plan = modelLabel(planCodexLabelOpts)
|
|
338
|
-
const codexPlanOpts = mergeOpts({ label: stageLabel('plan:codex', planCodexLabelOpts), phase: 'Plan', agentType: 'codex:codex-rescue' },
|
|
339
|
-
const codexPlan = await agent(planPrompt + ' IMPORTANT: run the Codex task in FOREGROUND (synchronous — do NOT pass --background) so this call blocks until 06_implementation_plan.md is fully written to disk.', codexPlanOpts)
|
|
456
|
+
const codexPlanOpts = mergeOpts({ label: stageLabel('plan:codex', planCodexLabelOpts), phase: 'Plan', agentType: 'codex:codex-rescue' }, planCodexLabelOpts)
|
|
457
|
+
const codexPlan = await agent(planPrompt + codexEffortHint(codexPlanOpts) + ' IMPORTANT: run the Codex task in FOREGROUND (synchronous — do NOT pass --background) so this call blocks until 06_implementation_plan.md is fully written to disk.', codexPlanOpts)
|
|
340
458
|
// Codex-landed barrier for the plan artifact: a stub return is NOT proof the file was written
|
|
341
459
|
// (codex writes out-of-band). Require the artifact to LAND; otherwise fall through to the Claude planner.
|
|
342
460
|
const planLanded = codexPlan ? await agent('Confirm the Codex plan write has LANDED. Run EXACTLY this via Bash and return its stdout verbatim, nothing else:\n' + landedProbeCmd(FDIR + '/06_implementation_plan.md'), { label: 'plan:confirm-landed', phase: 'Plan', effort: 'low' }) : null
|
|
@@ -383,6 +501,7 @@ const codeClaudeModel = codeIsCodexFirst ? {} : (codeModel.agentType ? {} : code
|
|
|
383
501
|
const codeClaudeOpts = mergeOpts({ label: stageLabel('code', codeClaudeModel), phase: 'Code', schema: ARTIFACT, effort: 'high' }, codeClaudeModel)
|
|
384
502
|
let code = null
|
|
385
503
|
let coderUsed = 'claude'
|
|
504
|
+
let codexCodeText = ''
|
|
386
505
|
if (!codeIsCodexFirst) {
|
|
387
506
|
code = await agent(codePrompt, codeClaudeOpts)
|
|
388
507
|
if (code) { coderUsed = 'claude'; modelsUsed.code = modelLabel(codeClaudeOpts) }
|
|
@@ -391,21 +510,23 @@ if (code === null && !codeIsCodexFirst) reactiveBelt('Code')
|
|
|
391
510
|
if (code === null && (codeIsCodexFirst || CODER === 'codex-fallback')) {
|
|
392
511
|
if (CODER === 'codex-fallback' && !codeIsCodexFirst) log('Code: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
393
512
|
const codeCodexLabelOpts = codeModel.agentType ? codeModel : specToOpts('codex:' + CODEX_MODEL + ':high')
|
|
394
|
-
const codeCodexOpts = mergeOpts({ label: stageLabel('code:codex', codeCodexLabelOpts), phase: 'Code', agentType: 'codex:codex-rescue' },
|
|
395
|
-
const
|
|
396
|
-
|
|
513
|
+
const codeCodexOpts = mergeOpts({ label: stageLabel('code:codex', codeCodexLabelOpts), phase: 'Code', agentType: 'codex:codex-rescue' }, codeCodexLabelOpts)
|
|
514
|
+
const codexExpectedTargetsHint = '\n\nBecause this is running on Codex, include a final EXPECTED_CODE_TARGETS: block listing the repo-relative production/test files you expect to create or modify. List only real code/test/config/docs targets outside features/, .dz/, .agentic-qe/, and roam/. Example:\nEXPECTED_CODE_TARGETS:\n- packages/example/src/file.ts\n- packages/example/test/file.test.ts'
|
|
515
|
+
const codexCode = await agent(codePrompt + CODEX_HINT + codexEffortHint(codeCodexOpts) + codexExpectedTargetsHint, codeCodexOpts)
|
|
516
|
+
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) }
|
|
397
517
|
}
|
|
398
518
|
|
|
399
519
|
// Step 7.5: Codex-landed barrier. Codex applies edits OUT-OF-BAND via its own runtime; without this,
|
|
400
|
-
// Step-8 QE
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
// reviews the ACTUAL landed changes. Claude-coded runs are synchronous → this barrier is skipped.
|
|
520
|
+
// Step-8 QE can read the tree before the async write flushes and false-grade "Step 7 never ran" on real
|
|
521
|
+
// completed code. Poll a bounded 120s backing-off window, preferring declared expected code targets when
|
|
522
|
+
// known. Claude-coded runs are synchronous → this barrier is skipped with zero target parsing/probe work.
|
|
404
523
|
let landedNote = ''
|
|
405
|
-
if (coderUsed
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
|
|
524
|
+
if (needsCodeLandedBarrier(coderUsed)) {
|
|
525
|
+
const expectedCodeTargets = extractExpectedCodeTargets(A.expectedCodeTargets, codexCodeText)
|
|
526
|
+
const barrierPlan = codeLandedBarrierPlan(coderUsed, expectedCodeTargets)
|
|
527
|
+
const barrierCmd = codeLandingProbeCmd(REPO, barrierPlan)
|
|
528
|
+
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' })
|
|
529
|
+
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)
|
|
409
530
|
}
|
|
410
531
|
|
|
411
532
|
// Step 8: QE (brutal-honesty, agentic-qe) + MANDATORY teach
|
|
@@ -434,8 +555,8 @@ if (qe === null && !qeIsCodex) reactiveBelt('QE')
|
|
|
434
555
|
if (qe === null && (qeIsCodex || QE_REVIEWER === 'codex-fallback')) {
|
|
435
556
|
if (QE_REVIEWER === 'codex-fallback' && !qeIsCodex) log('QE: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
436
557
|
const qeCodexLabelOpts = qeModel.agentType ? qeModel : specToOpts('codex:' + CODEX_MODEL + ':high')
|
|
437
|
-
const qeCodexOpts = mergeOpts({ label: stageLabel('qe:codex', qeCodexLabelOpts), phase: 'QE', agentType: 'codex:codex-rescue' },
|
|
438
|
-
const codexQe = await agent(qePrompt + CODEX_HINT, qeCodexOpts)
|
|
558
|
+
const qeCodexOpts = mergeOpts({ label: stageLabel('qe:codex', qeCodexLabelOpts), phase: 'QE', agentType: 'codex:codex-rescue' }, qeCodexLabelOpts)
|
|
559
|
+
const codexQe = await agent(qePrompt + CODEX_HINT + codexEffortHint(qeCodexOpts), qeCodexOpts)
|
|
439
560
|
if (codexQe) { qe = { grade: 'codex-review', gaps: [], codeTestsAdequate: null, docTestsPresent: null, summary: String(codexQe).slice(0, 500) }; qeReviewerUsed = qeIsCodex ? 'codex' : 'codex-fallback'; modelsUsed.qe = modelLabel(qeCodexLabelOpts) }
|
|
440
561
|
}
|
|
441
562
|
// Belt: if a codex-first QE returned null (codex unavailable), fall back to a Claude reviewer — never block.
|
|
@@ -457,8 +578,8 @@ if (isLplus) {
|
|
|
457
578
|
const fleetTraceOpts = mergeOpts({ label: stageLabel('fleet:trace', fleetModel), phase: 'FleetQE', agentType: 'qe-requirements-validator' }, fleetModel)
|
|
458
579
|
const fleetCovOpts = mergeOpts({ label: stageLabel('fleet:cov', fleetModel), phase: 'FleetQE', agentType: 'qe-coverage-specialist' }, fleetModel)
|
|
459
580
|
const fleetThunks = [
|
|
460
|
-
() => 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.', fleetTraceOpts),
|
|
461
|
-
() => 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.', fleetCovOpts),
|
|
581
|
+
() => 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),
|
|
582
|
+
() => 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),
|
|
462
583
|
]
|
|
463
584
|
await parallel(fleetThunks)
|
|
464
585
|
fleet = 'run'
|