@dzhechkov/skills-feature-adr 1.3.34 → 1.3.38
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
CHANGED
|
@@ -139,7 +139,41 @@ Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
|
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
Omit the Codex knobs entirely for today's all-Claude behavior. The run result reports
|
|
142
|
-
`plannerUsed` / `coderUsed` / `qeReviewerUsed` / `codexModel` so you can see who did what.
|
|
142
|
+
`plannerUsed` / `coderUsed` / `qeReviewerUsed` / `codexModel` / `modelsUsed` so you can see who did what.
|
|
143
|
+
|
|
144
|
+
### Per-stage model routing — `args.models`
|
|
145
|
+
|
|
146
|
+
The three Codex knobs above are shortcuts. `args.models` is the **general dial**: an optional map that
|
|
147
|
+
routes each of the 11 pipeline stages to an optimal model. Keys: `{router, requirements, research, adr,
|
|
148
|
+
ideation, ddd, architecture, plan, code, qe, fleet}`. Each value is a **spec** — Claude
|
|
149
|
+
`fable|opus|sonnet|haiku`, or Codex `codex` / `codex:<id>` / `codex:<id>:<reasoning>`
|
|
150
|
+
(`reasoning ∈ low|medium|high|xhigh`; ids incl. `gpt-5.5`, `gpt-5.6`).
|
|
151
|
+
|
|
152
|
+
**Recommended DEFAULT TABLE** (applied only when you opt in — one `args.models` key or any Codex knob
|
|
153
|
+
turns it on; otherwise every stage is session-inherited, byte-identical to today):
|
|
154
|
+
|
|
155
|
+
| router | requirements | adr | ideation | architecture | plan | code | qe | fleet |
|
|
156
|
+
|---|---|---|---|---|---|---|---|---|
|
|
157
|
+
| `fable` | `sonnet` | `opus` | `sonnet` | `opus` | `sonnet` | coder (default `opus`) | cross-model | `sonnet` |
|
|
158
|
+
|
|
159
|
+
(`research` folds into `requirements`, `ddd` into `architecture` — recorded in `modelsUsed`, not a
|
|
160
|
+
separate call.)
|
|
161
|
+
|
|
162
|
+
**Cross-model QE default (load-bearing):** when `args.models.qe` is unset, QE auto-routes to the **other
|
|
163
|
+
family than the coder** — a model that writes code must not also self-QE; independent cross-model review
|
|
164
|
+
catches what self-review misses. coder=Codex ⇒ QE=Claude (`opus`); coder=Claude ⇒ QE=Codex
|
|
165
|
+
(`codex:<top>:high`), or a Claude reviewer if Codex is unavailable (never blocks).
|
|
166
|
+
|
|
167
|
+
**Precedence:** `args.models[stage]` wins; the legacy `planner`/`coder`/`qeReviewer`/`codexModel` knobs
|
|
168
|
+
fill only unspecified stages. `codexModel` seeds the id for a bare `'codex'` spec. gpt-5.6-ready: a new
|
|
169
|
+
Codex id is a data-only allowlist edit.
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
// Claude writes the code; Codex independently QEs it (cross-model by construction):
|
|
173
|
+
Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
|
|
174
|
+
args: { slug: 'add-oauth', description: '…', tier: 'L',
|
|
175
|
+
models: { code: 'opus', qe: 'codex:gpt-5.6:high', architecture: 'opus', router: 'fable' } } })
|
|
176
|
+
```
|
|
143
177
|
|
|
144
178
|
---
|
|
145
179
|
|
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.38",
|
|
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"
|
|
@@ -60,6 +60,12 @@ Without this, the Code agent returns null and the run stalls. With `coder: 'code
|
|
|
60
60
|
detects the null, logs *"Claude unavailable (limit?) — falling back to Codex auto"*, and finishes
|
|
61
61
|
the code + tests on Codex — no restart, no lost work.
|
|
62
62
|
|
|
63
|
+
**Codex-landed barrier (Step 7.5):** Codex applies edits OUT-OF-BAND via its own runtime, so a naive
|
|
64
|
+
pipeline runs Step-8 QE before the async write flushes and false-grades *"Step 7 never ran"* (grade D on
|
|
65
|
+
real, landed code — observed on the goap-ed25519 fix). When Codex was the coder, feature-adr now polls
|
|
66
|
+
`git status` (excluding pipeline artifacts) up to ~30s until the code changes appear and hands the
|
|
67
|
+
confirmed file list to QE. Claude-coded runs are synchronous, so the barrier is skipped (zero cost).
|
|
68
|
+
|
|
63
69
|
**Example invocations:**
|
|
64
70
|
```js
|
|
65
71
|
// Plan on Codex, code+QE fall back to Codex only if Claude runs out, auto:
|
|
@@ -366,7 +366,7 @@ to Codex — always ASKING first, always with a Claude fallback (never blocks):
|
|
|
366
366
|
- **Model choice** → `args.codexModel` (`auto` default (Codex self-selects) · or an id your account exposes (e.g. `gpt-5.5`)).
|
|
367
367
|
|
|
368
368
|
*Scenario:* an L/XL run hits the session limit during coding → with `coder: 'codex-fallback'` feature-adr
|
|
369
|
-
logs *"Claude unavailable (limit?) — falling back to Codex auto"* and finishes on Codex, no restart.
|
|
369
|
+
logs *"Claude unavailable (limit?) — falling back to Codex auto"* and finishes on Codex, no restart. Codex writes out-of-band, so feature-adr confirms the edits LANDED (`git status`, ~30s poll) before QE — no false "Step 7 never ran" grade.
|
|
370
370
|
|
|
371
371
|
*Example:* `Workflow({ scriptPath: '.claude/workflows/feature-adr.js', args: { slug, description, tier:
|
|
372
372
|
'M', planner: 'codex', coder: 'codex-fallback', qeReviewer: 'codex-fallback', codexModel: 'auto' } }) // 'auto' = Codex picks top; or pin e.g. 'gpt-5.5'`.
|
|
@@ -375,6 +375,45 @@ Pre-flight, if Codex is `ready` (`codex-companion setup --json`), the orchestrat
|
|
|
375
375
|
before launching; plain `/feature-adr` offers the same at the planning checkpoint. Omit the Codex knobs
|
|
376
376
|
for today's all-Claude behavior. See `.claude/rules/feature-adr-ultracode.md`.
|
|
377
377
|
|
|
378
|
+
**Per-stage model routing — `args.models` (one dial, 11 stages).** `args.models` is an optional map that
|
|
379
|
+
routes each pipeline stage to an optimal model. Keys: `{router, requirements, research, adr, ideation,
|
|
380
|
+
ddd, architecture, plan, code, qe, fleet}`. Each value is a **spec**:
|
|
381
|
+
|
|
382
|
+
- **Claude** — `'fable' | 'opus' | 'sonnet' | 'haiku'` → adds `model` to that stage's `agent()` call
|
|
383
|
+
(any role `agentType` like `qe-code-reviewer` is PRESERVED).
|
|
384
|
+
- **Codex** — `'codex'` / `'codex:<id>'` / `'codex:<id>:<reasoning>'` (e.g. `'codex:gpt-5.6:xhigh'`;
|
|
385
|
+
`reasoning ∈ low|medium|high|xhigh`; ids incl. `gpt-5.5`, `gpt-5.6`) → routes the stage to the
|
|
386
|
+
`codex:codex-rescue` runtime with that `codexModel` + reasoning hint.
|
|
387
|
+
|
|
388
|
+
**Recommended DEFAULT TABLE** (applied only when you opt into routing — any one `args.models` key or any
|
|
389
|
+
Codex knob flips it on; otherwise every stage stays session-inherited, byte-identical to today):
|
|
390
|
+
|
|
391
|
+
| Stage | Default | | Stage | Default |
|
|
392
|
+
|---|---|---|---|---|
|
|
393
|
+
| router | `fable` | | architecture | `opus` |
|
|
394
|
+
| requirements | `sonnet` | | plan | `sonnet` |
|
|
395
|
+
| research | `sonnet` (folds into requirements) | | code | *the coder knob* (default Claude `opus`) |
|
|
396
|
+
| adr | `opus` | | qe | *CROSS-MODEL of the coder* (see below) |
|
|
397
|
+
| ideation | `sonnet` | | fleet | `sonnet` |
|
|
398
|
+
| ddd | `opus` (folds into architecture) | | | |
|
|
399
|
+
|
|
400
|
+
**Cross-model QE default (load-bearing).** When `args.models.qe` is UNSET, QE is auto-routed to the
|
|
401
|
+
**other model family than the coder** — a model that WRITES code must not also SELF-QE; independent
|
|
402
|
+
cross-model review catches what self-review misses. coder=Codex ⇒ QE=Claude (`opus`); coder=Claude ⇒
|
|
403
|
+
QE=Codex (`codex:<top>:high`), or a Claude reviewer if Codex is unavailable (**never blocks**).
|
|
404
|
+
|
|
405
|
+
**Precedence.** `args.models[stage]` is the general mechanism and WINS on conflict; the legacy
|
|
406
|
+
`planner`/`coder`/`qeReviewer`/`codexModel` knobs are shortcuts that fill a stage only when `args.models`
|
|
407
|
+
does not. `codexModel` sets the default id for a bare `'codex'` spec. Codex-fallback (Claude-first, then
|
|
408
|
+
Codex on limit-exhaustion) stays a knob-only behavior; a direct `models.code='codex'` means codex-first.
|
|
409
|
+
|
|
410
|
+
**gpt-5.6-ready** — a new Codex id is a DATA-ONLY addition to the `KNOWN_CODEX` allowlist (no control flow).
|
|
411
|
+
**Reporting** — the run result includes `modelsUsed` (the resolved per-stage model) so you see who did what.
|
|
412
|
+
|
|
413
|
+
*Example:* `Workflow({ scriptPath: '.claude/workflows/feature-adr.js', args: { slug, description, tier:
|
|
414
|
+
'L', models: { code: 'opus', qe: 'codex:gpt-5.6:high', architecture: 'opus', router: 'fable' } } })` —
|
|
415
|
+
Claude writes the code, Codex independently QEs it.
|
|
416
|
+
|
|
378
417
|
### Pattern memory loop (self-learning — runs in ALL modes)
|
|
379
418
|
|
|
380
419
|
**Self-learning is MANDATORY on EVERY `/feature-adr` run — including plain `/feature-adr` without any
|
|
@@ -408,6 +447,15 @@ non-blocking — an error or empty result never stalls the pipeline. This layer
|
|
|
408
447
|
**distinct from the Keysarium reward layer** installed by `--with-learning`: it uses agentic-qe MCP
|
|
409
448
|
memory + the dz store, is UNCONDITIONAL for the dz half (recall/teach/fa-record run in every mode) and Direct-mode-only for the aqe-MCP half, and never touches `.keysarium/memory/`.
|
|
410
449
|
|
|
450
|
+
**Canonical brain store — `args.brain` (never fragment the loop).** The dz durable loop only compounds if
|
|
451
|
+
Step-0 recall and Step-8 teach hit the SAME store. The workflow pins both to a canonical **brain** —
|
|
452
|
+
`args.brain`, default = the workspace root — via `cd <brain> && dz recall/teach … --project <brain>`, so a
|
|
453
|
+
Step-8 teach issued from a coder that `cd`'d into a target repo still lands in the brain, not that repo's
|
|
454
|
+
`.dz`. Omitting `args.brain` is behaviorally inert for a workspace-CWD run (`brain === repo`). **Share** a
|
|
455
|
+
brain: `dz recall --all --json > patterns.json` → `dz teach --from-json patterns.json --project <brain>`
|
|
456
|
+
(exact-text dedup, idempotent). **Recover** a fragmented store: `cd <stray-repo> && dz recall --all --json >
|
|
457
|
+
/tmp/stray.json` → `dz teach --from-json /tmp/stray.json --project <brain>` to merge it back into the brain.
|
|
458
|
+
|
|
411
459
|
**Live learning panel (`dz statusline`).** The pipeline **drives** the panel: at each pattern-memory-loop
|
|
412
460
|
step above it records its live state via `dz statusline --fa-record …`, so `dz statusline` can surface
|
|
413
461
|
per-run learning (which feature, which step, how many patterns recalled vs. newly stored). Each of the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
name: 'feature-adr',
|
|
3
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).',
|
|
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}}) instead of an ad-hoc orchestration, so every feature ships with an ADR + inline agentic-qe QE + self-learning.',
|
|
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' },
|
|
7
7
|
{ title: 'Design', detail: 'Steps 1-5 - requirements, ADR, QCSD, architecture (tier-gated)' },
|
|
@@ -25,6 +25,125 @@ const REPO = (A.repo || '.').replace(/\/+$/, '')
|
|
|
25
25
|
const FDIR = REPO + '/features/' + SLUG
|
|
26
26
|
// The dz CLI: bare `dz` (on PATH for installed users) unless the caller overrides with a bin path.
|
|
27
27
|
const DZ = A.dzBin || 'dz'
|
|
28
|
+
// CANONICAL BRAIN store: the self-learning loop (Step-0 recall → Step-8 teach) MUST read+write ONE
|
|
29
|
+
// shared pattern store so lessons never fragment into a target repo's .dz when the Step-7 coder cd's
|
|
30
|
+
// away. BRAIN defaults to the workspace root (REPO) — so an OMITTED args.brain is behaviorally inert
|
|
31
|
+
// for a workspace-CWD run (BRAIN===REPO, same store as today's bare recall/teach). Override args.brain
|
|
32
|
+
// with a stable absolute path to keep ONE brain across several target checkouts.
|
|
33
|
+
const BRAIN = (A.brain || REPO).replace(/\/+$/, '')
|
|
34
|
+
// Helpers PIN every learn-loop command to the canonical brain: `cd <BRAIN> &&` survives a cd'd agent
|
|
35
|
+
// (belt); `--project <BRAIN>` is explicit (suspenders). Either alone fixes it; together they also
|
|
36
|
+
// survive the relative-vs-absolute --project resolution asymmetry between recall and teach.
|
|
37
|
+
const DZ_RECALL = (terms) => 'cd ' + BRAIN + ' && ' + DZ + ' recall "' + terms + '" --project ' + BRAIN
|
|
38
|
+
const DZ_TEACH = (lesson, reward, domain) =>
|
|
39
|
+
'cd ' + BRAIN + ' && ' + DZ + ' teach "' + lesson + '" --reward ' + reward + ' --domain ' + domain + ' --project ' + BRAIN
|
|
40
|
+
|
|
41
|
+
// ── Codex-routing knobs (hoisted so the routing block below can fold them) ──
|
|
42
|
+
// CODER/QE_REVIEWER ∈ 'claude'|'codex'|'codex-fallback'. On 'codex-fallback' the Claude agent runs
|
|
43
|
+
// FIRST; if it returns null (e.g. the Claude Code session limit is exhausted mid-code/mid-QE), the SAME
|
|
44
|
+
// task is retried on the codex:codex-rescue runtime. args.codexModel is DEFAULT 'auto' (Codex self-selects
|
|
45
|
+
// the top model available to the account — ids are account/version-specific and move ahead of any static
|
|
46
|
+
// default, so 'auto' is the portable choice). To hard-pin a specific id the orchestrator writes it into
|
|
47
|
+
// ~/.codex/config.toml at pre-flight; the hint below only nudges.
|
|
48
|
+
const CODEX_MODEL = A.codexModel || 'auto'
|
|
49
|
+
const CODEX_HINT = ' (If you are the Codex runtime, prefer the ' + CODEX_MODEL + ' model.)'
|
|
50
|
+
const CODER = (A.coder === 'codex' || A.coder === 'codex-fallback') ? A.coder : 'claude'
|
|
51
|
+
const QE_REVIEWER = (A.qeReviewer === 'codex' || A.qeReviewer === 'codex-fallback') ? A.qeReviewer : 'claude'
|
|
52
|
+
const PLANNER = (A.planner === 'codex') ? 'codex' : 'claude'
|
|
53
|
+
|
|
54
|
+
// ── PER-STAGE MODEL ROUTING (args.models) ───────────────────────────────────
|
|
55
|
+
// One dial routes each pipeline stage to an optimal model. `args.models` is an optional per-stage map
|
|
56
|
+
// over {router, requirements, research, adr, ideation, ddd, architecture, plan, code, qe, fleet}; each
|
|
57
|
+
// value a SPEC — Claude 'fable'|'opus'|'sonnet'|'haiku', or Codex 'codex' / 'codex:<id>' /
|
|
58
|
+
// 'codex:<id>:<reasoning>' (reasoning ∈ low|medium|high|xhigh; ids incl gpt-5.5, gpt-5.6).
|
|
59
|
+
// LOAD-BEARING: when args.models.qe is unset the QE stage is auto-routed to the OTHER family than the
|
|
60
|
+
// coder (a model that codes must not also self-QE). BACKWARD-COMPATIBLE: omitting args.models AND the
|
|
61
|
+
// legacy knobs ⇒ routingRequested is false ⇒ every stage resolves to {} ⇒ byte-identical to today.
|
|
62
|
+
// Precedence: args.models[stage] > legacy planner/coder/qeReviewer/codexModel knobs > DEFAULT_MODELS.
|
|
63
|
+
// gpt-5.6-ready: adding a codex id is a DATA-ONLY edit to KNOWN_CODEX. This block is the parser-safe
|
|
64
|
+
// (string concat, explicit if/return, object-literal tables — NO template literals, NO inline ?:agent())
|
|
65
|
+
// mirror of src/feature-adr-routing.ts; keep the two in lock-step (a drift test asserts it).
|
|
66
|
+
const MODELS = (A.models && typeof A.models === 'object') ? A.models : {}
|
|
67
|
+
const KNOWN_CODEX = { 'auto': 1, 'gpt-5.5': 1, 'gpt-5.6': 1 }
|
|
68
|
+
const CLAUDE_NAMES = { fable: 1, opus: 1, sonnet: 1, haiku: 1 }
|
|
69
|
+
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
|
+
const routingRequested = (Object.keys(MODELS).length > 0) || (PLANNER === 'codex') || (CODER === 'codex' || CODER === 'codex-fallback') || (QE_REVIEWER === 'codex' || QE_REVIEWER === 'codex-fallback')
|
|
71
|
+
const modelsUsed = {}
|
|
72
|
+
|
|
73
|
+
function specToOpts(spec) {
|
|
74
|
+
if (!spec) return {}
|
|
75
|
+
const parts = String(spec).split(':')
|
|
76
|
+
if (parts[0] === 'codex') {
|
|
77
|
+
let id = parts[1] || CODEX_MODEL
|
|
78
|
+
if (id !== 'auto' && !KNOWN_CODEX[id]) { log('models: unknown codex id ' + id + ' — using ' + CODEX_MODEL); id = CODEX_MODEL }
|
|
79
|
+
const reasoning = parts[2] || 'high'
|
|
80
|
+
return { agentType: 'codex:codex-rescue', codexModel: id, _reasoning: reasoning }
|
|
81
|
+
}
|
|
82
|
+
if (CLAUDE_NAMES[parts[0]]) return { model: parts[0] }
|
|
83
|
+
log('models: unknown spec ' + spec + ' — session-inherited')
|
|
84
|
+
return {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveCoderSpec() {
|
|
88
|
+
if (CODER === 'codex' || CODER === 'codex-fallback') return 'codex:' + CODEX_MODEL + ':high'
|
|
89
|
+
return 'opus'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function coderIsCodex() {
|
|
93
|
+
if (CODER === 'codex' || CODER === 'codex-fallback') return true
|
|
94
|
+
const codeSpec = MODELS.code
|
|
95
|
+
if (codeSpec && String(codeSpec).split(':')[0] === 'codex') return true
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resolveQeSpec() {
|
|
100
|
+
if (coderIsCodex()) return 'opus'
|
|
101
|
+
const CODEX_AVAILABLE = A.codexAvailable !== false
|
|
102
|
+
if (!CODEX_AVAILABLE) return 'opus'
|
|
103
|
+
let top = CODEX_MODEL
|
|
104
|
+
if (top === 'auto') {
|
|
105
|
+
const ids = Object.keys(KNOWN_CODEX)
|
|
106
|
+
for (let i = 0; i < ids.length; i++) { if (ids[i] !== 'auto') top = ids[i] || top }
|
|
107
|
+
}
|
|
108
|
+
return 'codex:' + top + ':high'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// qeShouldUseCodex: the load-bearing cross-model gate — the model that wrote the code must NEVER self-QE.
|
|
112
|
+
// (1) explicit MODELS.qe wins; (2) legacy QE_REVIEWER==='codex' knob honored ONLY when coder is NOT codex
|
|
113
|
+
// (a codex coder + qeReviewer:'codex' would be codex-self-QE); (3) else the cross-model default decides.
|
|
114
|
+
function qeShouldUseCodex() {
|
|
115
|
+
const explicit = MODELS.qe
|
|
116
|
+
if (explicit !== undefined && explicit !== null) {
|
|
117
|
+
return String(explicit).split(':')[0] === 'codex'
|
|
118
|
+
}
|
|
119
|
+
if (QE_REVIEWER === 'codex') return !coderIsCodex()
|
|
120
|
+
return routingRequested && resolveQeSpec().split(':')[0] === 'codex'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveStageModel(stage) {
|
|
124
|
+
let spec = MODELS[stage]
|
|
125
|
+
if (spec === undefined) {
|
|
126
|
+
if (!routingRequested) return {}
|
|
127
|
+
spec = DEFAULT_MODELS[stage]
|
|
128
|
+
}
|
|
129
|
+
if (stage === 'code' && (spec === null || spec === undefined)) return specToOpts(resolveCoderSpec())
|
|
130
|
+
if (stage === 'qe' && (spec === null || spec === undefined)) return specToOpts(resolveQeSpec())
|
|
131
|
+
return specToOpts(spec)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function mergeOpts(base, extra) {
|
|
135
|
+
const out = {}
|
|
136
|
+
for (const k in base) out[k] = base[k]
|
|
137
|
+
for (const k in extra) out[k] = extra[k]
|
|
138
|
+
return out
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// modelLabel: record the resolved spec for a stage in modelsUsed (for the run report / who-did-what).
|
|
142
|
+
function modelLabel(opts) {
|
|
143
|
+
if (opts && opts.agentType === 'codex:codex-rescue') return 'codex:' + opts.codexModel + ':' + opts._reasoning
|
|
144
|
+
if (opts && opts.model) return opts.model
|
|
145
|
+
return 'session'
|
|
146
|
+
}
|
|
28
147
|
|
|
29
148
|
const ROUTER = { type: 'object', additionalProperties: false, required: ['tier', 'activeSteps', 'rationale'], properties: { tier: { type: 'string', enum: ['S', 'M', 'L', 'XL'] }, activeSteps: { type: 'array', items: { type: 'number' } }, rationale: { type: 'string' } } }
|
|
30
149
|
const ARTIFACT = { type: 'object', additionalProperties: false, required: ['wrote', 'summary'], properties: { wrote: { type: 'array', items: { type: 'string' } }, summary: { type: 'string' } } }
|
|
@@ -32,8 +151,10 @@ const QE = { type: 'object', additionalProperties: false, required: ['grade', 'g
|
|
|
32
151
|
|
|
33
152
|
// Step 0: Router + MANDATORY self-learning recall
|
|
34
153
|
phase('Router')
|
|
35
|
-
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, do not summarize instead of running them):
|
|
36
|
-
const
|
|
154
|
+
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, 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); M=4-10 (0,1,3,3.5,5,6,7,8); L=11-30 (all+9); XL=30+ (full+9). Return {tier, activeSteps, rationale} with the recalled patterns folded into rationale.'
|
|
155
|
+
const routerOpts = mergeOpts({ label: 'router+recall', phase: 'Router', schema: ROUTER, effort: 'low' }, resolveStageModel('router'))
|
|
156
|
+
modelsUsed.router = modelLabel(routerOpts)
|
|
157
|
+
const router = await agent(routerPrompt, routerOpts)
|
|
37
158
|
let tier = A.tier || (router ? router.tier : 'M')
|
|
38
159
|
const LEARNED = router ? router.rationale : 'none recalled'
|
|
39
160
|
const isMplus = tier === 'M' || tier === 'L' || tier === 'XL'
|
|
@@ -49,12 +170,24 @@ await agent('Run EXACTLY this one shell command via your Bash tool and report it
|
|
|
49
170
|
phase('Design')
|
|
50
171
|
const designThunks = []
|
|
51
172
|
const reqExtra = isLplus ? ' Also write ' + FDIR + '/02_research.md (codebase patterns + external analogues; read the repo for the closest existing implementation to mirror).' : ''
|
|
52
|
-
|
|
173
|
+
// Resolve per-stage model opts up-front (parser-safe: no inline resolveStageModel inside the thunk arrays).
|
|
174
|
+
// research folds into requirements, ddd folds into architecture (single shared call) — recorded for reporting.
|
|
175
|
+
const reqOpts = mergeOpts({ label: 'requirements', phase: 'Design', schema: ARTIFACT }, resolveStageModel('requirements'))
|
|
176
|
+
const adrOpts = mergeOpts({ label: 'adr', phase: 'Design', schema: ARTIFACT }, resolveStageModel('adr'))
|
|
177
|
+
const qcsdOpts = mergeOpts({ label: 'qcsd', phase: 'Design', schema: ARTIFACT }, resolveStageModel('ideation'))
|
|
178
|
+
const archOpts = mergeOpts({ label: 'architecture', phase: 'Design', schema: ARTIFACT }, resolveStageModel('architecture'))
|
|
179
|
+
modelsUsed.requirements = modelLabel(reqOpts)
|
|
180
|
+
modelsUsed.research = modelLabel(reqOpts)
|
|
181
|
+
modelsUsed.adr = modelLabel(adrOpts)
|
|
182
|
+
modelsUsed.ideation = modelLabel(qcsdOpts)
|
|
183
|
+
modelsUsed.architecture = modelLabel(archOpts)
|
|
184
|
+
modelsUsed.ddd = modelLabel(archOpts)
|
|
185
|
+
designThunks.push(() => agent('Step 1 (Requirements)' + (isLplus ? ' + Step 2 (Research)' : '') + ' of /feature-adr for "' + DESC + '" (tier ' + tier + ', slug ' + SLUG + '). Code: ' + CODE_HINT + '. APPLY these Step-0 recalled LEARNED PATTERNS (fold the applicable ones into requirements/constraints - the loop paying off): ' + LEARNED + '. Write ' + FDIR + '/01_requirements.md (functional + non-functional requirements, acceptance criteria, constraints, and an "Applied learned patterns" note).' + reqExtra + ' Return wrote[] + a 1-line summary.', reqOpts))
|
|
53
186
|
if (isMplus) {
|
|
54
|
-
designThunks.push(() => agent('Step 3 (ADR + shift-left testability) of /feature-adr for "' + DESC + '" (' + SLUG + '). READ the actual code (' + CODE_HINT + ') to ground it. Write ' + FDIR + '/03_adr/001-' + SLUG + '.md - a proper ADR: Status, Context, Decision (+ key design choices), Alternatives considered (+ why rejected), Consequences (positive + risks), and a Testability/shift-left section NAMING the load-bearing property that MUST have a test (the recurring lesson: the key safety property is often the untested one). Return wrote[] + summary.',
|
|
55
|
-
designThunks.push(() => agent('Step 3.5 (QCSD ideation swarm - HTSM quality criteria + SFDIPOT risk) of /feature-adr for "' + DESC + '" (' + SLUG + '). Assess quality criteria + product-factors risk. Write ' + FDIR + '/03.5_ideation_report.md with a GO/CONDITIONAL/NO-GO verdict + top quality risks for QE. Return wrote[] + summary.',
|
|
187
|
+
designThunks.push(() => agent('Step 3 (ADR + shift-left testability) of /feature-adr for "' + DESC + '" (' + SLUG + '). READ the actual code (' + CODE_HINT + ') to ground it. Write ' + FDIR + '/03_adr/001-' + SLUG + '.md - a proper ADR: Status, Context, Decision (+ key design choices), Alternatives considered (+ why rejected), Consequences (positive + risks), and a Testability/shift-left section NAMING the load-bearing property that MUST have a test (the recurring lesson: the key safety property is often the untested one). Return wrote[] + summary.', adrOpts))
|
|
188
|
+
designThunks.push(() => agent('Step 3.5 (QCSD ideation swarm - HTSM quality criteria + SFDIPOT risk) of /feature-adr for "' + DESC + '" (' + SLUG + '). Assess quality criteria + product-factors risk. Write ' + FDIR + '/03.5_ideation_report.md with a GO/CONDITIONAL/NO-GO verdict + top quality risks for QE. Return wrote[] + summary.', qcsdOpts))
|
|
56
189
|
const archExtra = isLplus ? ' Also ' + FDIR + '/04_domain_model.md (DDD).' : ''
|
|
57
|
-
designThunks.push(() => agent((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.',
|
|
190
|
+
designThunks.push(() => agent((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))
|
|
58
191
|
}
|
|
59
192
|
const design = await parallel(designThunks)
|
|
60
193
|
|
|
@@ -63,11 +196,16 @@ const design = await parallel(designThunks)
|
|
|
63
196
|
// the codex:codex-rescue runtime and GRACEFULLY FALL BACK to the default (Claude) planner if Codex is
|
|
64
197
|
// unavailable/errors — the pipeline never blocks on Codex.
|
|
65
198
|
phase('Plan')
|
|
66
|
-
const PLANNER = (A.planner === 'codex') ? 'codex' : 'claude'
|
|
67
199
|
const planPrompt = 'Step 6 (SPARC-GOAP implementation plan) of /feature-adr for "' + DESC + '" (' + SLUG + ', tier ' + tier + '). Given the requirements + ADR + architecture in ' + FDIR + ', decompose into milestones + concrete tasks with success metrics. Write ' + FDIR + '/06_implementation_plan.md. Return wrote[] + summary.'
|
|
200
|
+
// Resolve the plan model. args.models.plan wins; else the planner:'codex' knob (via routingRequested +
|
|
201
|
+
// DEFAULT_MODELS/coder-fold) or the DEFAULT_MODELS.plan ('sonnet') under routing; else {} (BC).
|
|
202
|
+
const planModel = resolveStageModel('plan')
|
|
203
|
+
const planIsCodex = (planModel.agentType === 'codex:codex-rescue') || (MODELS.plan === undefined && PLANNER === 'codex')
|
|
68
204
|
let plan = null
|
|
69
|
-
if (
|
|
70
|
-
|
|
205
|
+
if (planIsCodex) {
|
|
206
|
+
modelsUsed.plan = (planModel.agentType === 'codex:codex-rescue') ? modelLabel(planModel) : ('codex:' + CODEX_MODEL + ':high')
|
|
207
|
+
const codexPlanOpts = mergeOpts({ label: 'plan:codex', phase: 'Plan', agentType: 'codex:codex-rescue' }, planModel.agentType ? planModel : {})
|
|
208
|
+
const codexPlan = await agent(planPrompt, codexPlanOpts)
|
|
71
209
|
if (codexPlan) {
|
|
72
210
|
plan = { wrote: [FDIR + '/06_implementation_plan.md'], summary: String(codexPlan).slice(0, 500), planner: 'codex' }
|
|
73
211
|
log('Plan: Codex (top model)')
|
|
@@ -76,65 +214,105 @@ if (PLANNER === 'codex') {
|
|
|
76
214
|
}
|
|
77
215
|
}
|
|
78
216
|
if (plan === null) {
|
|
79
|
-
const
|
|
80
|
-
plan =
|
|
217
|
+
const claudePlanOpts = mergeOpts({ label: 'plan', phase: 'Plan', schema: ARTIFACT }, planIsCodex ? {} : planModel)
|
|
218
|
+
modelsUsed.plan = planIsCodex ? 'claude-fallback' : modelLabel(claudePlanOpts)
|
|
219
|
+
const claudePlan = await agent(planPrompt, claudePlanOpts)
|
|
220
|
+
plan = claudePlan ? { wrote: claudePlan.wrote, summary: claudePlan.summary, planner: planIsCodex ? 'claude-fallback' : 'claude' } : null
|
|
81
221
|
}
|
|
82
222
|
|
|
83
223
|
// Hybrid checkpoint for L/XL
|
|
84
224
|
const stopHere = STOP_AFTER === 'plan' || (isLplus && STOP_AFTER !== 'none')
|
|
85
225
|
if (stopHere) {
|
|
86
|
-
|
|
226
|
+
// MED-fix: pre-compute the PLANNED code/qe/fleet labels here (resolution is PURE → matches the post-
|
|
227
|
+
// checkpoint run), so the reviewer sees the load-bearing cross-model QE decision at the exact point
|
|
228
|
+
// they re-invoke. Marked `(planned)` since the stages haven't executed yet.
|
|
229
|
+
const codePlanned = modelLabel(resolveStageModel('code'))
|
|
230
|
+
const qePlanned = qeShouldUseCodex() ? modelLabel(resolveStageModel('qe')) : modelLabel(mergeOpts({ agentType: 'qe-code-reviewer' }, resolveStageModel('qe')))
|
|
231
|
+
const plannedModels = mergeOpts(modelsUsed, { code: codePlanned + ' (planned)', qe: qePlanned + ' (planned)' })
|
|
232
|
+
if (isLplus) plannedModels.fleet = modelLabel(resolveStageModel('fleet')) + ' (planned)'
|
|
233
|
+
return { tier: tier, phase: 'checkpoint-after-plan', artifactsDir: FDIR, planner: (plan ? plan.planner : null), plan: (plan ? plan.summary : null), modelsUsed: plannedModels, note: 'L/XL checkpoint - review the ADR + plan (+ the planned code/qe/fleet models), then re-invoke with args.stopAfter="none" to implement + QE.' }
|
|
87
234
|
}
|
|
88
235
|
|
|
89
|
-
// Codex fallback config (opt-in). CODER/QE_REVIEWER ∈ 'claude'|'codex'|'codex-fallback'. On
|
|
90
|
-
// 'codex-fallback' the Claude agent runs FIRST; if it returns null (e.g. the Claude Code session limit
|
|
91
|
-
// is exhausted mid-code/mid-QE — exactly the failure we hit before), the SAME task is retried on the
|
|
92
|
-
// codex:codex-rescue runtime. args.codexModel is DEFAULT 'auto' (Codex self-selects the top model
|
|
93
|
-
// available to the account — model ids are account/version-specific and move ahead of any static
|
|
94
|
-
// default, so 'auto' is the portable choice). To hard-pin a specific id (e.g. this account's top is
|
|
95
|
-
// gpt-5.5), the orchestrator writes it into ~/.codex/config.toml at pre-flight; the hint below only nudges.
|
|
96
|
-
const CODEX_MODEL = A.codexModel || 'auto'
|
|
97
|
-
const CODEX_HINT = ' (If you are the Codex runtime, prefer the ' + CODEX_MODEL + ' model.)'
|
|
98
|
-
const CODER = (A.coder === 'codex' || A.coder === 'codex-fallback') ? A.coder : 'claude'
|
|
99
|
-
const QE_REVIEWER = (A.qeReviewer === 'codex' || A.qeReviewer === 'codex-fallback') ? A.qeReviewer : 'claude'
|
|
100
|
-
|
|
101
236
|
// Step 7: Code (optional Codex fallback on Claude-limit exhaustion)
|
|
102
237
|
phase('Code')
|
|
103
238
|
const codePrompt = 'Step 7 (Code) of /feature-adr for "' + DESC + '" (' + SLUG + '). Implement the feature per the plan + ADR + architecture in ' + FDIR + '. Write the ACTUAL production code + its tests (mirror the closest existing implementation named in research/architecture). Follow repo conventions; build must pass. Write a change manifest ' + FDIR + '/07_code_changes/change_manifest.md listing every file touched. Return wrote[] (incl. real source files) + summary.'
|
|
239
|
+
// Resolve the coder model. args.models.code wins (a direct 'codex' spec = codex-first); else the legacy
|
|
240
|
+
// CODER knob drives it (with its codex-fallback null-guard). resolveStageModel('code') folds both via the
|
|
241
|
+
// code:null sentinel → resolveCoderSpec(). A Claude resolution merges {model} onto the Claude branch;
|
|
242
|
+
// under the BC omit-path it is {} (byte-identical).
|
|
243
|
+
const codeModel = resolveStageModel('code')
|
|
244
|
+
const codeIsCodexFirst = (MODELS.code !== undefined) ? (codeModel.agentType === 'codex:codex-rescue') : (CODER === 'codex')
|
|
245
|
+
const codeClaudeOpts = mergeOpts({ label: 'code', phase: 'Code', schema: ARTIFACT, effort: 'high' }, codeIsCodexFirst ? {} : (codeModel.agentType ? {} : codeModel))
|
|
104
246
|
let code = null
|
|
105
247
|
let coderUsed = 'claude'
|
|
106
|
-
if (
|
|
107
|
-
code = await agent(codePrompt,
|
|
108
|
-
if (code) coderUsed = 'claude'
|
|
248
|
+
if (!codeIsCodexFirst) {
|
|
249
|
+
code = await agent(codePrompt, codeClaudeOpts)
|
|
250
|
+
if (code) { coderUsed = 'claude'; modelsUsed.code = modelLabel(codeClaudeOpts) }
|
|
251
|
+
}
|
|
252
|
+
if (code === null && (codeIsCodexFirst || CODER === 'codex-fallback')) {
|
|
253
|
+
if (CODER === 'codex-fallback' && !codeIsCodexFirst) log('Code: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
254
|
+
const codeCodexOpts = mergeOpts({ label: 'code:codex', phase: 'Code', agentType: 'codex:codex-rescue' }, codeModel.agentType ? codeModel : {})
|
|
255
|
+
const codexCode = await agent(codePrompt + CODEX_HINT, codeCodexOpts)
|
|
256
|
+
if (codexCode) { code = { wrote: [FDIR + '/07_code_changes/change_manifest.md'], summary: String(codexCode).slice(0, 500) }; coderUsed = codeIsCodexFirst ? 'codex' : 'codex-fallback'; modelsUsed.code = modelLabel(codeCodexOpts) }
|
|
109
257
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
258
|
+
|
|
259
|
+
// Step 7.5: Codex-landed barrier. Codex applies edits OUT-OF-BAND via its own runtime; without this,
|
|
260
|
+
// Step-8 QE reads the tree before the async write flushes and false-grades "Step 7 never ran" (grade D
|
|
261
|
+
// on real, landed code — observed on the goap-ed25519 crypto fix). Poll git status (excluding pipeline
|
|
262
|
+
// artifacts) up to ~30s until real code changes appear, and hand the confirmed file list to QE so it
|
|
263
|
+
// reviews the ACTUAL landed changes. Claude-coded runs are synchronous → this barrier is skipped.
|
|
264
|
+
let landedNote = ''
|
|
265
|
+
if (coderUsed === 'codex' || coderUsed === 'codex-fallback') {
|
|
266
|
+
const barrierCmd = 'for i in 1 2 3 4 5 6; do n=$(git -C ' + REPO + ' status --porcelain 2>/dev/null | grep -vE "features/|[.]dz/|[.]agentic-qe/|roam/" | wc -l); [ "$n" -gt 0 ] && break; sleep 5; done; echo "changed=$n"; git -C ' + REPO + ' status --porcelain 2>/dev/null | grep -vE "features/|[.]dz/|[.]agentic-qe/|roam/" | head -40'
|
|
267
|
+
const probe = await agent('Confirm the Codex Step-7 edits have LANDED in the working tree BEFORE QE runs (Codex writes out-of-band). Run EXACTLY this via Bash and return its stdout verbatim, nothing else:\n' + barrierCmd, { label: 'code:confirm-landed', phase: 'Code' })
|
|
268
|
+
landedNote = '\n\nCODEX-CODED (out-of-band): review the CONFIRMED landed working-tree changes below — do NOT report "Step 7 never ran" if files are listed. If changed=0 the implementation genuinely did not land, then grade accordingly.\n' + String(probe || '(landed-probe failed)').slice(0, 1500)
|
|
114
269
|
}
|
|
115
270
|
|
|
116
271
|
// Step 8: QE (brutal-honesty, agentic-qe) + MANDATORY teach
|
|
117
272
|
phase('QE')
|
|
118
|
-
const qePrompt = 'Step 8 (QE - brutal-honesty review, agentic-qe) of /feature-adr for "' + DESC + '" (' + SLUG + '). Adversarially review the SHIPPED code (read it): correctness, edge cases, error handling, and the LOAD-BEARING property the ADR named (ASSERT it has a test - the recurring lesson). Grade A/B/C/D honestly. Assess code-test adequacy + doc-test presence. List CONFIRMED gaps with severity. Write ' + FDIR + '/08_qe_report.md. MANDATORY SELF-LEARNING STORE (close the loop, never skip):
|
|
273
|
+
const qePrompt = 'Step 8 (QE - brutal-honesty review, agentic-qe) of /feature-adr for "' + DESC + '" (' + SLUG + '). Adversarially review the SHIPPED code (read it): correctness, edge cases, error handling, and the LOAD-BEARING property the ADR named (ASSERT it has a test - the recurring lesson). Grade A/B/C/D honestly. Assess code-test adequacy + doc-test presence. List CONFIRMED gaps with severity. Write ' + FDIR + '/08_qe_report.md. MANDATORY SELF-LEARNING STORE (close the loop, never skip): store every lesson in the CANONICAL BRAIN store at `' + BRAIN + '` so it is NOT lost to a target repo you may have cd`d into. Via Bash run EXACTLY `' + DZ_TEACH('<a durable reusable lesson from this feature - a rule/pattern/pitfall, NOT a checkpoint echo>', '<0.7-0.95>', '<area>') + '` for each genuine lesson (1-3 max, high-signal) — the `cd ' + BRAIN + ' &&` prefix + `--project ' + BRAIN + '` pin guarantee the lesson lands in the brain regardless of your CWD. Then run `' + DZ + ' statusline --fa-record --slug ' + SLUG + ' --step "Step 8 QE" --recalled 3 --stored <count taught> --mode ' + MODE + ' --project ' + REPO + '` (run it verbatim via Bash, do not skip). Do NOT teach trivia or invent gaps. Return {grade, gaps, codeTestsAdequate, docTestsPresent}.' + landedNote
|
|
274
|
+
// CROSS-MODEL QE (load-bearing): resolveStageModel('qe') derives the OTHER family than the resolved
|
|
275
|
+
// coder when args.models.qe is unset (coder-codex ⇒ opus; coder-Claude ⇒ codex, or opus if codex absent).
|
|
276
|
+
// An explicit args.models.qe wins. A Claude qe spec is merged onto the qe-code-reviewer base (role
|
|
277
|
+
// PRESERVED); a codex qe spec REPLACES agentType with codex:codex-rescue (as today). The codex-null→
|
|
278
|
+
// Claude guard is retained as the runtime belt so codex-unavailable never blocks.
|
|
279
|
+
const qeModel = resolveStageModel('qe')
|
|
280
|
+
// Single tested source of truth (feature-adr-routing.ts:qeShouldUseCodex) — closes the self-QE hole where
|
|
281
|
+
// the legacy qeReviewer='codex' knob used to re-route QE back to codex even when the CODER was codex.
|
|
282
|
+
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)')
|
|
283
|
+
const qeIsCodex = qeShouldUseCodex()
|
|
284
|
+
const qeClaudeOpts = mergeOpts({ label: 'qe:brutal', phase: 'QE', agentType: 'qe-code-reviewer', schema: QE }, qeIsCodex ? {} : qeModel)
|
|
119
285
|
let qe = null
|
|
120
286
|
let qeReviewerUsed = 'claude'
|
|
121
|
-
if (
|
|
122
|
-
qe = await agent(qePrompt,
|
|
123
|
-
if (qe) qeReviewerUsed = 'claude'
|
|
287
|
+
if (!qeIsCodex) {
|
|
288
|
+
qe = await agent(qePrompt, qeClaudeOpts)
|
|
289
|
+
if (qe) { qeReviewerUsed = 'claude'; modelsUsed.qe = modelLabel(qeClaudeOpts) }
|
|
290
|
+
}
|
|
291
|
+
if (qe === null && (qeIsCodex || QE_REVIEWER === 'codex-fallback')) {
|
|
292
|
+
if (QE_REVIEWER === 'codex-fallback' && !qeIsCodex) log('QE: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
293
|
+
const qeCodexOpts = mergeOpts({ label: 'qe:codex', phase: 'QE', agentType: 'codex:codex-rescue' }, qeModel.agentType ? qeModel : {})
|
|
294
|
+
const codexQe = await agent(qePrompt + CODEX_HINT, qeCodexOpts)
|
|
295
|
+
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(qeCodexOpts) }
|
|
124
296
|
}
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
297
|
+
// Belt: if a codex-first QE returned null (codex unavailable), fall back to a Claude reviewer — never block.
|
|
298
|
+
if (qe === null && qeIsCodex) {
|
|
299
|
+
log('QE: Codex unavailable — falling back to a Claude reviewer (cross-model belt)')
|
|
300
|
+
const qeBeltOpts = mergeOpts({ label: 'qe:brutal', phase: 'QE', agentType: 'qe-code-reviewer', schema: QE }, routingRequested ? { model: 'opus' } : {})
|
|
301
|
+
qe = await agent(qePrompt, qeBeltOpts)
|
|
302
|
+
if (qe) { qeReviewerUsed = 'claude'; modelsUsed.qe = modelLabel(qeBeltOpts) }
|
|
129
303
|
}
|
|
130
304
|
|
|
131
305
|
// Step 9: Fleet QE (L/XL)
|
|
132
306
|
let fleet = 'skipped (S/M)'
|
|
133
307
|
if (isLplus) {
|
|
134
308
|
phase('FleetQE')
|
|
309
|
+
const fleetModel = resolveStageModel('fleet')
|
|
310
|
+
modelsUsed.fleet = modelLabel(mergeOpts({}, fleetModel))
|
|
311
|
+
const fleetTraceOpts = mergeOpts({ label: 'fleet:trace', phase: 'FleetQE', agentType: 'qe-requirements-validator' }, fleetModel)
|
|
312
|
+
const fleetCovOpts = mergeOpts({ label: 'fleet:cov', phase: 'FleetQE', agentType: 'qe-coverage-specialist' }, fleetModel)
|
|
135
313
|
const fleetThunks = [
|
|
136
|
-
() => 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.',
|
|
137
|
-
() => 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.',
|
|
314
|
+
() => 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),
|
|
315
|
+
() => 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),
|
|
138
316
|
]
|
|
139
317
|
await parallel(fleetThunks)
|
|
140
318
|
fleet = 'run'
|
|
@@ -155,6 +333,8 @@ return {
|
|
|
155
333
|
coderUsed: coderUsed,
|
|
156
334
|
qeReviewerUsed: qeReviewerUsed,
|
|
157
335
|
codexModel: CODEX_MODEL,
|
|
336
|
+
modelsUsed: modelsUsed,
|
|
158
337
|
selfLearning: 'recall@Step0 + teach@Step8 (mandatory)',
|
|
338
|
+
brain: BRAIN,
|
|
159
339
|
promiseTags: tags,
|
|
160
340
|
}
|