mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-27 15:11:47 +02:00
* fix(memory-ingest): --scan-secrets scans the rendered page and fails closed --scan-secrets ran gitleaks on the raw transcript .jsonl, then imported a page rendered from it. gitleaks' assignment rules don't match across a JSON-escaped quote (KEY=\"v\" on disk), so a secret the rendered page shows as KEY="v" was imported unflagged. And the gate skipped a file only on scanner "gitleaks" with findings, so a scan that errored (non-zero exit, 16MB maxBuffer overflow on a file with many findings, unparseable report) or could not run (gitleaks missing, slow-probe cooldown) imported the file unscanned. Scan the rendered page body, the exact bytes writeStaged() writes, via a new secretScanText() helper, and skip the file whenever the scan did not complete. Skipped files stay out of the state file, so the next run retries them. Reword the helper warnings and setup-gbrain/memory.md, which described the fail-open as intended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(test): reconcile Bun failure markers and footer counts * fix(sync-gbrain): verify source-scoped reads without mutation * fix(test): recognize grounded TTHW target choices structurally * fix(aside): make the readiness probe work under zsh and report why it failed The probe built its deadline into `_T` and expanded it unquoted, so `$_T aside repl …` only worked in a shell that word-splits. zsh does not: it looked for a command literally named "gtimeout 30", the probe answered ASIDE_NOT_RUNNING with Aside installed and ready, and every browsing skill fell back to the bundled Chromium in silence. zsh is the macOS default and Aside is macOS-only, so on a stock Mac the probe could never report READY. The deadline becomes a function, `_gs_d`. It receives the command as "$@", already split, so sh, bash and zsh all behave the same, and the gtimeout → timeout → perl alarm chain is unchanged. A 4th arm runs the call unbounded when none of the three is present, which is what the empty `_T` did before. Not `eval`: it re-parses the string, so the parens and `;` of the perl arm become syntax and that arm dies in bash *and* zsh — on a stock Mac, the arm that actually runs. On failure the probe now prints the CLI's reason after ASIDE_NOT_RUNNING:, the shape gstack-render already uses: the first line that starts with a capital letter, i.e. the CLI's own sentence or Node's `Error:` line below its loader frame. "Not running" covers states with different fixes — no window open for the profile, a NODE_OPTIONS preload that kills the CLI — and a bare verdict sent all of them to "open the Aside app". The BROWSER SETUP prose quotes that reason before asking the user to open the app. The text pin asserted the broken invocation verbatim, so it now pins the function and asserts neither `$_T aside repl` nor an eval form comes back. A second test executes the rendered probe in sh, bash and zsh on each of the four deadline arms with stubbed binaries on a narrowed PATH, plus two failing CLIs: one that prints its own sentence, one that crashes like Node with the useful line below the frame. The deadline function costs zero bytes against the lines it replaces; the reason costs 53 per copy of the probe (44 where the reworded BROWSER SETUP line gives 9 back). That moves four guards by the measured amount: plan-devex-review's skeleton cap to 68,550 (measured 68,544), plan-ceo-review's skeleton cap to 80,150 (measured 80,111) and union ratio to 1.081 (measured 1.0803), and plan-eng-review's union ratio to 1.151 (measured 1.1504). Fixes #2842, #2941. * Clarify engineering review startup and decision flow * Fix Windows readiness fixture PATH and command shim * fix(test): recognize grounded TTHW target choices structurally * Clarify engineering review startup and decision flow * fix(test): restrict QA-only fixture tools to its no-Edit contract * v1.90.0.0 fix(sync-gbrain): guard readiness verdicts and refresh metadata * fix(browse): validate canonical upload targets * fix(gbrain): classify structured PGLite busy response * fix(browse): preserve native extension runtime APIs * Fix displayless browser handoff ownership * Accept unique installed autoplan methodology aliases * fix(skills): preserve positional literals during installation * fix(browse): checksum installer contents through stdin * fix(test): normalize Windows checksum fixture paths * test: emulate unavailable shasum in Windows checksum fixture * fix(investigate): preserve owned freeze lifecycle * fix(review): preserve N+1 retry and Red Team completion * fix: bound Aside readiness and preserve safe fallback * test: exercise setup and Chromium on native ARM * fix: preserve install ownership and ARM browser selection * Fix gbrain ingest scan boundaries and seed observation * Refresh managed ship hooks and supervise expanded paid census * Reject resumed gbrain pages excluded by current policy * Recover zombie agent locks safely and enable CI Python venv * Repair paid actor declarations and Aside pitch assertions * Bump consolidated wave to next free minor release * Clarify CEO review admin choices and option tradeoffs * Preserve CEO mode handoff anchors in clarified workflow * Make Windows portability fixtures use shell-native paths * Restore ARM Bun alias and clarify ship review gates * Refresh ship workflow golden snapshots * Fix Windows DX documentation controls without piped stdin * Decode Codex child pipes without Bun's encoded-stream stall * Bound DX pre-review audit before product questions * Clarify trusted review-start read in paid revalidation * Bump consolidated wave to next free minor release * Clarify CEO review admin choices and option tradeoffs * Preserve CEO mode handoff anchors in clarified workflow * Make Windows portability fixtures use shell-native paths * Restore ARM Bun alias and clarify ship review gates * Refresh ship workflow golden snapshots * Fix Windows DX documentation controls without piped stdin * Decode Codex child pipes without Bun's encoded-stream stall * Bound DX pre-review audit before product questions * Clarify trusted review-start read in paid revalidation * Reconcile new main planning flow and paid judge census * fix: reconcile rebased planning and source-bound validation * test: pin cookie workflow judge to scored Sonnet model * fix: keep terminal agent boot out of module imports * fix: preserve pending-question uncertainty in engineering review * fix: stabilize Windows reliability-wave fixtures * fix: clarify design consultation research workflow * fix: preserve independent design consultation inputs * fix: resolve design taste scope and browser research guidance * fix: make consultation opt-in preflight unambiguous * test: await native Edge owner readiness or terminal result --------- Co-authored-by: Bruce Krysiak <brucek@alum.mit.edu> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Antonio Vitalic <antoninte99@gmail.com>
280 lines
16 KiB
TypeScript
280 lines
16 KiB
TypeScript
/**
|
|
* Review Army resolver — parallel specialist reviewers for /review
|
|
*
|
|
* Generates template prose that instructs Claude to:
|
|
* 1. Detect stack and scope (via gstack-diff-scope)
|
|
* 2. Select and dispatch specialist subagents in parallel
|
|
* 3. Collect, parse, merge, and deduplicate JSON findings
|
|
* 4. Feed merged findings into the existing Fix-First pipeline
|
|
*
|
|
* Shipped as Release 2 of the self-learning roadmap (SELF_LEARNING_V0.md).
|
|
*/
|
|
import type { TemplateContext } from './types';
|
|
import { CC_BACKGROUND_DEFAULT_SINCE } from './constants';
|
|
|
|
function generateSpecialistSelection(ctx: TemplateContext): string {
|
|
const isShip = ctx.skillName === 'ship';
|
|
const stepSel = isShip ? '9.1' : '4.5';
|
|
const stepMerge = isShip ? '9.2' : '4.6';
|
|
const nextStep = isShip ? 'Step 9.3 (cross-review dedup)' : 'Step 5';
|
|
return `## Step ${stepSel}: Review Army — Specialist Dispatch
|
|
|
|
### Detect stack and scope
|
|
|
|
\`\`\`bash
|
|
source <(${ctx.paths.binDir}/gstack-diff-scope <base> 2>/dev/null) || true
|
|
# Detect stack for specialist context
|
|
STACK=""
|
|
[ -f Gemfile ] && STACK="\${STACK}ruby "
|
|
[ -f package.json ] && STACK="\${STACK}node "
|
|
[ -f requirements.txt ] || [ -f pyproject.toml ] && STACK="\${STACK}python "
|
|
[ -f go.mod ] && STACK="\${STACK}go "
|
|
[ -f Cargo.toml ] && STACK="\${STACK}rust "
|
|
echo "STACK: \${STACK:-unknown}"
|
|
DIFF_BASE=$(git merge-base origin/<base> HEAD)
|
|
DIFF_INS=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo "0")
|
|
DIFF_DEL=$(git diff "$DIFF_BASE" --stat | tail -1 | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo "0")
|
|
DIFF_LINES=$((DIFF_INS + DIFF_DEL))
|
|
echo "DIFF_LINES: $DIFF_LINES"
|
|
# Detect test framework for specialist test stub generation
|
|
TEST_FW=""
|
|
{ [ -f jest.config.ts ] || [ -f jest.config.js ]; } && TEST_FW="jest"
|
|
[ -f vitest.config.ts ] && TEST_FW="vitest"
|
|
{ [ -f spec/spec_helper.rb ] || [ -f .rspec ]; } && TEST_FW="rspec"
|
|
{ [ -f pytest.ini ] || [ -f conftest.py ]; } && TEST_FW="pytest"
|
|
[ -f go.mod ] && TEST_FW="go-test"
|
|
echo "TEST_FW: \${TEST_FW:-unknown}"
|
|
\`\`\`
|
|
|
|
### Read specialist hit rates (adaptive gating)
|
|
|
|
\`\`\`bash
|
|
${ctx.paths.binDir}/gstack-specialist-stats 2>/dev/null || true
|
|
\`\`\`
|
|
|
|
### Select specialists
|
|
|
|
Based on the scope signals above, select which specialists to dispatch.
|
|
|
|
**Always-on (dispatch on every review with 50+ changed lines):**
|
|
1. **Testing** — read \`${ctx.paths.skillRoot}/review/specialists/testing.md\`
|
|
2. **Maintainability** — read \`${ctx.paths.skillRoot}/review/specialists/maintainability.md\`
|
|
|
|
**If DIFF_LINES < 50:** Skip all specialists. Print: "Small diff ($DIFF_LINES lines) — specialists skipped." Continue to ${nextStep}. This threshold only gates specialist dispatch; any core shared-code check still runs.
|
|
|
|
**Conditional (dispatch if the matching scope signal is true):**
|
|
3. **Security** — if SCOPE_AUTH=true, OR if SCOPE_BACKEND=true AND DIFF_LINES > 100. Read \`${ctx.paths.skillRoot}/review/specialists/security.md\`
|
|
4. **Performance** — if SCOPE_BACKEND=true OR SCOPE_FRONTEND=true. Read \`${ctx.paths.skillRoot}/review/specialists/performance.md\`
|
|
5. **Data Migration** — if SCOPE_MIGRATIONS=true. Read \`${ctx.paths.skillRoot}/review/specialists/data-migration.md\`
|
|
6. **API Contract** — if SCOPE_API=true. Read \`${ctx.paths.skillRoot}/review/specialists/api-contract.md\`
|
|
7. **Design** — if SCOPE_FRONTEND=true. Use the existing design review checklist at \`${ctx.paths.skillRoot}/review/design-checklist.md\` and run the mechanical pass at the top of that checklist (the user-installed design detector, when present) before the LLM items
|
|
8. **Simplification** — if DIFF_LINES > 100. Read \`${ctx.paths.skillRoot}/review/specialists/simplification.md\`. Advisory-only lens: hunts unrequested structure (hand-rolled stdlib, one-implementation abstractions, dependencies duplicating platform features), never coverage.
|
|
|
|
### Adaptive gating
|
|
|
|
After scope-based selection, apply adaptive gating based on specialist hit rates:
|
|
|
|
For each conditional specialist that passed scope gating, check the \`gstack-specialist-stats\` output above:
|
|
- If tagged \`[GATE_CANDIDATE]\` (0 findings in 10+ dispatches): skip it. Print: "[specialist] auto-gated (0 findings in N reviews)."
|
|
- If tagged \`[NEVER_GATE]\`: always dispatch regardless of hit rate. Security and data-migration are insurance policy specialists — they should run even when silent.
|
|
|
|
**Force flags:** If the user's prompt includes \`--security\`, \`--performance\`, \`--testing\`, \`--maintainability\`, \`--data-migration\`, \`--api-contract\`, \`--design\`, \`--simplification\`, or \`--all-specialists\`, force-include that specialist regardless of gating.
|
|
|
|
Note which specialists were selected, gated, and skipped. Print the selection:
|
|
"Dispatching N specialists: [names]. Skipped: [names] (scope not detected). Gated: [names] (0 findings in N+ reviews)."`;
|
|
}
|
|
|
|
function generateSpecialistDispatch(ctx: TemplateContext): string {
|
|
return `### Dispatch specialists in parallel
|
|
|
|
For each selected specialist, launch an independent subagent via the Agent tool.
|
|
**Launch ALL selected specialists in a single message** (multiple Agent tool calls)
|
|
so they run in parallel. Each subagent has fresh context — no prior review bias.
|
|
|
|
**Each specialist subagent prompt:**
|
|
|
|
Construct the prompt for each specialist. The prompt includes:
|
|
|
|
1. The specialist's checklist content (you already read the file above)
|
|
2. Stack context: "This is a {STACK} project."
|
|
3. Past learnings for this domain (if any exist):
|
|
|
|
\`\`\`bash
|
|
${ctx.paths.binDir}/gstack-learnings-search --type pitfall --query "{specialist domain}" --limit 5 2>/dev/null || true
|
|
\`\`\`
|
|
|
|
If learnings are found, include them: "Past learnings for this domain: {learnings}"
|
|
|
|
4. Instructions:
|
|
|
|
"You are a specialist code reviewer. Read the checklist below, then run
|
|
\`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE"\` to get the full diff. Apply the checklist against the diff.
|
|
|
|
For each finding, output a JSON object on its own line:
|
|
{\\"severity\\":\\"CRITICAL|INFORMATIONAL\\",\\"confidence\\":N,\\"path\\":\\"file\\",\\"line\\":N,\\"category\\":\\"category\\",\\"summary\\":\\"description\\",\\"fix\\":\\"recommended fix\\",\\"fingerprint\\":\\"path:line:category\\",\\"specialist\\":\\"name\\"}
|
|
|
|
Required fields: severity, confidence, path, category, summary, specialist.
|
|
Optional: line, fix, fingerprint, evidence, test_stub, advisory, evidence_paths, helper_target.
|
|
|
|
Optional extraction advice belongs to the core shared-code check; do not duplicate its proposals. Report real defects in duplicated code independently. Preserve advisory metadata when returning structural advice, and never label a demonstrated defect advisory merely because sharing a helper could fix it.
|
|
|
|
If you can write a test that would catch this issue, include it in the \`test_stub\` field.
|
|
Use the detected test framework ({TEST_FW}). Write a minimal skeleton — describe/it/test
|
|
blocks with clear intent. Skip test_stub for architectural or design-only findings.
|
|
|
|
If no findings: output \`NO FINDINGS\` and nothing else.
|
|
Do not output anything else — no preamble, no summary, no commentary.
|
|
|
|
Stack context: {STACK}
|
|
Past learnings: {learnings or 'none'}
|
|
|
|
CHECKLIST:
|
|
{checklist content}"
|
|
|
|
**Subagent configuration:**
|
|
- Use \`subagent_type: "general-purpose"\`
|
|
- Pass \`run_in_background: false\` on every specialist Agent call — subagents run in the BACKGROUND by default since ${CC_BACKGROUND_DEFAULT_SINCE}, and all specialists must complete before merge. (Merely omitting the flag no longer produces a foreground run; it must be explicitly false.)
|
|
- If any specialist subagent fails or times out, log the failure and retain results from successful specialists for aggregation. Specialists are additive — partial findings are useful evidence, not completed coverage.${ctx.skillName === 'ship' ? ' Step 9.4 stops before Step 10 when a dispatched specialist failed; rerun the missing review before shipping.' : ''}`;
|
|
}
|
|
|
|
function generateFindingsMerge(ctx: TemplateContext): string {
|
|
const isShip = ctx.skillName === 'ship';
|
|
const stepMerge = isShip ? '9.2' : '4.6';
|
|
const stepSel = isShip ? '9.1' : '4.5';
|
|
const fixFirstRef = isShip ? 'Step 9.3 dedup, then Step 9.4 Fix-First' : 'Step 5 Fix-First';
|
|
const critPassRef = isShip ? 'the checklist pass (Step 9)' : 'the CRITICAL pass findings from Step 4';
|
|
const persistRef = isShip ? 'the review-log persist' : 'the review-log entry in Step 5.8';
|
|
return `### Step ${stepMerge}: Collect and merge findings
|
|
|
|
After all specialist subagents complete, collect their outputs.
|
|
|
|
**Parse findings:**
|
|
For each specialist's output:
|
|
1. If output is "NO FINDINGS" — skip, this specialist found nothing
|
|
2. Otherwise, parse each line as a JSON object. Skip lines that are not valid JSON.
|
|
3. Collect all parsed findings into a single list, tagged with their specialist name.
|
|
|
|
**Validate advisory severity first.** If a current finding has \`"severity":"CRITICAL"\` and \`"advisory":true\`, remove \`advisory\` and retain its \`CRITICAL\` severity. Handle it as a normal defect before fingerprinting, partitioning, deduplication, counting, scoring, and Fix-First. Never downgrade severity to make advisory metadata consistent. Valid INFORMATIONAL advisories remain advisory in every category, including simplification. Apply this validation to core and specialist findings alike before combining them.
|
|
|
|
**Fingerprint and deduplicate:**
|
|
For each finding, compute its fingerprint:
|
|
- For a shared-code advisory (category \`shared-libs\` or a \`shared-libs:\` fingerprint), call the installed \`sharedLibsFingerprint\` helper from \`${ctx.paths.skillRoot}/lib/review-evidence.ts\` with literal JSON on stdin, as in the core pass. Recompute from \`evidence_paths\` and \`helper_target\`; never trust a supplied hash or generate hash text yourself. Missing/malformed metadata cannot deduplicate or reuse a saved decision.
|
|
- If \`fingerprint\` field is present, use it
|
|
- Otherwise: \`{path}:{line}:{category}\` (if line is present) or \`{path}:{category}\`
|
|
|
|
The last two rules apply only to other findings. Preserve \`advisory\`, \`evidence_paths\`, and \`helper_target\` through merging. Core review owns shared-code proposals: consolidate equivalent specialist advice with the core proposal and count overlapping savings once. Keep the actual specialist activity in its stats; core-only advice must not create a specialist dispatch or finding.
|
|
|
|
Partition defects and advisories BEFORE grouping by fingerprint. A defect and an advisory must never merge with each other, even if a supplied fingerprint collides. A higher-confidence advisory or prior skipped extraction cannot replace, downgrade, or suppress a demonstrated defect. For findings sharing the same fingerprint within the same partition:
|
|
- Keep the finding with the highest confidence score
|
|
- Tag it: "MULTI-SPECIALIST CONFIRMED ({specialist1} + {specialist2})"
|
|
- Boost confidence by +1 (cap at 10)
|
|
- Note the confirming specialists in the output
|
|
|
|
**Apply confidence gates:**
|
|
- Confidence 7+: show normally in the findings output
|
|
- Confidence 5-6: show with caveat "Medium confidence — verify this is actually an issue"
|
|
- Confidence 3-4: move to appendix (suppress from main findings)
|
|
- Confidence 1-2: suppress entirely
|
|
|
|
**Advisory carve-out (all sources, including core shared-code and simplification):**
|
|
After severity validation, remaining findings with \`"advisory": true\` are excluded from BOTH the quality_score
|
|
summation and the findings-count header below — they are structure suggestions,
|
|
not defects, and must not make "5 findings … 10/10" look contradictory. In
|
|
Fix-First they are ASK-only: NEVER auto-applied, even when mechanical. Also exclude
|
|
them from unresolved-defect totals and clean-status blockers. Preserve normal
|
|
Fix-First handling for any real defect affecting the same code.
|
|
|
|
**Compute PR Quality Score:**
|
|
After merging, compute the quality score over NON-advisory findings only:
|
|
\`quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))\`
|
|
Cap at 10. Log this in the review result at the end.
|
|
|
|
**Output merged findings:**
|
|
Present the merged findings in the same format as the current review:
|
|
|
|
\`\`\`
|
|
SPECIALIST REVIEW: N findings (X critical, Y informational) from Z specialists
|
|
|
|
[For each finding, in order: CRITICAL first, then INFORMATIONAL, sorted by confidence descending;
|
|
advisory findings last, each rendered with an [ADVISORY] label in place of the severity]
|
|
[SEVERITY] (confidence: N/10, specialist: name) path:line — summary
|
|
Fix: recommended fix
|
|
[If MULTI-SPECIALIST CONFIRMED: show confirmation note]
|
|
|
|
PR Quality Score: X/10
|
|
\`\`\`
|
|
|
|
**Simplification footer (after the score line):**
|
|
- If the simplification specialist was dispatched and returned findings, sum
|
|
their \`lines_removable\` values and print: \`net: -N lines possible\` (omit
|
|
findings without the field from the sum).
|
|
- If it was dispatched and returned NO FINDINGS, print:
|
|
\`Simplification: lean already — nothing to cut.\`
|
|
- If it was not dispatched, print neither line.
|
|
|
|
Do not add core shared-code savings to this specialist footer. Explain any overlap once in the core proposal instead of presenting duplicate savings.
|
|
|
|
These findings flow into ${fixFirstRef} alongside ${critPassRef}.
|
|
The Fix-First heuristic applies identically — specialist findings follow the same AUTO-FIX vs ASK classification (except advisory findings, which are ASK-only per the carve-out above).
|
|
|
|
**Compile per-specialist stats:**
|
|
After merging findings, compile a \`specialists\` object for ${persistRef}.
|
|
For each specialist (testing, maintainability, security, performance, data-migration, api-contract, design, simplification, red-team):
|
|
- If dispatched: \`{"dispatched": true, "findings": N, "critical": N, "informational": N}\`
|
|
- If skipped by scope: \`{"dispatched": false, "reason": "scope"}\`
|
|
- If skipped by gating: \`{"dispatched": false, "reason": "gated"}\`
|
|
- If not applicable (e.g., red-team not activated): omit from the object
|
|
|
|
Advisory findings COUNT in the stats \`findings\` field — the advisory
|
|
carve-out governs defect counts, score penalties, and clean-status blockers,
|
|
not specialist activity. Count only findings that specialist actually returned.
|
|
Logging simplification's advisories as \`findings: 0\` would auto-gate the
|
|
lens into permanent silence after 10 dispatches.
|
|
|
|
Include the Design specialist even though it uses \`design-checklist.md\` instead of the specialist schema files.
|
|
Remember these stats — you will need them for ${persistRef}.`;
|
|
}
|
|
|
|
function generateRedTeam(ctx: TemplateContext): string {
|
|
const isShip = ctx.skillName === 'ship';
|
|
const stepMerge = isShip ? '9.2' : '4.6';
|
|
const fixFirstRef = isShip ? 'Step 9.3 dedup, then Step 9.4 Fix-First' : 'Step 5 Fix-First';
|
|
return `### Red Team dispatch (conditional)
|
|
|
|
**Activation:** Only if DIFF_LINES > 200 OR any specialist produced a CRITICAL finding.
|
|
|
|
If activated, dispatch one more subagent via the Agent tool (pass \`run_in_background: false\` — foreground; subagents default to background since ${CC_BACKGROUND_DEFAULT_SINCE}).
|
|
|
|
The Red Team subagent receives:
|
|
1. The red-team checklist from \`${ctx.paths.skillRoot}/review/specialists/red-team.md\`
|
|
2. The merged specialist findings from Step ${stepMerge} (so it knows what was already caught)
|
|
3. The git diff command
|
|
|
|
Prompt: "You are a red team reviewer. The code has already been reviewed by N specialists
|
|
who found the following issues: {merged findings summary}. Your job is to find what they
|
|
MISSED. Read the checklist, run \`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE"\`, and look for gaps.
|
|
Output findings as JSON objects (same schema as the specialists). Focus on cross-cutting
|
|
concerns, integration boundary issues, and failure modes that specialist checklists
|
|
don't cover."
|
|
|
|
If the Red Team finds additional issues, merge them into the findings list before
|
|
${fixFirstRef}. Red Team findings are tagged with \`"specialist":"red-team"\`.
|
|
|
|
If the Red Team returns NO FINDINGS, note: "Red Team review: no additional issues found."
|
|
${isShip ? 'If the Red Team subagent fails or times out, continue through dedup and persistence with dispatched coverage incomplete. Step 9.4 must not certify that pass as completed or clean.' : 'If the Red Team subagent fails or times out, skip silently and continue.'}`;
|
|
}
|
|
|
|
export function generateReviewArmy(ctx: TemplateContext): string {
|
|
// Codex host: strip entirely — Codex should not run Review Army
|
|
if (ctx.host === 'codex') return '';
|
|
|
|
const sections = [
|
|
generateSpecialistSelection(ctx),
|
|
generateSpecialistDispatch(ctx),
|
|
generateFindingsMerge(ctx),
|
|
generateRedTeam(ctx),
|
|
];
|
|
|
|
return sections.join('\n\n---\n\n');
|
|
}
|