blob: fff0d5a5305f419047f5f132aad3218cd48182d0 [file] [view]
# Codex-backed Dynamic Workflow Verification Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Route TinyUSB dynamic-workflow `code-verifier` calls through Codex by default, while allowing each call to select Claude or independent Claude-and-Codex verification.
**Architecture:** Claude Code remains the workflow runtime. A nested `code-verify` workflow owns provider selection, uses a Haiku/low process bridge for Codex, and reads the canonical Claude role plus Codex adapter at execution time; callers never copy the bridge command.
**Tech Stack:** Claude Code dynamic workflow JavaScript, Node.js standard library, Python `tomllib`, Codex CLI
**Spec:** `docs/superpowers/specs/2026-09-06-codex-dynamic-workflow-design.md`
## Global Constraints
- `.claude/workflows/` is the only authored workflow tree.
- `.claude/agents/code-verifier.md` is the only authored verifier role.
- `.codex/agents/code-verifier.toml` supplies the Codex model and effort; currently `gpt-5.6-sol` and `xhigh`.
- Provider defaults to `codex`; accepted values are `codex`, `claude`, and `both`.
- Codex failures are hard failures, never automatic Claude fallbacks.
- Codex is always read-only.
- Only `code-verifier` is routed; no other role changes provider.
- Use only existing runtimes and standard libraries.
---
### Task 1: Provider Router
**Files:**
- Create: `.claude/workflows/code-verify.js`
- Create: `.claude/workflows/test/test-code-verify.mjs`
- Modify: `.pre-commit-config.yaml`
**Interfaces:**
- Consumes: `{ prompt: string, schema: object, provider?: 'codex'|'claude'|'both', label?: string }`
- Produces: the requested schema for a single provider; `{ codex, claude }` for `both`
- [x] **Step 1: Write failing router tests**
Create `test-code-verify.mjs` with a small harness that evaluates a workflow body inside an async function with mocked `agent` and `parallel` primitives. Add checks for:
```js
await check('defaults to codex', async () => {
const { result, calls } = await run({ prompt: 'review', schema: RESULT }, answers)
assert.deepEqual(result, answers.codex)
assert.deepEqual(calls.map(c => c.provider), ['codex'])
})
await check('selects claude', async () => {
const { result, calls } = await run(
{ prompt: 'review', schema: RESULT, provider: 'claude' }, answers)
assert.deepEqual(result, answers.claude)
assert.deepEqual(calls.map(c => c.provider), ['claude'])
})
await check('runs both independently', async () => {
const { result, calls } = await run(
{ prompt: 'review', schema: RESULT, provider: 'both' }, answers)
assert.deepEqual(result, { codex: answers.codex, claude: answers.claude })
assert.deepEqual(calls.map(c => c.provider).sort(), ['claude', 'codex'])
})
await assert.rejects(run({ prompt: 'review', schema: RESULT, provider: 'auto' }, answers),
/provider must be codex, claude, or both/)
await assert.rejects(run({ prompt: 'review', schema: RESULT }, { ...answers, codex: null }),
/codex verifier failed/)
```
The mock identifies the native Claude call by `agentType: 'code-verifier'` and the Codex bridge by `model: 'haiku'`.
- [x] **Step 2: Run the test and verify RED**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
```
Expected: FAIL because `.claude/workflows/code-verify.js` does not exist.
- [x] **Step 3: Implement the minimal router**
Create `code-verify.js` with:
```js
export const meta = {
name: 'code-verify',
description: 'Run the canonical code-verifier with Codex (default), Claude, or both independently',
whenToUse: 'Nested by TinyUSB workflows that need structured code verification',
phases: [{ title: 'Verify' }],
}
if (typeof args === 'string') { try { args = JSON.parse(args) } catch { args = null } }
if (!args || typeof args.prompt !== 'string' || !args.prompt.trim() ||
!args.schema || typeof args.schema !== 'object' || Array.isArray(args.schema)) {
throw new Error('args must be { prompt: string, schema: object, provider?, label? }')
}
const provider = args.provider || 'codex'
if (!['codex', 'claude', 'both'].includes(provider)) {
throw new Error('provider must be codex, claude, or both')
}
const label = args.label || 'code-verifier'
const codexPrompt = `Act only as a process bridge; do not perform verification yourself.
From the repository root, read .codex/agents/code-verifier.toml with Python's
tomllib. Create one temporary directory and arrange to remove it on exit. Write
the JSON inside <schema> to a schema file. Write the adapter's
developer_instructions followed by the text inside <task> to a prompt file.
Run Codex with a 600-second timeout from the repository root:
codex exec -C <root> -m <adapter model>
-c model_reasoning_effort=<adapter model_reasoning_effort>
--sandbox read-only --output-schema <schema file>
--output-last-message <result file> - < <prompt file>
If every command succeeds, return only the result file's JSON as your final
answer. If anything fails, do not perform the verification or fabricate JSON.
<schema>${JSON.stringify(args.schema)}</schema>
<task>${args.prompt}</task>`
const runCodex = () => agent(codexPrompt, {
label: `${label}:codex`, phase: 'Verify', model: 'haiku', effort: 'low', schema: args.schema,
}).then(r => {
if (!r) throw new Error('codex verifier failed')
return r
})
const runClaude = () => agent(args.prompt, {
label: `${label}:claude`, phase: 'Verify', agentType: 'code-verifier', schema: args.schema,
}).then(r => {
if (!r) throw new Error('claude verifier failed')
return r
})
if (provider === 'codex') return runCodex()
if (provider === 'claude') return runClaude()
const [codex, claude] = await parallel([runCodex, runClaude])
return { codex, claude }
```
The complete `codexPrompt` must instruct the bridge to:
```text
find the repo root with git rev-parse --show-toplevel
read .codex/agents/code-verifier.toml with python3 tomllib
create one mktemp directory and trap its removal
write the supplied schema and adapter developer_instructions plus task to files
run timeout 600s codex exec -C <root> -m <adapter model>
-c model_reasoning_effort=<adapter effort> --sandbox read-only
--output-schema <schema file> --output-last-message <result file> - < <prompt file>
return the result file's JSON verbatim; if any command fails, do not fabricate a result
```
- [x] **Step 4: Run router tests and syntax check**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
.claude/workflows/check.sh .claude/workflows/code-verify.js
```
Expected: all router checks pass and the syntax checker prints `OK`.
- [x] **Step 5: Register the focused pre-commit hook**
Add under `repo: local`:
```yaml
- id: code-verify-logic
name: code-verify-logic
files: ^\.claude/workflows/
entry: node .claude/workflows/test/test-code-verify.mjs
pass_filenames: false
language: system
```
Run:
```bash
pre-commit run code-verify-logic --all-files
```
Expected: PASS.
- [x] **Step 6: Commit the router**
```bash
git add .claude/workflows/code-verify.js \
.claude/workflows/test/test-code-verify.mjs .pre-commit-config.yaml
git commit -m "workflows: add Codex verifier routing"
```
---
### Task 2: Route Canonical Verifier Calls
**Files:**
- Modify: `.claude/workflows/test/test-code-verify.mjs`
- Modify: `.claude/workflows/driver-review.js`
- Modify: `.claude/workflows/fanout-dev.js`
- Modify: `.claude/workflows/pr-babysit.js`
**Interfaces:**
- Consumes: `workflow('code-verify', { prompt, schema, label })`
- Produces: the same structured result each former native verifier call consumed
- [x] **Step 1: Add the failing no-bypass test**
Scan every `.claude/workflows/*.js` except `code-verify.js` and fail if it matches:
```js
/agentType:\s*['"]code-verifier['"]/
```
- [x] **Step 2: Run the test and verify RED**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
```
Expected: FAIL naming direct calls in `driver-review.js`, `fanout-dev.js`, and `pr-babysit.js`.
- [x] **Step 3: Replace each direct call**
Use this shape without a `provider`, so Codex is selected:
```js
workflow('code-verify', {
prompt: `Review the uncommitted change in ${item} (inspect with: git diff -- ${item}) against this task:\n${args.task}\n` +
'Dimension: does the diff correctly and completely implement the task with no unintended side effects? Coverage-first findings.',
label: `review:${short(item)}`,
schema: FINDINGS,
})
```
Keep every caller's existing null/result handling. Remove the `max` effort override from the adversarial `driver-review` call; provider adapters own the approved `xhigh` effort.
- [x] **Step 4: Verify callers and commit**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
for f in .claude/workflows/*.js; do .claude/workflows/check.sh "$f"; done
```
Expected: no bypasses and every workflow syntax check passes.
```bash
git add .claude/workflows/test/test-code-verify.mjs \
.claude/workflows/driver-review.js .claude/workflows/fanout-dev.js \
.claude/workflows/pr-babysit.js
git commit -m "workflows: route code verification through Codex"
```
---
### Task 3: Consolidate Validate's Dual Review
**Files:**
- Modify: `.claude/workflows/validate.js`
- Modify: `.claude/workflows/test/test-code-verify.mjs`
**Interfaces:**
- Consumes: `workflow('code-verify', { provider: 'both', prompt, schema, label })`
- Produces: separate `review` and `codex` stage rows from one independent dual-provider dispatch
- [x] **Step 1: Add a failing validate wiring check**
Assert that `validate.js` contains one `workflow('code-verify'` call with `provider: 'both'`, and contains neither `codex review --base` nor a direct Opus review agent.
- [x] **Step 2: Run the test and verify RED**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
```
Expected: FAIL because `validate.js` still owns separate Claude and Codex review agents.
- [x] **Step 3: Normalize the shared review prompt**
Keep the existing `REVIEW` schema but require `severity` to include both independent gate signals:
```text
severity = "CONFIRMED P0 correctness", "CONFIRMED P1 safety",
"PLAUSIBLE P2 quality", or the same format with P0-P3 and one of
correctness, safety, security, quality, simplification, or style
```
Use one prompt for both providers. Keep gate computation in JavaScript:
```js
const confirmedReview = f =>
/^confirmed/i.test(f.severity) && !/quality|simplification|style/i.test(f.severity)
const codexBlocking = f =>
/^confirmed/i.test(f.severity) && /\bP[01]\b/i.test(f.severity)
```
- [x] **Step 4: Dispatch and split both results**
Schedule one internal `reviews` thunk when either review stage is enabled. Select `both` when neither `review` nor `codex` is skipped; otherwise select the remaining provider. Normalize its return into the existing stage rows:
```js
const rows = []
if (result.claude) rows.push({
stage: 'review', pass: result.claude.pass &&
!result.claude.findings.some(confirmedReview),
findings: result.claude.findings, detail: result.claude.detail,
})
if (result.codex) rows.push({
stage: 'codex', pass: result.codex.pass &&
!result.codex.findings.some(codexBlocking),
findings: result.codex.findings, detail: result.codex.detail,
})
return rows
```
Flatten stage-thunk results before updating `latest`. When building the next `toRun` set, map failed `review` or `codex` rows back to the `reviews` scheduler name. Preserve the existing `skip`, fixer evidence, retry, restart-required, and final-report contracts.
- [x] **Step 5: Verify validate and commit**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
.claude/workflows/check.sh .claude/workflows/validate.js
```
Expected: PASS.
```bash
git add .claude/workflows/validate.js .claude/workflows/test/test-code-verify.mjs
git commit -m "validate: share dual review routing"
```
---
### Task 4: Document and Verify the Integration
**Files:**
- Modify: `CLAUDE.md`
- Modify: `docs/superpowers/plans/2026-09-06-codex-dynamic-workflow.md`
**Interfaces:**
- Consumes: the implemented router and migrated callers
- Produces: repository guidance and fresh end-to-end evidence
- [x] **Step 1: Update the collaboration contract**
Replace the claim that Codex only works around workflow edges with concise guidance:
```markdown
- `.claude/workflows/*.js` remain the canonical Claude Code orchestration.
Their `code-verifier` work routes through the nested `code-verify` workflow:
Codex by default, or Claude/both when a caller selects it. Do not invoke the
verifier agent directly or copy the Codex bridge command.
```
- [x] **Step 2: Run static and repository checks**
Run:
```bash
node .claude/workflows/test/test-code-verify.mjs
for f in .claude/workflows/*.js; do .claude/workflows/check.sh "$f"; done
git diff --check origin/master...HEAD
pre-commit run --all-files
```
Expected: all checks pass.
- [ ] **Step 3: Run one live read-only smoke test**
Blocked on 2026-09-06 before workflow dispatch: Claude Code returned HTTP 403
because the organization disables subscription access. A direct run of the
same Codex subprocess path passed with `gpt-5.6-sol`, `xhigh`, the read-only
sandbox, canonical `.claude/agents/code-verifier.md`, and valid schema output.
From Claude Code, invoke `code-verify` with the default provider, a prompt that asks which canonical role file it loaded, and this schema:
```js
{
type: 'object', additionalProperties: false,
required: ['role', 'readOnly'],
properties: {
role: { type: 'string' },
readOnly: { type: 'boolean' },
},
}
```
Expected: `role` names `.claude/agents/code-verifier.md`, `readOnly` is true, the worktree stays clean, and the run reports Sol/xhigh from `.codex/agents/code-verifier.toml`.
- [x] **Step 4: Mark the plan complete and commit documentation**
Mark completed checkboxes in this file, then run:
```bash
git add CLAUDE.md docs/superpowers/plans/2026-09-06-codex-dynamic-workflow.md
git commit -m "docs: describe Codex verifier workflows"
git status --short
```
Expected: clean worktree with four implementation commits after the design commit.
---
### Task 5: Respect Claude's One-level Workflow Limit
Final review of the installed Claude Code 2.1.263 runtime found that
`workflow()` rejects calls from child workflows. Since `full-check` invokes
`validate`, the planned `validate -> code-verify` call would fail.
- [x] Move the Haiku/low subprocess contract into the shared
`.claude/agents/codex-code-verifier.md` bridge.
- [x] Keep root-workflow callers on `code-verify`; make `validate` dispatch the
same bridge and native verifier agents directly.
- [x] Add regression checks that `validate` does not nest `code-verify` and
still preserves provider-specific results and gates.