Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,15 @@ The Stop hook is fail-open everywhere. Any error returns `{"decision":"approve"}
| `CLAUDEX_STALE_MINUTES` | 15 | Loops older than this are auto-swept on next invocation |
| `CLAUDEX_STATE_DIR` | `.claude/claudex` | State directory location |

## Cost expectation
## Headless Hermes planning bridge

Each plan-mode round is one full Codex review of `PLAN.md`. In practice that's ~25–30k Codex tokens per round. With the default 3 rounds you should expect **~75–90k tokens per `/claudex:plan`**. Codex authenticates against your ChatGPT account, so the bill goes to your ChatGPT Plus / Pro / Team / Enterprise plan, not to claudex. If you're on a tight rate limit, run `--rounds 2` for fast topics and reserve `--rounds 5+` for high-stakes designs.
This fork adds [`bin/claudex-plan-review`](docs/HEADLESS_ADAPTER.md), a production-oriented adapter for running an existing `PLAN.md` through headless Claude Code and Claudex. Its default `sweep-v2` path runs the Phase 1 frozen-snapshot, five-persona lifecycle with a generation cap of `1..5` (the staged Hermes workflow uses five); `--engine legacy` preserves backward compatibility. It validates explicit executable/plugin/auth prerequisites, pins child `PATH`, enforces wall-clock and Claude telemetry-budget bounds, kills the complete process group on timeout, copies complete state/generation evidence, and emits one strict JSON result.

Only `converged` is clean. `max_reached`, `degraded`, `failed`, and `timed_out` are explicit non-clean outcomes. Sweep classification requires terminal authoritative state plus exact same-snapshot five-persona coverage, valid manifests/hashes, and consolidated findings proving clean; Claude prose or one final findings file is insufficient. See the adapter document for usage, exit codes, subscription telemetry wording, architecture, and staging instructions. The in-repo Hermes skill is staged at [`skills/project-plan-review/`](skills/project-plan-review/SKILL.md); it is not installed automatically.

## Subscription usage expectation

Legacy plan mode invokes one Codex review per round. Sweep-v2 can invoke all five required Codex personas in each generation, up to its hard cap of five generations. Claude Code and Codex authenticate through subscription-backed CLIs in this workflow; any dollar-valued fields are usage-equivalent telemetry/bounded-run controls, not direct API billing or an invoice. Both services may still enforce subscription rate limits.

## Safety

Expand All @@ -260,17 +266,20 @@ Highlights:
## Tests

```bash
# Phase 0: confirm platform behaviors work on your machine (50 checks)
# Phase 0: confirm platform behaviors work on your machine (count printed by test)
bash plugins/claudex/tests/platform-validation.sh

# Smoke test: simulate full lifecycle without invoking Codex (60 checks)
# Smoke test: simulate full lifecycle without invoking Codex (count printed by test)
bash plugins/claudex/tests/smoke-test.sh

# Synthetic E2E: real Codex calls against a throwaway repo (19 checks, costs a few cents in tokens)
# Synthetic E2E: real Codex calls against a throwaway repo (count printed by test; uses subscription tokens)
bash plugins/claudex/tests/synthetic-e2e.sh

# Headless adapter deterministic unit/error/timeout/state-isolation tests
python3 -m unittest -v tests/test_adapter.py
```

All three should pass before trusting claudex on a real project.
Run the platform, smoke, and adapter suites for every change. Run the live synthetic E2E when authenticated Codex usage is available.

## Project structure

Expand Down
720 changes: 720 additions & 0 deletions bin/claudex-plan-review

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions docs/HEADLESS_ADAPTER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Headless Claudex planning bridge

`bin/claudex-plan-review` is a bounded adapter for a Hermes leaf subagent (or any automation runner) to drive Claude Code → Claudex Stop-hook → Codex plan review. Its default engine is the feature-flagged Phase 1 `sweep-v2` lifecycle; `--engine legacy` preserves the original round-based adapter behavior.

## Architecture

1. The caller supplies absolute paths for a trusted git repository, an existing non-empty plan, the Claudex plugin, Claude Code, and Codex.
2. The adapter validates paths, plugin files, engine bounds, versions, and both CLI authentication states. It creates no global installation and uses `--plugin-dir` for this Claude session only.
3. If the supplied plan is outside the repository, it is staged as `<repo>/PLAN.md`, reviewed, copied back, and any pre-existing repository plan is restored.
4. Claude Code runs headlessly with `/claudex:plan --engine sweep-v2 --from-draft --skip-interview --rounds N`, stream-JSON output, hook events, an explicit telemetry budget, and a pinned child `PATH`.
5. The supervisor polls newly created `.claude/claudex/*.state` files, records transitions, and preserves the complete sweep generation directory and state under adapter evidence.
6. A wall-clock timeout terminates the entire process group, waits five seconds, escalates to `SIGKILL`, and reaps it.
7. Classification independently revalidates authoritative state, manifest, frozen/live snapshot hashes, all five persona sidecars/findings, aggregate evidence hash, and consolidated findings. Claude prose is never authoritative.

Interactive review mode and the plugin's legacy plan mode are unchanged.

## Exact usage

```bash
/path/to/claudex/bin/claudex-plan-review \
--repo /absolute/path/to/project \
--plan /absolute/path/to/project/PLAN.md \
--topic "grounded feature scope, constraints, and explicit non-goals" \
--engine sweep-v2 \
--rounds 5 \
--timeout 900 \
--budget-usd 5.00 \
--plugin-root /absolute/path/to/claudex/plugins/claudex \
--claude /absolute/path/to/claude \
--codex /absolute/path/to/codex \
--output-dir /absolute/new/evidence-directory
```

`--engine` defaults to `sweep-v2`; its `--rounds` value is the maximum generation count and must be `1..5`. The Hermes project-plan-review workflow uses five. Pass `--engine legacy` for backward-compatible round behavior. `--output-dir` is optional; the default is `.claude/claudex/adapter-runs/<timestamp>-<id>`. `--model` defaults to `sonnet`.

Stdout contains exactly one compact JSON object. Diagnostics go to evidence files. Exit codes:

| Outcome | Exit | Clean | Meaning |
|---|---:|---:|---|
| `converged` | 0 | yes | State is done/converged/clean/coverage-complete and exactly five required personas have valid clean evidence against the same snapshot; manifest, hashes, and consolidated findings all agree. |
| `max_reached` | 10 | no | The generation cap ended with complete authoritative same-snapshot evidence containing material findings. |
| `degraded` | 11 | no | State/evidence is incomplete, contradictory, missing, malformed, mutated, hash-mismatched, cancelled, or otherwise non-authoritative. |
| `failed` | 12 | no | Validation, prerequisite/auth, launch, or Claude execution failed. |
| `timed_out` | 124 | no | The deadline expired; the complete process group was killed and reaped. |

Only `outcome=converged` with `clean=true` is a success gate. Generation five material findings are `max_reached`, never clean. One final findings file is not sufficient evidence for sweep-v2 convergence.

## Machine-readable evidence

Sweep-v2 results expose:

- `engine`, `generation`, and `max_generations` (legacy-only `round`/`max_rounds` remain for compatibility);
- `snapshot_sha256` and `converged_snapshot_sha256`;
- ordered `persona_coverage` for the exact five personas;
- copied `evidence_state_file`, `generation_manifest`, `generation_evidence_dir`, and `consolidated_findings` paths (`state_file` and `final_findings` retain legacy source-path semantics);
- honest `outcome`, `clean`, `reason`, findings classification/severity, process exit, elapsed time, and telemetry.

The state and full review directory—including every generation—are copied into `<evidence>/artifacts/` before result emission, so read-back does not depend on later `.claude/claudex` cleanup. `source_state_file` is informational; gates should use the copied paths.

## Subscription usage and telemetry

Claude Code and Codex are subscription-backed in this workflow. `--budget-usd`, Claude's `--max-budget-usd`, and `reported_claude_cost_usd` are CLI usage-equivalent telemetry and a bounded-run control; they do not prove direct API billing or represent an invoice. Codex subscription usage is separate, may be rate-limited, and cannot be dollar-enforced or measured by this adapter. A sweep generation can invoke all five Codex personas. The timeout is a wall-clock safety boundary, not a billing guarantee.

For Hermes delegation, keep the outer child deadline at least 180 seconds beyond the adapter timeout so the leaf can read and report evidence.

## Installation and Hermes skill staging

No installation is required to run from a checkout; keep executable paths explicit. The staged Hermes skill lives at `skills/project-plan-review/`. Review it in-repo first. Installing it later requires separate operator approval. This repository does **not** install the skill, alter Hermes configuration, or change the active Claude plugin installation.
4 changes: 2 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ note "After /reload-plugins, /claudex should appear in your slash command list."
# ─────────────────────────────────────────────
hdr "7. Platform validation"
# ─────────────────────────────────────────────
if [ -x "$PLUGIN_ROOT/tests/platform-validation.sh" ]; then
if [ -x "$PLUGIN_ROOT/plugins/claudex/tests/platform-validation.sh" ]; then
echo "Running platform-validation.sh..."
echo ""
if bash "$PLUGIN_ROOT/tests/platform-validation.sh" 2>&1 | tail -5; then
if bash "$PLUGIN_ROOT/plugins/claudex/tests/platform-validation.sh" 2>&1 | tail -5; then
:
else
failures=$((failures+1))
Expand Down
6 changes: 4 additions & 2 deletions plugins/claudex/commands/plan.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Run an autonomous plan-and-review loop. Claude drafts PLAN.md, Codex grills it adversarially, Claude revises until LGTM or N rounds.
argument-hint: '[--rounds N] [--from-draft] [--skip-interview] <feature description>'
argument-hint: '[--engine sweep-v2] [--rounds N] [--from-draft] [--skip-interview] <feature description>'
allowed-tools: Bash, Read, Write, Edit, AskUserQuestion
---

Expand All @@ -17,6 +17,7 @@ Parse these flags from the start of $ARGUMENTS (the script handles them; you mai
- `--rounds N`. Override the default max rounds (3). Common picks: 3 (default, fast), 5 (deeper grilling), 7+ (very high stakes).
- `--from-draft`. Use the existing `PLAN.md` in the project root instead of drafting from scratch. PLAN.md must exist and be non-empty.
- `--skip-interview`. Bypass the topic-sharpening interview offer in step 2 below. Useful when you've already nailed the topic or you're in a rush.
- `--engine sweep-v2`. Opt into the Phase 1 frozen-snapshot engine. It requires an existing non-empty `PLAN.md`, runs all five required personas sequentially per generation, defaults to five generations, and never permits more than five. Omit this flag for unchanged legacy plan mode.

## Procedure

Expand Down Expand Up @@ -74,7 +75,7 @@ Run start-loop.sh. **CRITICAL: the topic MUST be passed as a single double-quote

Compose the bash command this way:

1. Identify any flags from `$ARGUMENTS`: `--rounds N`, `--from-draft`. (`--skip-interview` was already consumed in step 2.)
1. Identify any flags from `$ARGUMENTS`: `--engine sweep-v2`, `--rounds N`, `--from-draft`. (`--skip-interview` was already consumed in step 2.)
2. Identify the topic: everything that isn't a recognized flag.
3. Pass flags as-is (no quoting needed). Pass the topic as ONE double-quoted argument.

Expand All @@ -86,6 +87,7 @@ bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan [flags] "<topic>"
bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan "add expiry dates to my links"
bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan --rounds 5 "migrate auth to Clerk's new API"
bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan --from-draft "refactor the billing pipeline"
bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan --engine sweep-v2 "review the existing frozen plan"

# With interview, the enriched topic is also double-quoted:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/start-loop.sh" plan [flags] --interviewed "<enriched_topic>"
Expand Down
12 changes: 7 additions & 5 deletions plugins/claudex/docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ USER: /claudex:plan add expiry dates
┌─────────────────────┐
│ stop-hook.sh │ Reads state file
└─────────────────────┘ phase=reviewing
│ → If signal=no-material-findings: ALLOW + cleanup
│ → If round >= max: ALLOW + "stopped at max"
│ → If signal=no-material-findings: summary BLOCK
│ → If round >= max: summary BLOCK
│ → Else: increment round, BLOCK with new round
CLAUDE either revises PLAN.md (loops back) OR exits cleanly
CLAUDE either revises PLAN.md (loops back) OR prints the summary
Next Stop hook: summarizing → done → APPROVE + cleanup
```

## The state file
Expand Down Expand Up @@ -116,7 +117,8 @@ Every error path leads to approve. The plugin is not allowed to break the user's
The hook branches on `mode` (plan or review) and then on `phase`. Plan mode has the most states:

- `drafting` → check PLAN.md, transition to reviewing, write runner, BLOCK
- `reviewing` → check decision_signal, either ALLOW (done/max) or increment round + BLOCK
- `reviewing` → check decision_signal, either transition to `summarizing` + BLOCK or increment round + BLOCK
- `summarizing` → transition to `done`, cleanup + ALLOW
- `done` → cleanup + ALLOW
- `cancelled` → cleanup + ALLOW

Expand All @@ -141,7 +143,7 @@ The runner script includes:

The challenge: hooks fire AFTER Claude finishes a turn. So how does Claude tell the hook "the loop should end now"?

Answer: Claude updates the state file before ending its turn. Specifically, Claude runs `mark-done.sh` which sets `phase=done` and `decision_signal=no-material-findings`. The hook reads those fields on its next fire and ALLOWs exit.
Answer: Claude updates the state file before ending its turn. Specifically, Claude runs `mark-done.sh`, which leaves `phase=reviewing` and sets `decision_signal=no-material-findings`. The hook reads those fields on its next fire, transitions to `summarizing`, and BLOCKs once so Claude prints the user-visible summary. The following fire transitions to `done` and ALLOWs exit.

This is just a state machine using the file system as a synchronization channel. Simple. Robust. Survives crashes.

Expand Down
Loading