@dzhechkov/skills-feature-adr 1.3.42 → 1.3.43
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 +7 -0
- package/package.json +1 -1
- package/templates/.claude/workflows/feature-adr.js +44 -23
package/README.md
CHANGED
|
@@ -141,6 +141,13 @@ Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
|
|
|
141
141
|
Omit the Codex knobs entirely for today's all-Claude behavior. The run result reports
|
|
142
142
|
`plannerUsed` / `coderUsed` / `qeReviewerUsed` / `codexModel` / `modelsUsed` so you can see who did what.
|
|
143
143
|
|
|
144
|
+
**Live model visibility in `/workflows`:** each stage's agent label is decorated with its *resolved*
|
|
145
|
+
model — e.g. `adr · codex:gpt-5.5:xhigh`, `plan · opus` — so you see which model actually ran a stage in
|
|
146
|
+
the live progress tree, not just in the final report. This matters because a Codex stage's auto
|
|
147
|
+
model-badge shows the `codex:codex-rescue` Claude wrapper (the session model), never `codex`; the label
|
|
148
|
+
text is the honest signal. A Claude fallback appears as its own distinct node (never mislabeled as Codex),
|
|
149
|
+
and a routing-off run adds no suffix (byte-identical to today).
|
|
150
|
+
|
|
144
151
|
### Per-stage model routing — `args.models`
|
|
145
152
|
|
|
146
153
|
The three Codex knobs above are shortcuts. `args.models` is the **general dial**: an optional map that
|
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.43",
|
|
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"
|
|
@@ -226,6 +226,14 @@ function modelLabel(opts) {
|
|
|
226
226
|
return 'session'
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
// stageLabel: make resolved model participation visible in LIVE /workflows labels.
|
|
230
|
+
// A session-inherited resolution is the routing-off BC path, so it stays silent.
|
|
231
|
+
function stageLabel(baseLabel, opts) {
|
|
232
|
+
const m = modelLabel(opts)
|
|
233
|
+
if (!m || m === 'session') return baseLabel
|
|
234
|
+
return baseLabel + ' · ' + m
|
|
235
|
+
}
|
|
236
|
+
|
|
229
237
|
// needsLandedBarrier — mirror of harness-core's pure gate (feature-adr-routing.ts). TRUE only when a
|
|
230
238
|
// stage resolved to Codex. A Claude stage is synchronous (artifact on disk when agent() returns), so
|
|
231
239
|
// this is FALSE and the barrier below is never constructed → an all-Claude run, and a Claude-design +
|
|
@@ -245,7 +253,7 @@ function landedProbeCmd(f) {
|
|
|
245
253
|
// codex-rescue returns finalMessage text, not StructuredOutput; success is proven by the file landing —
|
|
246
254
|
// (b) add a FOREGROUND hint so the codex runtime blocks until the write completes, (c) poll the
|
|
247
255
|
// artifact, and (d) fall back to a Claude agent if it never lands (never blocks the pipeline).
|
|
248
|
-
async function designStage(promptText, opts, artifactPath) {
|
|
256
|
+
async function designStage(promptText, opts, artifactPath, baseLabel) {
|
|
249
257
|
if (!needsLandedBarrier(opts)) return await agent(promptText, opts)
|
|
250
258
|
const codexOpts = {}
|
|
251
259
|
for (const k in opts) if (k !== 'schema') codexOpts[k] = opts[k]
|
|
@@ -253,7 +261,8 @@ async function designStage(promptText, opts, artifactPath) {
|
|
|
253
261
|
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' })
|
|
254
262
|
if (res && probe && /landed=/.test(String(probe))) return { wrote: [artifactPath], summary: String(res).slice(0, 300) }
|
|
255
263
|
log('design artifact did not land on codex (' + artifactPath + ') — falling back to Claude')
|
|
256
|
-
|
|
264
|
+
const fallbackOpts = {}
|
|
265
|
+
return await agent(promptText, mergeOpts({ label: stageLabel((baseLabel || 'design') + ':claude-fb', fallbackOpts), phase: 'Design', schema: ARTIFACT }, fallbackOpts))
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
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' } } }
|
|
@@ -266,7 +275,8 @@ const ADR_FITNESS_CHECKLIST = 'ADR fitness checklist for Step 8: read every ' +
|
|
|
266
275
|
phase('Router')
|
|
267
276
|
await usageProbe('Router')
|
|
268
277
|
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.'
|
|
269
|
-
const
|
|
278
|
+
const routerModel = resolveStageModel('router')
|
|
279
|
+
const routerOpts = mergeOpts({ label: stageLabel('router+recall', routerModel), phase: 'Router', schema: ROUTER, effort: 'low' }, routerModel)
|
|
270
280
|
modelsUsed.router = modelLabel(routerOpts)
|
|
271
281
|
const router = await agent(routerPrompt, routerOpts)
|
|
272
282
|
let tier = A.tier || (router ? router.tier : 'M')
|
|
@@ -287,22 +297,26 @@ const designThunks = []
|
|
|
287
297
|
const reqExtra = isLplus ? ' Also write ' + FDIR + '/02_research.md (codebase patterns + external analogues; read the repo for the closest existing implementation to mirror).' : ''
|
|
288
298
|
// Resolve per-stage model opts up-front (parser-safe: no inline resolveStageModel inside the thunk arrays).
|
|
289
299
|
// research folds into requirements, ddd folds into architecture (single shared call) — recorded for reporting.
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
const
|
|
300
|
+
const reqModel = resolveStageModel('requirements')
|
|
301
|
+
const adrModel = resolveStageModel('adr')
|
|
302
|
+
const qcsdModel = resolveStageModel('ideation')
|
|
303
|
+
const archModel = resolveStageModel('architecture')
|
|
304
|
+
const reqOpts = mergeOpts({ label: stageLabel('requirements', reqModel), phase: 'Design', schema: ARTIFACT }, reqModel)
|
|
305
|
+
const adrOpts = mergeOpts({ label: stageLabel('adr', adrModel), phase: 'Design', schema: ARTIFACT }, adrModel)
|
|
306
|
+
const qcsdOpts = mergeOpts({ label: stageLabel('qcsd', qcsdModel), phase: 'Design', schema: ARTIFACT }, qcsdModel)
|
|
307
|
+
const archOpts = mergeOpts({ label: stageLabel('architecture', archModel), phase: 'Design', schema: ARTIFACT }, archModel)
|
|
294
308
|
modelsUsed.requirements = modelLabel(reqOpts)
|
|
295
309
|
modelsUsed.research = modelLabel(reqOpts)
|
|
296
310
|
modelsUsed.adr = modelLabel(adrOpts)
|
|
297
311
|
modelsUsed.ideation = modelLabel(qcsdOpts)
|
|
298
312
|
modelsUsed.architecture = modelLabel(archOpts)
|
|
299
313
|
modelsUsed.ddd = modelLabel(archOpts)
|
|
300
|
-
designThunks.push(() => designStage('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, FDIR + '/01_requirements.md'))
|
|
314
|
+
designThunks.push(() => designStage('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, FDIR + '/01_requirements.md', 'requirements'))
|
|
301
315
|
if (isMplus) {
|
|
302
|
-
designThunks.push(() => designStage('Step 3 (ADR + shift-left testability) of /feature-adr for "' + DESC + '" (' + SLUG + '). READ the actual code (' + CODE_HINT + ') to ground it. ' + ADR_TEMPLATE_GUIDE + ' Write ' + FDIR + '/03_adr/001-' + SLUG + '.md as a MADR-structured ADR that PASSES the Step-8 ADR fitness checklist (do NOT emit the legacy shape). Emit ALL of these sections, in order: a decision-shaped # Title (present-tense imperative verb — the auto-filename tracks the feature slug, so the IMPERATIVE signal lives in the title); ## Status (proposed/accepted/rejected/deprecated/superseded + a reversibility/revisit clause); ## Context (neutral, problem-first, BEFORE the Decision); ## Decision Drivers (ranked/weighted D1, D2, …); ## Considered Options (frame the CHOSEN approach as one option ALONGSIDE the rejected ones, each with symmetric Pros:/Cons:); ## Decision (concrete/testable — exact names, versions, paths, commands); ## Rationale (map each point to a driver Dn + why the losers lost); ## Consequences (Positive + Negative/Accepted Downsides + Follow-up ADRs + After-action Review with owner + date); a REQUIRED ## Confirmation stanza with Method:, Monitoring:, Success metric:, Owner:, Load-bearing property:, and Required automated check: `<test file>` NAMING the load-bearing property that MUST have a Step-8 test (the recurring lesson: the key safety property is often the untested one); and a ## Links traceability block (requirements, driving use case, related ADRs). Add a one-line provenance note (model-generated, edited for clarity) and, for a long ADR, a top-of-file table of contents. Do NOT use an "Alternatives considered" or "Testability/shift-left" heading in place of Considered Options / Confirmation. When creating ADDITIONAL ADRs, name them 03_adr/NNN-{decision-slug}.md with a lowercase-kebab, present-tense imperative, dateless, ticketless slug. Return wrote[] + summary.', adrOpts, FDIR + '/03_adr/001-' + SLUG + '.md'))
|
|
303
|
-
designThunks.push(() => designStage('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, FDIR + '/03.5_ideation_report.md'))
|
|
316
|
+
designThunks.push(() => designStage('Step 3 (ADR + shift-left testability) of /feature-adr for "' + DESC + '" (' + SLUG + '). READ the actual code (' + CODE_HINT + ') to ground it. ' + ADR_TEMPLATE_GUIDE + ' Write ' + FDIR + '/03_adr/001-' + SLUG + '.md as a MADR-structured ADR that PASSES the Step-8 ADR fitness checklist (do NOT emit the legacy shape). Emit ALL of these sections, in order: a decision-shaped # Title (present-tense imperative verb — the auto-filename tracks the feature slug, so the IMPERATIVE signal lives in the title); ## Status (proposed/accepted/rejected/deprecated/superseded + a reversibility/revisit clause); ## Context (neutral, problem-first, BEFORE the Decision); ## Decision Drivers (ranked/weighted D1, D2, …); ## Considered Options (frame the CHOSEN approach as one option ALONGSIDE the rejected ones, each with symmetric Pros:/Cons:); ## Decision (concrete/testable — exact names, versions, paths, commands); ## Rationale (map each point to a driver Dn + why the losers lost); ## Consequences (Positive + Negative/Accepted Downsides + Follow-up ADRs + After-action Review with owner + date); a REQUIRED ## Confirmation stanza with Method:, Monitoring:, Success metric:, Owner:, Load-bearing property:, and Required automated check: `<test file>` NAMING the load-bearing property that MUST have a Step-8 test (the recurring lesson: the key safety property is often the untested one); and a ## Links traceability block (requirements, driving use case, related ADRs). Add a one-line provenance note (model-generated, edited for clarity) and, for a long ADR, a top-of-file table of contents. Do NOT use an "Alternatives considered" or "Testability/shift-left" heading in place of Considered Options / Confirmation. When creating ADDITIONAL ADRs, name them 03_adr/NNN-{decision-slug}.md with a lowercase-kebab, present-tense imperative, dateless, ticketless slug. Return wrote[] + summary.', adrOpts, FDIR + '/03_adr/001-' + SLUG + '.md', 'adr'))
|
|
317
|
+
designThunks.push(() => designStage('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, FDIR + '/03.5_ideation_report.md', 'qcsd'))
|
|
304
318
|
const archExtra = isLplus ? ' Also ' + FDIR + '/04_domain_model.md (DDD).' : ''
|
|
305
|
-
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'))
|
|
319
|
+
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'))
|
|
306
320
|
}
|
|
307
321
|
const design = await parallel(designThunks)
|
|
308
322
|
|
|
@@ -319,8 +333,9 @@ const planModel = resolveStageModel('plan')
|
|
|
319
333
|
const planIsCodex = (planModel.agentType === 'codex:codex-rescue') || (MODELS.plan === undefined && PLANNER === 'codex')
|
|
320
334
|
let plan = null
|
|
321
335
|
if (planIsCodex) {
|
|
322
|
-
|
|
323
|
-
|
|
336
|
+
const planCodexLabelOpts = (planModel.agentType === 'codex:codex-rescue') ? planModel : specToOpts('codex:' + CODEX_MODEL + ':high')
|
|
337
|
+
modelsUsed.plan = modelLabel(planCodexLabelOpts)
|
|
338
|
+
const codexPlanOpts = mergeOpts({ label: stageLabel('plan:codex', planCodexLabelOpts), phase: 'Plan', agentType: 'codex:codex-rescue' }, planModel.agentType ? planModel : {})
|
|
324
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)
|
|
325
340
|
// Codex-landed barrier for the plan artifact: a stub return is NOT proof the file was written
|
|
326
341
|
// (codex writes out-of-band). Require the artifact to LAND; otherwise fall through to the Claude planner.
|
|
@@ -334,7 +349,8 @@ if (planIsCodex) {
|
|
|
334
349
|
}
|
|
335
350
|
if (plan === null && planIsCodex) reactiveBelt('Plan')
|
|
336
351
|
if (plan === null) {
|
|
337
|
-
const
|
|
352
|
+
const claudePlanModel = planIsCodex ? {} : planModel
|
|
353
|
+
const claudePlanOpts = mergeOpts({ label: stageLabel(planIsCodex ? 'plan:claude-fb' : 'plan', claudePlanModel), phase: 'Plan', schema: ARTIFACT }, claudePlanModel)
|
|
338
354
|
modelsUsed.plan = planIsCodex ? 'claude-fallback' : modelLabel(claudePlanOpts)
|
|
339
355
|
const claudePlan = await agent(planPrompt, claudePlanOpts)
|
|
340
356
|
plan = claudePlan ? { wrote: claudePlan.wrote, summary: claudePlan.summary, planner: planIsCodex ? 'claude-fallback' : 'claude' } : null
|
|
@@ -363,7 +379,8 @@ const codePrompt = 'Step 7 (Code) of /feature-adr for "' + DESC + '" (' + SLUG +
|
|
|
363
379
|
// under the BC omit-path it is {} (byte-identical).
|
|
364
380
|
const codeModel = resolveStageModel('code')
|
|
365
381
|
const codeIsCodexFirst = (MODELS.code !== undefined) ? (codeModel.agentType === 'codex:codex-rescue') : (CODER === 'codex')
|
|
366
|
-
const
|
|
382
|
+
const codeClaudeModel = codeIsCodexFirst ? {} : (codeModel.agentType ? {} : codeModel)
|
|
383
|
+
const codeClaudeOpts = mergeOpts({ label: stageLabel('code', codeClaudeModel), phase: 'Code', schema: ARTIFACT, effort: 'high' }, codeClaudeModel)
|
|
367
384
|
let code = null
|
|
368
385
|
let coderUsed = 'claude'
|
|
369
386
|
if (!codeIsCodexFirst) {
|
|
@@ -373,9 +390,10 @@ if (!codeIsCodexFirst) {
|
|
|
373
390
|
if (code === null && !codeIsCodexFirst) reactiveBelt('Code')
|
|
374
391
|
if (code === null && (codeIsCodexFirst || CODER === 'codex-fallback')) {
|
|
375
392
|
if (CODER === 'codex-fallback' && !codeIsCodexFirst) log('Code: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
376
|
-
const
|
|
393
|
+
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' }, codeModel.agentType ? codeModel : {})
|
|
377
395
|
const codexCode = await agent(codePrompt + CODEX_HINT, codeCodexOpts)
|
|
378
|
-
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(
|
|
396
|
+
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(codeCodexLabelOpts) }
|
|
379
397
|
}
|
|
380
398
|
|
|
381
399
|
// Step 7.5: Codex-landed barrier. Codex applies edits OUT-OF-BAND via its own runtime; without this,
|
|
@@ -404,7 +422,8 @@ const qeModel = resolveStageModel('qe')
|
|
|
404
422
|
// the legacy qeReviewer='codex' knob used to re-route QE back to codex even when the CODER was codex.
|
|
405
423
|
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)')
|
|
406
424
|
const qeIsCodex = qeShouldUseCodex()
|
|
407
|
-
const
|
|
425
|
+
const qeClaudeModel = qeIsCodex ? {} : qeModel
|
|
426
|
+
const qeClaudeOpts = mergeOpts({ label: stageLabel('qe:brutal', qeClaudeModel), phase: 'QE', agentType: 'qe-code-reviewer', schema: QE }, qeClaudeModel)
|
|
408
427
|
let qe = null
|
|
409
428
|
let qeReviewerUsed = 'claude'
|
|
410
429
|
if (!qeIsCodex) {
|
|
@@ -414,14 +433,16 @@ if (!qeIsCodex) {
|
|
|
414
433
|
if (qe === null && !qeIsCodex) reactiveBelt('QE')
|
|
415
434
|
if (qe === null && (qeIsCodex || QE_REVIEWER === 'codex-fallback')) {
|
|
416
435
|
if (QE_REVIEWER === 'codex-fallback' && !qeIsCodex) log('QE: Claude unavailable (limit?) — falling back to Codex ' + CODEX_MODEL)
|
|
417
|
-
const
|
|
436
|
+
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' }, qeModel.agentType ? qeModel : {})
|
|
418
438
|
const codexQe = await agent(qePrompt + CODEX_HINT, qeCodexOpts)
|
|
419
|
-
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(
|
|
439
|
+
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) }
|
|
420
440
|
}
|
|
421
441
|
// Belt: if a codex-first QE returned null (codex unavailable), fall back to a Claude reviewer — never block.
|
|
422
442
|
if (qe === null && qeIsCodex) {
|
|
423
443
|
log('QE: Codex unavailable — falling back to a Claude reviewer (cross-model belt)')
|
|
424
|
-
const
|
|
444
|
+
const qeBeltModel = routingRequested ? { model: 'opus' } : {}
|
|
445
|
+
const qeBeltOpts = mergeOpts({ label: stageLabel('qe:brutal:claude-fb', qeBeltModel), phase: 'QE', agentType: 'qe-code-reviewer', schema: QE }, qeBeltModel)
|
|
425
446
|
qe = await agent(qePrompt, qeBeltOpts)
|
|
426
447
|
if (qe) { qeReviewerUsed = 'claude'; modelsUsed.qe = modelLabel(qeBeltOpts) }
|
|
427
448
|
}
|
|
@@ -433,8 +454,8 @@ if (isLplus) {
|
|
|
433
454
|
await usageProbe('FleetQE')
|
|
434
455
|
const fleetModel = resolveStageModel('fleet')
|
|
435
456
|
modelsUsed.fleet = modelLabel(mergeOpts({}, fleetModel))
|
|
436
|
-
const fleetTraceOpts = mergeOpts({ label: 'fleet:trace', phase: 'FleetQE', agentType: 'qe-requirements-validator' }, fleetModel)
|
|
437
|
-
const fleetCovOpts = mergeOpts({ label: 'fleet:cov', phase: 'FleetQE', agentType: 'qe-coverage-specialist' }, fleetModel)
|
|
457
|
+
const fleetTraceOpts = mergeOpts({ label: stageLabel('fleet:trace', fleetModel), phase: 'FleetQE', agentType: 'qe-requirements-validator' }, fleetModel)
|
|
458
|
+
const fleetCovOpts = mergeOpts({ label: stageLabel('fleet:cov', fleetModel), phase: 'FleetQE', agentType: 'qe-coverage-specialist' }, fleetModel)
|
|
438
459
|
const fleetThunks = [
|
|
439
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),
|
|
440
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),
|