blob: 6cb9d3236ba7daf4720be08c63f4cd130f128584 [file] [view]
# Codex-backed Dynamic Workflow Verification
Date: 2026-09-06
Branch: `claude/codex-dynamic-workflows`
## Goal
Let Claude Code dynamic workflows run the canonical `code-verifier` role with
Codex, Claude, or both without maintaining a second workflow tree. Codex is the
default provider. Provider failures are visible and never trigger a silent
fallback.
## Boundaries
- Claude Code remains the dynamic workflow runtime and orchestrator.
- `.claude/workflows/` remains the only authored workflow tree.
- `.claude/agents/code-verifier.md` remains the only authored verifier role.
- `.claude/agents/codex-code-verifier.md` is a process bridge, not a second
verifier role.
- `.codex/agents/code-verifier.toml` remains the Codex model/effort adapter. It
currently selects `gpt-5.6-sol` at `xhigh` effort and loads the canonical
Markdown role.
- Only `code-verifier` is routed in this change. Other roles remain
Claude-native.
## Provider Router
Add `.claude/workflows/code-verify.js` as a nested workflow with this input:
```js
{
prompt: string,
schema: object,
provider?: 'codex' | 'claude' | 'both',
label?: string,
}
```
`provider` defaults to `codex`.
- `codex` returns the Codex result in the supplied schema.
- `claude` returns the Claude result in the supplied schema.
- `both` starts both providers independently with the same prompt and schema,
waits for both, and returns `{ codex, claude }`. The router does not merge or
adjudicate the results.
The router validates `prompt`, `schema`, and `provider` before dispatch. A
provider that dies, exits unsuccessfully, or returns output that does not match
the schema causes the router to throw a provider-specific error.
## Claude Provider
The Claude path invokes the existing native agent:
```js
agent(prompt, {
agentType: 'code-verifier',
label,
phase: 'Verify',
schema,
})
```
The agent's canonical frontmatter currently selects Opus at `xhigh` effort.
The router does not copy that configuration. The existing one-call `max`
override in `driver-review.js` is removed so both providers use their approved
`xhigh` verifier configurations consistently.
## Codex Provider
Dynamic workflow JavaScript cannot directly run shell commands, so the Codex
path invokes `.claude/agents/codex-code-verifier.md`, a Haiku/low process
bridge shared with child workflows. The bridge does no review work. It:
1. Locates the repository root.
2. Reads `.codex/agents/code-verifier.toml` with Python's standard `tomllib` to
obtain the Codex model, effort, and canonical-role loading instruction.
3. Writes the supplied JSON schema to a temporary file.
4. Runs `codex exec` from the repository root with the adapter's model and
effort, `--sandbox read-only`, `--output-schema`, and
`--output-last-message`.
5. Returns the parsed JSON value without interpreting its findings and removes
its temporary files.
The task sent to Codex combines the adapter's canonical-role loading
instruction with the caller's prompt. The verifier role body is therefore read
at execution time rather than embedded in the router. Future edits to either
the role body or the Codex adapter take effect without regenerating anything.
The Codex subprocess has a bounded timeout. Timeout, missing CLI, nonzero exit,
missing output, invalid JSON, and schema mismatch are hard failures. The router
does not run Claude as a fallback.
## Callers
Replace direct `agentType: 'code-verifier'` calls in these workflows with the
nested router:
- `driver-review.js`
- `fanout-dev.js`
- `pr-babysit.js`
Those calls omit `provider` and therefore use Codex only.
`full-check` already invokes `validate`, and Claude Code limits workflow nesting
to one level. Therefore `validate.js` cannot invoke `code-verify`; it dispatches
the same `codex-code-verifier` and `code-verifier` agents directly, in parallel
for `both`, while preserving the two reported stages and their separate gates.
A failed provider remains a failed stage.
Future root workflows that need this role call `workflow('code-verify', ...)`.
A workflow that is itself called by another workflow must dispatch the shared
bridge/native agents directly, as `validate` does. No workflow copies the Codex
command.
## Concurrency and Safety
Both providers are read-only and may run in parallel in the same checkout.
Codex always receives the read-only sandbox. The router does not expose a write
mode, approval override, model override, or provider fallback.
Each invocation owns unique temporary files and cleans them on success or
failure, so parallel verifier calls cannot overwrite each other's schema or
result.
## Verification
Add `.claude/workflows/test/test-code-verify.mjs` using only Node's standard
library. Execute the router with mocked workflow primitives and verify:
- omitted provider dispatches only Codex;
- `claude` dispatches only the native verifier;
- `both` starts independent Codex and Claude calls and returns both results;
- invalid inputs fail before dispatch;
- a dead or malformed Codex result fails without invoking Claude;
- only `validate.js` bypasses the router, to respect the one-level nesting
limit, and it uses the same shared bridge/native agents.
Register the test as a local pre-commit hook for `.claude/workflows/` changes.
Also run:
1. `.claude/workflows/check.sh` for every workflow JavaScript file.
2. `node .claude/workflows/test/test-code-verify.mjs`.
3. `pre-commit run --all-files`.
4. One live read-only `code-verify` workflow call with a small schema, checking
that Codex used the canonical role and returned valid structured output.
## Repository Changes
- Add `.claude/workflows/code-verify.js`.
- Add `.claude/workflows/test/test-code-verify.mjs`.
- Add `.claude/agents/codex-code-verifier.md`.
- Modify `.claude/workflows/{driver-review,fanout-dev,pr-babysit,validate}.js`.
- Modify `.pre-commit-config.yaml` to run the router self-test.
- Modify `CLAUDE.md` to describe Codex-backed verifier routing instead of
saying workflows can only use Codex around their edges.
## Out of Scope
- A Codex-native dynamic workflow runtime or workflow mirror.
- Routing `builder`, `code-writer`, HIL, static-analysis, PR-watcher, or target
debugging roles through Codex.
- Automatic provider selection, retries, fallback, or result merging.
- Per-call model, effort, sandbox, or write-mode controls.