All notable changes to superpowers-v (Compound V) are documented here.
The format is based on Keep a Changelog, and this project uses semantic versioning.
Two approaches internalized from a critical read of the repowise project (AGPL-3.0 — ideas only, NO code copied), then built from scratch against this repo's own history. Six further candidates from the same review were evaluated and rejected on our own data.
Feature A — co-change advisory: the inverse of the scope gate. The scope gate answers "did a worker write OUTSIDE its lane?" — a containment question. It cannot answer the opposite failure: "does this partition own file A but forget partner file B, which this repo's own history says almost always moves with A?" New scripts/compound-v-cochange.py (rules / check) answers that from git log alone — zero model involvement.
- ORDERED rules, never symmetric pairs. It emits
A -> Bwith its own direction:marketplace.json -> plugin.jsonandplugin.json -> marketplace.jsonare two distinct rules with different support and different confidence, because "is B missing when A moves?" is a directional question. - Four conjunctive bars before a rule fires — support ≥ 8, P(B|A) ≥ 0.70, a 95% Wilson lower bound ≥ 0.50 (guards small-sample luck:
8/11reads as 0.73 but its lower bound is 0.43), and narrow support ≥ 3 (co-changes in non-release, non-format commits touching ≤ 10 files — what separates a real coupling from a wide doc sweep). Release and bulk commit counts are reported beside every rule so a headlinesupportcan't be read in isolation. Rename unification (-M) is applied, so a rename cannot manufacture a phantom rule.--explain-rejectionsshows what was rejected and on which bar. - ADVISORY — it adds NO NEW hard gate.
compound-v:partition-reviewernow writes itsPASS/FAILverdict FIRST (new Step 6.5) and only then runs co-change (Step 7), which may only append to an unconditionalWARNINGSsection rendered for BOTH PASS and FAIL. There is noFAIL: COCHANGE_*code and the agent is instructed not to invent one. The guarantee is ordering-bound, not exit-code-bound, because an exit code cannot bind an LLM reviewer — andcheckexits 0 whether or not it finds anything (non-zero is reserved for operational errors), which is what structurally stops a caller from promoting a correlation into a gate. - It does NOT replace either existing CI lockstep guard, and neither guard changed. CI enforces the plugin.json / marketplace.json / CHANGELOG versions exactly, at push time; co-change advises statistically, at partition time, about a file a plan may have forgotten. Both stay.
- "Could not tell" is a distinct answer from "nothing found." A byte-capped git read or a history too short to clear the support bar returns
complete: falsewith a reason and emits no rules at all; the reviewer must report that asNOTE: COCHANGE_INCOMPLETE("could not determine"), never as a clean bill of health. A non-zero git exit is surfaced as an operational error, never flattened into "no rules". - Anti-ruflo: every warning carries support, rate, Wilson lower bound, narrow support and the sample window verbatim — no risk score, no confidence %, no "likely". Inventing a summary metric on top of the counts is precisely the fabricated-evidence failure this project exists to prevent.
Feature B — failure-prioritized, explicitly lossy evidence packing. Every truncator feeding an external judge was a tail-drop, so it amputated exactly the traceback at the END of a log. pack_evidence() in scripts/compound-v-collect-results.py keeps the failure content and drops the filler instead, via a TOTAL 8-rung loss hierarchy (byte-identical passthrough → drop non-failure spans → zero the context radius → priority-allocate and truncate an oversized span → shrink the header → drop the header and all markers → a fixed placeholder → omit the block).
- The claim is exactly "failure-prioritized, explicitly lossy, and always within budget" — NOT "never drops a failure line". Those two are mutually impossible: failure lines alone can exceed any budget, and a log where every line is an
ERRORhas no filler left to drop. Rungs 5-7 are unmarked by construction — there is no room left for a marker, so the rung reached IS the signal (the caller logs it). - Packing runs AFTER redaction, and that ordering is the security property.
redact_uncapped()is extracted fromcompound-v-epic-arbiter.pywith both fail-closed rules intact (unclosed PEM/PGP block, unclosed quoted labelled secret), and the evidence path is redact → fail-closed → pack, on already-sanitized text. Packing only deletes WHOLE lines, so it can never un-redact. Packing first would be a real egress hole, not a style preference: dropping a key'sBEGIN/ENDor apassword=label line destroys the multi-line structure redaction matches on, and a short secret would then also evade the opaque-token regex. Omission markers are path-free by construction andsection_labelis a closed enum. - An over-budget prompt SKIPS the poll with a bounded diagnostic rather than silently truncating it. A missing ballot is honest; a quietly-shortened prompt changes what a judge votes on.
ADR 0002 — any published number ships with its limits in the same document (docs/superpowers/adr/0002-limits-ship-with-the-claim.md). The anti-ruflo CI gate catches fabricated numbers; it cannot catch a number that is entirely real and still misleads because the reader can't see what it was measured on. Every published figure now carries a "What this does not show" note next to the claim. Five alternatives declined, including CONVENTIONS.md (generated — /v:onboard --refresh would silently erase the rule) and a CI grep (a regex cannot separate a claim from a version string, and widening the anti-ruflo gate would false-positive itself into being disabled — the v2.14.1 lesson). The ADR applies the rule to itself and states plainly that it creates no hard gate.
Selftests under LANG=C on the Python 3.9 floor: compound-v-cochange.py 69 cases; compound-v-collect-results.py 94 checks; compound-v-epic-arbiter.py 237 checks (was 196 — the original 196 are untouched); compound-v-scope-check.py passing; frontmatter lint clean. Run against this repo's own history, the engine emits six firing ordered rules and nothing sub-threshold.
Those six rules were measured on one repository — this one — over the 381 eligible commits in its history at the time of measurement, with a ≤ 10-file narrow-support bar calibrated to this repo's commit width. A young repo, a squash-merge-only history, or a wide monorepo will legitimately produce no rules at all; that is a correct result, not a failure. A rule is a correlation in past commits — not a causal claim, and not a contract that two files must move together. The historical single-file touches behind these rules are unpaired historical touches: an unadjudicated signal. None was adjudicated as a violation and this release does not claim any was one. Nothing here measures whether the advisory improves review outcomes, catches real omissions in practice, or saves any time or tokens — no such measurement was taken.
During the brainstorm/elicitation phase Compound V already intercepts, it now remembers your own dated past decisions and surfaces them as falsifiable history — always paired with a divergent counter-move, so a recall triggers re-examination, not autopilot. New scripts/compound-v-preferences.py (recall/capture/distill/stats/purge) + /v:preferences + a brainstorm.preferences config key. Grounded by three pre-flights whose domain audit reframed the feature: the original "let the brainstorm reason as the creator" clone was rated high-hazard (choice-blindness confabulation, default-nudge dark patterns, echo chamber, opposing this project's own anti-anchoring moat), so v1 ships the safe memory + challenge framing instead.
- Three modes
off | on-demand (default) | marked.markedputs a soft, falsifiable dated badge (↩ your past pick: N/M · date) beside the matching option — a label, never a pre-selected default (a mark is information; a pre-tick is an answer you must override — the audit's red line). Every surfacing is challenge-paired or suppressed (no-challenge). - The "why" is captured UNPROMPTED (free-text first); a tapped candidate is a weaker
borrowedclass, excluded from the distilled "your reasoning" — never an inferred rationale. - Anti-anchoring: suppressed on recon-touched / high-novelty forks (never fires where Trigger-0 widens). Drift honesty: recency-weighted last-K disagreement demotes + banners a shifting pattern; a holdout probe records un-nudged choices; patterns auto-expire.
- Split storage: the raw
decisions.jsonlstays LOCAL (~/.claude/compound-v/preferences/, private,purge-able); the distilledpreferences.mdis written in-repo (docs/superpowers/preferences/, git-tracked → V-memory,/v:remember-able) and is secret+PII-scrubbed before write. - Anti-ruflo: counts only (
4/5 similar forks), never a fabricated confidence%; recall is evidence, never an authority — the brainstorm human-gate is untouched. Pure Python 3.9 stdlib (reuses V-memory'sfts5_escape+redactandappend_lineby import);--selftestauto-run by the CI all-selftest gate.
Closes the plugin's biggest competitive gap (no observability UI) — while keeping the no-daemon / git-derived-control philosophy. New scripts/compound-v-dashboard.py renders docs/superpowers/execution/** (runs, epics, per-job status, scope-gate verdicts, usage, blocker ledger) as a browser view; wired through /v:status --html|--serve and the new /v:dashboard command.
emit— a self-contained static HTML snapshot (data inlined, offline, theme-aware — for sharing / audit), written to a git-ignoreddocs/superpowers/execution/dashboard.html.serve— an ephemeral, read-only,127.0.0.1-only live viewer that auto-refreshes as a run/epic progresses (the local equivalent of a competitor's live agent UI). It is a foreground process you Ctrl-C; it never backgrounds, never auto-launches, binds loopback only, serves GET/HEAD only (any other method → 405), is realpath-contained to the execution root (traversal / symlink-escape → 403, non-.json/.html/.yaml→ 404, no directory-listing leak), and writes nothing to any run dir.- Read-only by design — observe in the browser, control via the CLI. No merge/kill/retry buttons; the guarantees stay git-derived and human-gated (the moat, not a gap).
- Anti-ruflo — a dashboard that does not lie: renders only what is in the state files — real counts (never a fabricated
%-progress), measured-only usage (—when a backend reports none, never a fabricated0), and only real timestamps sourced from the state files. Degrade-safe: a run with onlymanifest.yamlshows "no state yet", malformed JSON shows "unparseable", an empty root shows "no runs yet" — never a crash. - Pure Python 3.9-safe stdlib (
http.server, no Flask/CDN/npm);--selftest(auto-run in CI by the v2.14.1 all-selftest gate); security posture verified live (loopback bind, 405/403/404 on the attack cases).
A 5-dimension health audit (dead-code · CI/test-coverage · doc/skill-drift · contract-consistency · safety) found the plugin CODE clean (all safety invariants hold, contracts tight) but two real holes in the CI safety net that let regressions ship green.
- CI ran only 4 of 29 script
--selftests.validate.ymlnow runs everyscripts/*.py --selftest(dynamic discovery) under the Python 3.9 floor — previously only the marathon quartet (epic-state/arbiter/watch/headless-shim) was defended, leaving the scope gate (compound-v-scope-check.py), model resolver, pre-eval, usage, memory, and the collector uncovered. It also validates every tracked run-manifest (not just the example) and hard-fails if the validator script is missing. - The intra-plugin dead-link guard was a silent no-op. Its
fail=1was set inside a pipedwhilesubshell and never propagated, so the guard printed dead links but never failed the build. It now accumulates hits in a temp file, covers.py/.sh/.json/.ymllink targets (not just.md), and strips:linesuffixes. Fixed the 10 dead cross-refs it now catches. - Test coverage: added
--selftesttocompound-v-collect-results.py(33 checks over the real job_result conformance logic CI previously only checked via a drift-prone reimplementation) andcompound-v-update-memory.py(15 checks). - Cleanup: removed two dead helpers (
_split_lines,_repo_root_default) and a straymodels.err(now gitignored). - Docs: corrected the stale "devin/opencode worker not yet built" claim (both scripts are built; auth-pending/unverified), added
/v:adrto the AGENTS.md command table, and documented the v2.14 headless--allow-buildopt-in.
Built as one dogfooded epic (docs/superpowers/execution/2026-07-14-v2.14-blockers-and-headless/), grounded by three LIVE pre-flights (archaeology · domain · library) that reshaped the design before a line was written, plus three user policy decisions.
Confirmed blockers — done_with_blockers now reachable via a genuine 2nd external family.
- The marathon arbiter panel (
compound-v-epic-arbiter.py) now polls a second, distinct external model family — Gemini viaagy— read-only, alongside Codex (GPT). The advisory poll passes an explicit resolved Gemini--model(family derived from that string, fail-closed —agy1.1.1's catalog is no longer Gemini-only), reads stdout, reuses the Codex redaction/parse/security-boundary path, and passes no--dangerously-skip-permissions(verified live:agy --printanswers read-only without it). - A blocker is CONFIRMED only when ≥2 distinct external families agree on the SAME
blocker_category(closed enum:credential | external-account | infra | third-party-data | legal-approval | human-decision) — not merely theblocked_externallabel. This defends the correlated-oracle false-confirm (two LLMs hallucinating different missing facts under the same label). compound-v-epic-state.pyderivesconfirmedfrom the arbiter's frozen audit (bound via a new--audit-file, realpath-contained + validated: matchingepic_id/feature/blocked_externaldisposition,audit["confirmed"] is True) — never from a caller-supplied--families-agreeingCSV (which stays as recorded metadata), and raw--confirmed/--blocker-confirmedbooleans stay hard-rejected. It adds thedone_with_blockersterminal (a successful, auto-merging terminal) + an awaiting-final-review pre-terminal + the mandatoryis_terminalprefix; records the agreed--blocker-categoryon the ledger; auto-sets a durableblocker_audit_dueobligation on a confirmed blocker (gatesrecord_final_review(passed)+ the terminal until an approved re-review clears it, with an atomic--record-blocker-audit-failedrevert on ISSUES); and relaxesrecord_final_review(passed)to accept an epic whose only non-donefeatures are confirmed-blocked. An abandoned/halt_featurefeature or a SUSPECTED (unconfirmed) blocker still halts toblocked_needing_human. The checkpoint (non-marathon) path is byte-identical./v:epicauto-mergesdone_with_blockersvia the final integration review →finishing-a-development-branch(the chosen policy); a confirmed blocker is always over-sampled by a durable-obligation PASS-integrity re-review (verifying the frozen audit'sconfirmed, ≥2 distinct external families on the same category, and no retry dissent); and the blocked remainder (feature · category · families · evidence) is surfaced to the human, never silently dropped. Framed honestly: ≥2 distinct families is the minimum defensible bar — distinct-family LLM votes are correlated (shared pretraining/RLHF), not fully independent — paired with same-category agreement + audit over-sampling, not treated as strong independent corroboration.
Headless resurrection shim — opt-in, present-only.
- New
compound-v-headless-shim.py emit --os macos|linuxprints a macOSlaunchdplist / Linux cron entry + runbook so a user can opt into resurrecting a marathon epic while the desktop app is closed. It is present-only — the plugin neverlaunchctl/crontab-installs it (AST-asserted) and never runs the agent. - The emitted command uses
--permission-mode dontAsk+ a curated--allowedToolsallowlist (runs read-only + allowlist, refuses everything else) — never a bypass flag. The runbook carries a prominent DO-NOT block referencing the repo-deletion incident. It bakes an absoluteclaudepath (fails the emit if unresolved),/dev/nullstdin, and printslaunchctl bootstrap gui/$UID(modern) as the user's install step. Honest boundary: launchd fires on wake with one coalesced catch-up; it does not run while powered off/asleep, and agui/$UIDLaunchAgent needs a GUI login.
.github/workflows/validate.ymlruns the newcompound-v-headless-shim.py --selftestunder the Python 3.9 floor alongside the existing epic-state/arbiter/watch selftests.
Two features, built as one dogfooded epic (docs/superpowers/execution/2026-07-13-usage-and-advisor/), grounded by three LIVE pre-flights that changed the design before a line was written.
- Feature A — measured usage on
job_result. A new optionalusageobject ({input_tokens, output_tokens, advisor_calls, backend, measured}) is threaded worker → collector → aggregator →/v:status, recording ONLY real measured backend output (anti-ruflo: never an estimate).scripts/compound-v-usage-extract.py(new) normalizes per-backend event streams (verified live, not from training data): codexturn.completed.usagesummed across turns, opencodestep_finish.part.tokens, cursorresult.usage. Backends with no machine-readable usage (antigravityagy, claude-via-Task subagent, devin) emitmeasured:false+ null tokens — fail-open, never a fabricated number.scripts/compound-v-collect-results.pybuild_result()now passesusagethrough (a pre-flight-caught blocking gap: the collector re-synthesizes every result and previously dropped it, so every measured value was silently discarded).scripts/compound-v-usage-aggregate.py(new) rolls usage up per ticket/feature/epic, countingmeasured:falsejobs as "unmeasured" rather than zero./v:statusgains a degrade-safe usage column; the old blanket "no token metrics" line is reworded to permit MEASURED usage while still banning estimates.
- Feature B — on-demand cross-brand advisor (opt-in, subagent pattern). A cheap Sonnet executor consults a stronger advisor of a preferably DIFFERENT brand (Codex if available, else Opus) only on a hard sub-decision.
- Live pre-flight REFUTED the assumed
claude -p --advisorflag (it does not exist) and rejected the realadvisor_20260301API tool (requires an API key +anthropicSDK, breaking the plugin's pure-stdlib/no-service/subscription ethos). Advisor is therefore a harness subagent pattern. scripts/compound-v-resolve-model.pyexposesadvisor_eligible(a standard/core-slice implementer OR a fast-path Claude worker) and a cross-brand advisor selector (codex > other non-claude > Opus fallback; never Haiku).scripts/compound-v-validate-manifest.pyvalidates an optional per-jobadvisor:block and rejects it on ineligible job types; manifests without it stay valid.scripts/compound-v-advisor-consult.sh(new) runs ONE READ-ONLY advisory turn —codex exec --sandbox read-onlyorclaude -p --model opus --permission-mode plan, and NEVER--dangerously-skip-permissions. A read-only advisor that cannot write files structurally forecloses the 2026-07-13 nested-bypass-agent incident. Proven by a fake-backend stub test with no live run.scripts/compound-v-preeval.pygains anadvisor_calls → escalatesensor (a fail-open, escalation-only clone of thechurn_hottriad): repeated advisor consults are a post-run signal that a job was harder than its tier.
- Live pre-flight REFUTED the assumed
v2.10 shipped the Marathon Loop but deliberately deferred auto-resurrection: a hard death still needed a human to re-run /v:epic <epic-id>. v2.11 closes that gap with an ADDITIVE opt-in watch surface on top of marathon (--watch at --init, rejected without --stance marathon; no in-place upgrade of an existing epic, same rule as marathon itself). A watch-off marathon epic stays byte-identical to v2.10 — none of the fields below are ever written for it.
- V1 — atomic resume authority + liveness heartbeat (
scripts/compound-v-epic-state.py):--claim-resumeis the crux — ONEfcntl.flock-guarded atomic transaction that decides whether a scheduler-fired session may resume a dead epic, returning{"claimed","reason":"claimed|live|terminal|resume-cap","resume_count"}. There is no pid or lease object involved — the Claude Code harness has no stable driver pid across shell calls, so a FRESHlast_progress_atheartbeat alone defers the claim (live) and the--claim-resumeflock is the sole ownership/serialization authority, closing the duplicate-resurrection gap an earlier pid-lease design would have had.--livenessis a read-only watcher poll ({"incomplete","stale","epic_status","terminal","resume_count"});stalerequires incomplete, non-terminal, and past a heartbeat threshold (default 45 min) — heartbeat age is the whole staleness signal.--renew-leaseis the live driver's own heartbeat call (kept under its original flag name for driver-side stability): it simply bumpslast_progress_atto now, no pid, no TTL, nothing to create-or-renew. A newresume_countglobal breaker axis (max_resume_count, default 20) permits N resumes and blocks the (N+1)th, tripping the sameblocked_needing_humanlatch as every other breaker;--clear-breaker --reset-resume-countre-arms it. Built directly on v2.10's crash-safe resume — nothing about the existing single-process marathon path changed. - V2 — two-tier watcher (
scripts/compound-v-epic-watch.py, new): never talks to a scheduler directly and never re-implements any state-spine logic.emit-promptprints a SELF-CONTAINED resume prompt for a scheduler to hand to a fresh, memoryless session — that session calls--claim-resume, branches on the result, and performs the full disarm inline on a terminal/resume-cap verdict (a cold-prompt design: no conversation history is assumed).planreads--livenessand advises the two tiers' cadence (off-minute:17/:47, ~30 min apart) and whether to disarm. The driver (/v:epic, not this script) owns the real scheduler wiring — sessionCronCreate/CronDeletefor Tier-1,mcp__scheduled-tasks__create_scheduled_task/delete_scheduled_taskfor Tier-2. - V3 — idempotent watcher registry + driver arm/disarm + capability detection:
--record-watcher-armed/--record-watcher-disarmed/--list-watcherstrack scheduler tasks idempotently by(provider, task-id), so a crash-and-replay during arming or disarming is a harmless no-op, never a duplicate or a leak./v:epic's marathon loop (commands/v-epic.md§0c "Watch-on marathon start" and "Watch disarm") bumps the heartbeat and arms both tiers once at invocation start (recording an intent record before each real scheduler create call, so a crash mid-arm never double-arms on re-entry), re-arms a Tier-1 task past its ~7-day expiry, and disarms both tiers (plus a deterministic-id fallback, attempted even when the registry is empty) at every terminal exit./v:initgained a capability-detection step for scheduler availability on this machine, feeding theepic.autonomy.watchconfig key (consulted only once, at a NEW epic's--init— the persistedepic-state.jsonis the sole authority afterward). - CI:
.github/workflows/validate.ymlnow also runspython3 scripts/compound-v-epic-watch.py --selftestin the same Python 3.9 step as the existingcompound-v-epic-state.py/compound-v-epic-arbiter.pyselftests.
Auto-resurrection is bounded and partial, not magic:
- Tier-1 (session
CronCreate) pauses while the session is unavailable or busy, MISSES any fire that elapses while paused (no catch-up), may restore on the next conversation turn while still unexpired, and expires after 7 days even inside a continuously open session. - Tier-2 (
scheduled-tasks, on-disk) runs only while the desktop app is open and the machine is awake; it performs exactly ONE catch-up for the most recent missed run on app start/wake, within 7 days. It is not an always-on server. - "Survives quota exhaustion" holds only if the quota has since reset AND the session is still authenticated — an expired OAuth token still needs a human.
- A machine that is truly off (laptop closed, asleep) is not covered by either tier. Genuine machine-off execution needs remote infrastructure, never claimed built-in here.
- Resurrection is bounded by
max_resume_count(default 20) — a persistently-dying run halts atblocked_needing_humanfor a human, same as any other tripped breaker.
Opt-in (epic.autonomy.watch, default off); the default epic and a watch-off marathon are unchanged. No fabricated cost/token metrics anywhere in this surface.
Built on v2.10's crash-safe marathon resume. Cross-model reviewed by Codex gpt-5.6-sol.
An opt-in marathon stance for /v:epic: instead of stopping at every feature checkpoint, the epic can chew through the whole runnable feature DAG in one invocation. The default checkpoint epic is behaviorally unchanged — marathon is chosen only at --init time and cannot be flipped onto an existing checkpoint epic. Scope was deliberately PHASED after three Codex Sol xhigh review rounds converged that every critical concurrency finding traced to auto-resurrection (a two-tier watcher reviving the epic while you're away); removing that from v2.10 makes the marathon single-process and the whole class of concurrency criticals disappears. See docs/superpowers/specs/2026-07-12-epic-autonomous-mode-design.md for the full scope decision and the deferred v2.11 sketch.
- Marathon Loop + DAG-autonomous routing (
scripts/compound-v-epic-state.py): a marathon-onlyautonomystate block,--next --autonomous(a separate, read-only routing function from the default--next— byte-identical default behavior preserved) that routes on deterministic DAG reachability, so an abandoned or blocked feature removes only its transitive dependents, never its independents.attemptstracking,--can-retry,--record-disposition,--record-final-review. Terminal states:done(all features done and a persistedfinal_review.status=="passed"— never on feature-completion alone),blocked_needing_human(a tripped breaker, ahalt_epicverdict, or exhausted reachable work),running_with_failures(non-terminal).done_with_blockersis defined but structurally unreachable in v2.10 (needs a 2nd confirming external model family — deferred to v2.11). - Cross-model Arbiter Panel (
scripts/compound-v-epic-arbiter.py, new): classifies a feature FAILURE via a two-phase, challenge-bound API —--prepareissues a bounded Claude ballot-task prompt tied to{epic_id,feature,attempt,challenge_id}(an HMAC-keyed, per-epic challenge secret; a mismatched/replayed/stale challenge is dropped before any model call), then--classifypolls Codex (real sandbox, read-only, through the timeout supervisor, evidence size-capped and secret-redacted before egress) and validates a driver-supplied Claude ballot, aggregating both with a complete, deterministic truth table. Family-diverse aggregation: ballots collapse one-per-family (gpt/gemini/claude/grok/unknown); a parse-failed or errored ballot is dropped and logged, never fabricated as a vote; empty or tied → conservativehalt_feature;retry_fixpast the per-feature retry cap is masked tohalt_feature. Antigravity/Cursor are excluded from arbitration (no kernel write-confinement) — implementation workers only, never advisors. Fail-closed secret redaction before any external-model egress (labelled tokens, auth headers, private keys, URL credentials, multiline/unclosed-quote secrets — omits the suspect evidence rather than risk a half-redacted leak). O_NOFOLLOW evidence containment: every untrusted path under the arbiter's audit directory is opened via dir-fd +O_NOFOLLOW(TOCTOU-safe — never validate-a-name-then-reopen-it), atomic tmp+rename writes, capped/rotated audit JSONs so an all-night run can't fill the disk. Every ballot + resolved family + aggregation reason is frozen todocs/superpowers/execution/epics/<epic-id>/arbiter/<feature>-<attempt>.json. - Blocker Ledger (
scripts/compound-v-epic-state.py): "do everything you can" — finish everything reachable, isolate only the genuinely impossible, escalate with proof, never halt the rest. Ablocked_externaldisposition marks a featureblocked(ledger entry) without halting the epic;--next --autonomousskips it and routes around only its transitive dependents. v2.10 blockers are always SUSPECTED —--blocker-confirmed trueis hard-rejected everywhere it could be set; CONFIRMED (≥2distinct known external model families agreeing) is structurally unreachable on a Codex+Claude-only panel and is deferred to v2.11 alongside a second safe external family and thedone_with_blockersterminal. - Global circuit breakers + human resume (
scripts/compound-v-epic-state.py):total_attempts,no_progress_cycles(a full autonomous pass that advancesdoneby zero), and wall-clock hours sinceautonomy.started_at— counts and hours only, never a fabricated cost.--breaker-checkis read-only;--trip-breakeratomically parks the epic atblocked_needing_human. Re-checked before every feature and before every model call (arbiter, sample-audit, final review) — an honest, not a hard real-time, guarantee. Human recovery, never automatic:--clear-breaker(--reset-wall-clock,--set-max-total-attempts N) re-arms a tripped breaker;--clear-dispositionclears a stickyhalt_epicverdict — both followed by the human re-running/v:epic <epic-id>, which is re-entrant and resumes the marathon fromepic-state.json. - PASS integrity — anti-reward-hack gate (
agents/spec-reviewer.md§2.5,commands/v-epic.md): a marathon SUCCESS is not blindly trusted. The reviewer contract gained a deterministic "did this diff weaken its own tests/scorers to pass?" check; the marathon driver sample-audits a deterministic fraction of PASSes (every 3rddonethis invocation, plus always the first) with a fresh adversarial re-review, and gates terminaldoneon a final cross-feature re-verification (--record-final-review) over the whole accumulated diff sinceautonomy.start_sha. - CI:
.github/workflows/validate.ymlnow sets up Python 3.9 (the documented marathon-scripts floor) and runs both new--selftestsuites (compound-v-epic-state.py,compound-v-epic-arbiter.py) as a required job — a red selftest now fails CI.
Marathon is opt-in; the default epic still checkpoints. "Survives a fall" means two things, both true today and neither overclaimed: in-session, the loop continues past a soft per-feature error to the next runnable feature automatically, within the one live /v:epic invocation. After a hard death (quota, closed terminal, crashed machine), a human re-invokes /v:epic <epic-id>, which resumes from the committed epic-state.json. There is no automatic resurrection while you're away in v2.10 — nothing wakes the epic back up on its own. That is the deferred v2.11 auto-watcher (Execution Lease + Two-Tier Watcher + generation-fenced execution across the dispatcher/worker/merge-back/commit layer) — its own spec, its own review pass, because it needs correct distributed concurrency that the v2.10 single-process design deliberately avoids. No fabricated cost/token metrics anywhere in either stance.
Converged from four independent pre-implementation reviews plus three Codex Sol xhigh adversarial rounds on 2026-07-12 that drove the phased-scope decision. Built across four disjoint units (state spine → arbiter panel → driver/reviewer wiring → this docs/CI/release unit) and cross-model reviewed by Codex gpt-5.6-sol.
A fast, cheap Pre-Evaluation stage now runs before Trigger 0, scores each change request on two separate axes, and — only when a change is provably trivial and low-impact — OFFERS a proportionate fast-path. Everything else routes to the full pipeline. The request-level score never auto-routes; it only ever offers (Iron-Invariant #4). Fail-closed is the law everywhere: any ambiguity, missing data, tier disagreement, unknown axis, budget overrun, or parse failure → FULL_PIPELINE (or escalate, post-diff). Never fail open.
- Two-axis truth-table scoring, no raw LLM magnitude (
scripts/compound-v-preeval.py,skills/compound-v/phase-preeval.md). Bands (difficulty ⊥ impact) are assembled by deterministic logic from tiered evidence — path patterns (T1), a calibrated fast-path history (T2), and a singlelight-tier classify (T3) invoked by the parent harness as a Task, never from Python. The derived 1-10 is a post-decision band-midpoint DISPLAY label, never the gate. Six hard Layer-A overrides (localization-failed, sensitive-path, shared-token/a11y/generated, semantic-vs-path disagreement, churn-hot, unknown-axis) each short-circuit to FULL with zero further cost; a fired override needs zero model calls. - Bounded localization (
scripts/compound-v-localize.py): alowverdict is impossible until a bounded, read-onlylocalize()(rg → git grep → grep degrade, hard file-cap + timeout, every external CLI routed through the timeout supervisor withstdin </dev/null) has resolved real paths / tokens / fan-out. Writes a committed localization artifact the fast-path manifest binds against. - Content-pattern taxonomy (
.claude/compound-v-impact-taxonomy.example.yaml,scripts/compound-v-taxonomy.py): impact is decided on what a change semantically is, not only where its file lives (AC-8). Kinds includeshared_tokenanda11y— a "cosmetic" color that is really a brand/contrast-compliance surface, or anaria-labelthat silently breaks WCAG, both escalate. Regex patterns are a documented safe subset (no nested quantifiers), deterministically validated and matched inside a killable subprocess (AC-16). - Cross-artifact-bound fast-path manifest, materialized by a dedicated owner (
scripts/compound-v-fastpath-materialize.py, AC-14): an acceptedFASTPATH_ELIGIBLErecord is materialized into a run whose single-implementer manifest (review modeled as a dispatcher phase outsidejobs, not a job) passescompound-v-validate-manifest.py --mode pre-dispatch— the solewrite_allowedliteral equalslocalization.resolved_paths[0], andpre_eval_id/ decision /taxonomy_digest/ localization content-digest are all validator-enforced to be equal across manifest + record + artifact (AC-13). A tampered or ineligible record is rejected fail-closed before any write or commit. - Normalized escalation-only churn (
scripts/compound-v-churn.py): generated/vendor paths and pure-format commits are excluded (single-sourced in the taxonomychurnblock). A churn-hot path escalates; absence or an insufficient sample never lowers. - Sibling post-diff re-classifier (
scripts/compound-v-postdiff-reclassify.py, AC-5): a separate analyzer (never an extension of the hardened name-only scope gate) runs pre-merge against the pinned baseline and the same authoritative changed-path set. It answers one question — "does the materialized diff still deserve the fast-path, or must it ESCALATE?" — via sensitive-path touch, size accounting (tracked numstat unioned with separately-measured untracked bytes), a shared taxonomy content re-check over changed hunks, and a typed structural pass (a real stdlib-astanalyzer for Python; JS/TS/Go/Ruby fail closed unless provably trivial). Any uncertainty escalates. - Three new state-machine states, idempotent + crash-consistent escalation (
skills/compound-v/state-machine.md, AC-15):PRE_EVAL_DONEis a record-status field (nostate.jsonexists at prediction time), whileFASTPATH_DISPATCHED/ESCALATION_REQUIREDare real phases. Escalation mints a new run-id and never mutates the frozen manifest (AC-4); a two-phase protocol (commit patch+baseline evidence → deterministic child run-id → create+commit child → commit parentescalated_to) reconciles partial states on resume, discovering an existing child before minting one. - Three-event triage-outcomes + git-derived precision in
/v:status(scripts/compound-v-triage-outcomes.py, AC-3/AC-12): telemetry is strictly append-only —predicted→bind→actual, joined on the write-oncepre_eval_id, no back-fill. Precision is computed from the fast-path parent outcome only, git-derived, and reportsinsufficient(never a fabricated number) on an empty or below-floor stream (AC-10). The escalation child contributes escalation evidence, never a healthy signal (cohort separation). /v:init+/v:onboardwiring:/v:initgainspre_eval.*config (fail-closed defaults, malformed → warn → default) and revocable remember-my-choice per taxonomy-category (AC-11 — a remembered choice skips the OFFER for that category only; it can never bypass the fail-closed overrides)./v:onboarddrafts a first-cut taxonomy + churn-exclusion block from the repo's structure, kept/edited by a human at the gate (never auto-applied). Thin ADR capture is exposed via/v:adr.
tests/v2.9-e2e/test_fastpath_and_escalation.py— a runnable stdlibunitteste2e suite (12 cases, green underLANG=C) driving the REAL merged scripts end-to-end: AC-1 (shared-token "make button red" → FULL via override #3), AC-11 (a css-only-remembered request still escalates on a shared-token/a11y surface; the engine has norememberparameter), AC-3/7 (accepted fast-path → materialized manifest passes--mode pre-dispatch; clean diff does not escalate; a sensitive-path or shared-token diff does), and AC-10/12 (precision reportsinsufficient, never a number, on an empty/below-floor stream).
Built by Compound V dogfooding itself — the feature shipped as a 16-job manifest dispatched through the very orchestrator it extends. The plan was hardened to convergence across 5 Codex plan-review rounds (each reject → accept-all, closing 3–4 crit + high findings per round, folded into a single Lifecycle & commit-ordering protocol as the release's one authority).
- Structured session-id capture. The headless codex worker now runs
codex execwith--jsonand parses the first{"type":"thread.started","thread_id":"<uuid>"}event, carrying the UUID-validatedthread_idinside the canonicaljob_result.session_id(empty when the event is absent) — replacing the brittle stderr banner scrape; the dispatcher persistssession_id+failure_classintostate.json jobs[<id>]. (scripts/compound-v-run-codex-worker.sh,skills/backend-launcher/adapter-codex.md) logs/<job-id>.jsonlrun-dir convention. A new--events-log <path>worker arg tees the--jsonevent stream todocs/superpowers/execution/<run-id>/logs/<job-id>.jsonl; the dispatcher records that same path instate.json jobs[<id>].log. Standalone worker use keeps an$ARTdefault, so the arg is optional and degrade-safe. (agents/parallel-dispatcher.md,skills/compound-v/state-machine.md)- Liveness JSONL signal.
classify_job()now reads the events-log's newest line when present: an event newer than the staleness threshold is a WORKING signal, an older newest-event reinforces STALE. Malformed/partial JSONL never raises — it falls through to the prior git+FS+pid behavior. Nologfield ⇒ identical prior behavior. (scripts/compound-v-liveness.py) --ephemeraldiscovery review.compound-v-codex-review.shadds--ephemeralto its singlecodex execinvocation — discovery rounds must not persist or resume (statelessness is the anti-anchoring point). Never added to the worker. (scripts/compound-v-codex-review.sh)
- Resume/parallel-dispatcher contradiction reconciled.
v-resume.mdandparallel-dispatcher.mdnow state a byte-identical resume-eligibility rule: a codex job may be resumed viacodex exec resume <captured-uuid>IFF itsfailure_classis environmental (timeout | network) AND its worktree still exists; every other case recreates the worktree fresh at HEAD. Kills the archaeology-flagged contradiction (v-resume.md:29 vs parallel-dispatcher.md:183). (commands/v-resume.md,agents/parallel-dispatcher.md) - Dead
job["log"]now populated. The state-machine'slogfield, previously documented but never written, is now recorded at dispatch for codex jobs and consumed by liveness. - Stderr UUID-scrape replaced. The fragile stderr session-id extraction is deleted in favor of the structured
--jsonthread.startedcapture above.
- Thread-naming unsupported in
codex exec(live-probed 2026-07-11):codex execexposes no flag to name or pin a thread id, so the worker captures the auto-generated UUID from thethread.startedevent rather than assigning one.--jsonand--output-last-messageverified to coexist (result path unchanged);--ephemeralverified accepted bycodex exec. All codex capability facts here are live-probed, per the library audit — not re-invented.
- Rename bypass (HIGH). The gate's diff ran with git's default rename detection ON, so
git mv docs/important.md src/renamed.mdunderwrite_allowed: [src/**]collapsed to a single record whose--name-onlyoutput was just the destination — the out-of-scope deletion ofdocs/important.mdwas invisible and the verdict was pass (reproduced). The diff argv now carries--no-renames: both sides of a rename surface as a delete + an add, and the out-of-scope source path BLOCKS. (scripts/compound-v-scope-check.py) - Symlink escape (MEDIUM). The gate string-matched changed paths and never
lstat-ed anything — a symlink inside the allowed area pointing outside the worktree glob-matched cleanly, and a write through it landed OUTSIDE the repo with verdict pass (reproduced). The verdict path now scans the WHOLE gate root (os.walkwithfollowlinks=False, symlinks only — cheap) and reports every symlink whoserealpathescapes the root as a violation"<path> (symlink escapes the worktree)"— unconditionally: even inside the allowed area, and even for a pre-existing link committed before the baseline with no new changes at all, because a write through either lands where git sees nothing and the link itself is the only reliable gate-time signal. Degrade-safe on unreadable entries. Honesty note (in the module docstring too): the gate DETECTS the channel; it cannot observe writes already made through it — kernel-level confinement (the codex backend's sandbox) remains the preventive layer. - Three new selftest cases — rename-out-of-scope, job-created escaping symlink, pre-existing (committed-before-baseline) escaping symlink — each verified to FAIL against the unfixed logic via a temporary revert and PASS after the fix; the suite is green.
- Trigger-0 hook backstop (
hooks/brainstorm-trigger0-nudge.sh, registered inhooks/hooks.json): when the Skill tool invokessuperpowers:brainstorming, a one-line idempotent reminder to run the Trigger 0 gates is injected. A reminder, not enforcement — Trigger 0 stays description-driven; the hook closes the "agent simply forgets" gap documented in v2.7.0. xhigheffort — codex-only. codex-cli live-acceptsmodel_reasoning_effort=xhigh(probed 2026-07-11 on 0.144.1); the effort vocabulary gainsxhighvalid iffbackend: codex— every other backend rejects with a clear error naming the rule. Enforced in lockstep atcompound-v-resolve-model.py,compound-v-validate-manifest.py, both codex shell workers, and stated identically on every active effort-vocabulary surface.- Directions-late protocol (anti-anchoring, made explicit): the brainstorm forms its own first-principles proposals BEFORE reading the recon doc's
## SUGGESTED DIRECTIONS; consumption is observable via the recon-outcomes stream. - recon-outcomes stream (
docs/superpowers/memory/recon-outcomes.jsonl): an append-only event machine — a gate-stopped Trigger-0 evaluation emits exactly one terminal event (plumbing_skip|kb_skip|off|declined|no_engine); an engine run emitsfired→saved(withpath) →consumedas three separate appended events, never a mutated line. Never read by routing. - VERIFIED / UNVERIFIED split in recon docs: the output contract is now genuinely five verbatim sections (
## QUESTIONS TO ASK,## VERIFIED FACTS / CONSTRAINTS,## UNVERIFIED LEADS,## SUGGESTED DIRECTIONS,## SOURCES). VERIFIED = checked against a cited primary source (provisionally binding; 1B/1C revalidate); everything else is an UNVERIFIED LEAD that must become a question until validated. - Gate-2 freshness rule: a strong KB hit now requires scope AND freshness — volatile material (libraries, APIs, regulations, availability, best practices) older than ~30 days degrades to partial: still evidence, no longer skip-authority.
- Recon wiring finally reaches the executing 1B/1C: the recon-read step existed only in the phase docs —
agents/domain-expert.md,agents/doc-validator.md, and both prompt templates never mentioned it, so dispatched pre-flights never learned a recon doc existed. All four now carry the read step plus the exact-path handoff contract. - Epistemic contradiction in the gate-3 offer: the "verbatim" copy promised deep-research even on machines without it, conflicting with the honesty rule one section down — the offer is now engine-aware and honest, and decline paths are reachable.
- Fail-closed config, verbatim everywhere it's consumed: missing file or key → documented defaults (
deep_research: "ask",batch_elicitation: true); malformed JSON, wrong type, or unknown value → warn once, thendeep_research=askandbatch_elicitation=falsefor the session — an invalid value is never treated asauto. - Staleness sweep:
GEMINI.mdwas entirely pre-v2.7 ("three transitions", Gemini 2.5, missing command rows) — refreshed to the four-transition reality; survivingcodex-cli 0.130pins → 0.144.1 and cursor worker provenance comments 2025.09.12 → 2026.06.26.
Five audit lines drove this release: F1 (live dogfood of Trigger 0/elicitation — procedures actually executed), F2 (cross-repo consistency/staleness sweep), F3 (scripts robustness — both scope-gate exploits reproduced on scratch fixtures before any fix), from three parallel Fable agents; plus C1/C2 — two independent max-effort Codex gpt-5.6-sol passes at xhigh (the v2.7.0 guidance red-teamed as executable instructions, 28 findings; design red-team, 9 findings + 5 proposals). Cross-model by construction, convergent findings independently confirmed across lines. The pre-dispatch Codex plan review (verdict reject, 7 findings, all accepted) reshaped the plan itself — including the whole-root pre-existing-symlink scan shipped above.
The build was reviewed to convergence. Round 1 (9 findings, all accepted): a .git-named escaping symlink bypassed the scan; the outcomes event machine had no legal failure transition; the exact-path handoff had no literal storage carrier; five active surfaces still described batching as companion-only; the dedicated "max-effort" review command still capped at high. Rounds 2–5 hardened the symlink scan alone through four more genuine edges — a nested real .git directory hiding a link, a chmod 000 directory, and finally a real false-PASS where os.path.islink silently swallows EACCES on a 0400 (readable-but-not-searchable) directory (fixed by switching to os.lstat, which raises). Each fix carries a selftest; the gate self-test suite is green.
- Trigger 0 — pre-brainstorm recon (
skills/compound-v/phase-0-recon.md): when a brainstorm is about to begin on an unfamiliar topic, a gated, bounded research pass (bundleddeep-researchif present, ≤6 parallel WebSearch otherwise, skip-with-notice if neither) writes an anti-anchoring recon doc todocs/superpowers/recon/that the brainstorm — and later pre-flights 1B/1C — read first. Gate order: plumbing-skip → V-memory KB hit →brainstorm.deep_researchconfig (askdefault /auto/offhard kill-switch). Recon is evidence, never a routing input. Description-driven with zero hook backstop — weaker than Triggers 1–3, documented as such. - Batched elicitation (
skills/compound-v/brainstorm-elicitation.md): ≥3 independent questions (≤5 groups/screen, never a grid) may batch into ONE Visual Companion form screen — reusing upstream's companion server as-is, only if the user already accepted it this session. Independence is judged on answer interaction; when unsure → sequential. Deliberately overrides upstream's "text questions → terminal" rule for this narrow case, and says so. /v:init:brainstorm.deep_research+brainstorm.batch_elicitationpolicy keys (committed config) and adeep_researchpresence probe (machine-local capabilities cache, advisory only — fire-time listing check is the contract).- CI guard: CHANGELOG top version must equal
plugin.jsonversion — closes the bug class where v2.6.4 shipped with both manifests still at 2.6.3 (the bump was written but never committed, and manifest-vs-manifest lockstep can't see it).
- Pre-flight phase docs 1B/1C now read
docs/superpowers/recon/before opening new searches (deepen, don't repeat). skills/compound-v/skill-escalation.mdreconciled with Trigger 0's earlier deep-research use (previously claimed deep-research fires only past 1B/1C).
/v:initstatedask/autounconditionally — now explicitly gate 3 of 3 (plumbing-skip and KB-hit gates named, authority linked).- Epic mode silently bypassed Trigger 0 — per-feature brainstorms now run the recon gate sequence up front; later features converge via the KB-hit gate by design; the autonomous loop is described as the post-spec execution tail.
- Stale three-phase enumerations (SKILL.md quick-reference heading, plugin/marketplace descriptions) updated to the four-transition reality.
- The CHANGELOG guard was hardened round-by-round to CommonMark-correct fence handling: opener char+length tracked, closer requires same char + run ≥ opener + only trailing whitespace, a backtick opener with a backtick in its info string is not a fence, headings indented ≤3 spaces are matched with indent-independent version extraction. A 15-fixture adversarial suite was exercised locally; unbalanced fences still fail conservatively (loud, never a false pass).
Two real incidents noticed by Oscar Salcedo, which a requested Codex cross-model hunt for "similar/adjacent bugs" grew into a full sweep of the same bug class across the orchestrator:
- Data loss: an uncommitted run directory vanishes on worktree cleanup.
docs/superpowers/execution/<run-id>/**is documented as "the committed run substrate" (execution-manifest.md) — but nothing in the pipeline actually committed it.superpowers:finishing-a-development-branch's cleanup step runsgit worktree removeon both its Merge and Discard paths, which silently deletes any uncommitted files in that worktree — taking Compound V's own audit trail with it. After a restart,/v:statuswould then honestly (but confusingly) report "no orchestrator runs" for a repo that demonstrably had one. - Misleading status message for non-Compound-V work. When a repo had real prior work done via plain Superpowers
subagent-driven-development(evidenced by.superpowers/sdd/task-brief/report/review artifacts) rather than Compound V's own manifest-driven dispatch,/v:status's "no orchestrator runs" message read as "nothing happened here" — it had no visibility into that different, upstream-owned execution path. Fixed with a cheap presence-check (not a parse — that directory's format belongs to the base Superpowers plugin, not Compound V) that disambiguates the message without trying to understand or summarize its contents.
The commit-discipline fix, after four rounds of Codex review, landed nine explicit commit points across the pipeline (each closing a path where state could be written but never survive a worktree cleanup):
/v:orchestrate— commitsmanifest.yaml+ the initialstate.jsonright after materializing them.parallel-dispatcherStep 7 — commits the run directory + memory/scorecard files in one shot before handing off tofinishing-a-development-branch(the one point that can trigger the destructive cleanup). Round 1 of review caught a bug in this very fix:state.json's phase was flipped toMERGEDafter the commit, so the committed record permanently lagged one phase behind reality — fixed by writingMERGEDfirst, then committing everything together. 3–7.commands/v-epic.md— a separate, epic-levelepic-state.json(the epic's only resume mechanism, one level up from any single feature's run directory) was never committed anywhere. Five commit points added: after init, at every checkpoint (the defaultMAX_FEATURES=1stopping point after every feature), at epic-complete, at epic-blocked, and — caught in round 3 — after crash-reconcile (the--status failed"abandon and stop" path is terminal and doesn't otherwise pass through the checkpoint's commit).commands/v-resume.md— its own completion path didn't reference committing the recovered run substrate; a resume completing this way could re-lose the very state it just recovered.commands/v-collect.md— standalone use (re-checking an already-dispatched run without re-dispatching) rewroteresults/*.json+state.jsonwith no commit step at all.
state-machine.md documents the general "written to disk ≠ durable" principle tying it all together. Docs-only; no code changed. Codex cross-model verification, four rounds: round 1 found the MERGED-ordering bug plus the v-epic/v-resume/v-collect gaps; round 2 (broad hunt) confirmed the fix and found nothing new to add; round 3 caught the crash-reconcile gap; round 4 (narrow re-check) confirmed all nine commit points present, correctly scoped, and non-contradictory.
deep→gpt-5.6-sol,standard→gpt-5.6-terra,light→gpt-5.6-luna(wasgpt-5.5/gpt-5.5/gpt-5.3-codex-spark) — a real per-tier differentiation wheredeep/standardpreviously shared the same model. Live-verified all three oncodex-cli 0.144.1(PROBE_OK).gpt-5.6-solrequires codex-cli >= 0.143.0 — confirmed broken with a clear 400"requires a newer version of Codex"on 0.142.5, working on 0.144.1; an under-floor client fails loud (not silently — the failure-policy retries once then halts cleanly).gpt-5.6-terra/gpt-5.6-lunawork on older clients too (verified back to 0.142.5).compound-v-codex-review.sh's cross-model-review default follows the deep tier (gpt-5.6-sol, "Codex on their max").- Two independently-stale adapter pins refreshed during the audit:
adapter-codex.md's verified-against pin (0.130.0→0.144.1);adapter-cursor.md's verified-against pin (2025.09.12→2026.06.26) and a now-false claim — "cursor-agent has no models list command" — corrected: it does now (cursor-agent models, a live 187-entry catalog verified). Grok is not present in that live catalog for this account (press coverage says available, likely region/plan-gated — not documented since unconfirmed hands-on). No auto-discovery was added for Cursor's catalog (would be over-engineering — it spans unrelated vendor families with no shared naming convention, unlike Antigravity's single-family Gemini catalog/v:modelsalready ranks); curated + user-overridable stays the flow, now pointing at the real command for manual discovery. - Every doc stating the codex model map as current fact updated for consistency (
v-init.mdseed,v-models.mdroster/table/example,routing-policy.mdmap/resolve-example,execution-manifest.mdtier table/config example). Illustrative "(e.g.gpt-5.5)" mentions explaining the resolution mechanism (never hardcode a model — the resolver handles it) and the datedrouting-lessons.mdhistorical entry were deliberately left untouched. Codex cross-model verification caught one real miss —compound-v-resolve-model.py's own source comment still said "cursor-agent has nomodelslist command" (I'd audited the.mddocs for this false claim but missed the.pycomment) — fixed; a second pass confirmed every codex tier mapping, the review-script default, and every version pin consistent, with no remaining stalegpt-5.5/gpt-5.3-codex-sparkcurrent-default claims.
- Closed a real downstream-repo review comment: a teammate flagged the committed
.claude/compound-v.jsonas looking like it should be gitignored. The diagnosis: the file mixed genuine team policy (stance,models,memory,epic,review,workflows_accelerator— correct to commit) with a machine-local capability snapshot (backends,checked_at— "which CLI/MCP tools were detected on the machine that last ran/v:init") — a fact about one developer's machine, wrong the moment a teammate with a different local setup opens the file. - The fix removes
backends/checked_atfrom the committed file — no new file needed. A correct, already-uncommitted home for exactly this data already existed:~/.claude/compound-v-capabilities.json(/v:initStep 4b, user-home-scoped, already documented as "reused across repos").backendswas pure redundancy with it. - Audited before touching anything:
compound-v-resolve-model.py'sload_config_models()reads only themodelskey; a full-repo grep found zero programmatic readers ofbackends/checked_at— actual backend availability is already re-probed live at dispatch time (the env-aware codex→claude fallback). So this is a hygiene/trust fix, not a routing-behavior change — nothing about dispatch logic changed. Backward-compatible: an existing committed file with the old fields is simply ignored, no migration needed. commands/v-init.mdStep 4a andcommands/v-models.mdStep 3 updated (write path + example JSON + an explicit "why" note at the canonical source). Codex cross-model verification: ACCURATE — independently confirmedload_config_models()reads onlymodels, zero remainingbackendsreferences anywhere in the repo, the sole remainingchecked_atis correctly inside the Step 4b capability-cache shape, and the Step 4b cache fully covers the old capability role.
- Closed a real incident from a downstream repo: a parallel-dispatch batch assigned a job to Codex, but the job's worktree needed its git base fixed — Codex's sandbox is confined to
$WT, while the worktree's actual git metadata lives outside it, in<repo>/.git/worktrees/<job-id>/, andapproval_policy: nevermeans it can't ask to escalate. A sandbox limitation, not a code one. The orchestrator worked around it by dropping worktree isolation for Codex — which is not a fix: it removes the only file-scope enforcement Codex has (codex ⇒ worktreeis a hard invariant incompound-v-validate-manifest.pyprecisely because Codex can only be confined to a directory, never a file allow-list), and risks interleaved writes if other jobs are running concurrently in the same tree. - The correct fix, now explicit: every dispatch or retry of an external worker (Codex/Antigravity/Cursor) goes through the full worker-script lifecycle, which already recreates the worktree fresh at current HEAD every time — never patch an existing worktree's git state, and never delegate that patch to the worker itself. A job that needs another job's already-landed output must model that as
depends_onin the manifest, not discover it mid-run. - New
SKILL.md§Worktree git-base fixes (the shared, mechanism-level explanation), a cross-reference inadapter-codex.md, and explicit language inparallel-dispatcher.md's isolation step and its retry line (which previously just said "re-dispatch the same backend" without specifying the worktree is recreated fresh — the exact ambiguity that let the workaround slip through). Docs-only; no code changed. - Codex cross-model verification, two rounds, caught real gaps in the fix itself. Round 1 confirmed the core sandbox mechanism but found the
depends_onguidance overclaimed: merge-back only stages a job's changes (git apply --index) — it never commits, soHEADdoesn't move, and a dependent job's "fresh worktree atHEAD" would not contain a prerequisite's merged-but-uncommitted work. Fixed:parallel-dispatcher.mdStep 1 now requires the caller to verify Task 0's result is actually committed (for bothdirectandworktreeisolation) before Step 2 begins — the missing link betweendepends_onand a correct fresh-worktree baseline. Round 1 also flagged an overclaim that Codex "cannot touch outside metadata even with--dangerously-style flags" — narrowed to the documented pinned invocation only. Round 2 confirmed the round-1 fixes, then caught one more: the wording assumed adirect-isolation implementer always commits its own work —adapter-claude.mdestablishes only that it writes against the main tree, not that it commits — fixed to an explicit caller-side verify-and-commit step for both isolation modes. Two full review rounds, three real corrections, all fixed.
pr-reviewskill — a two-axis, stack-agnostic deep code-review skill for a pull/merge request or a local diff. It first builds shared understanding of the change's intent, then hunts bugs and edge cases along two deliberately separate axes run as context-isolated sub-agents so neither pollutes the other: Standards (does the code follow this repo's documented conventions, discovered in a Phase-0 sweep?) ⊥ Spec (does it faithfully implement the originating spec/issue/PRD?). Findings are reported side by side, never merged across axes; genuine author-intent unknowns are promoted to Open Questions; every finding carries a verdict + confidence. Review-only — it never edits, commits, pushes, or merges code. ShipsSKILL.md+ fourreferences/(exploration checklist, review domains, findings format, comment-posting)./v:pr-reviewcommand — a thin entry point in the/v:*family that runs the skill. Argument = PR/MR URL or number; empty = current branch vs. its base. Auto-detects the host: GitHub (gh), GitLab (glab), or a hostless local diff.- Self-contained — no new runtime deps, hooks, or scripts; frontmatter within the linter's limits and all intra-plugin
.mdcross-refs resolve.
- Dense search: repeated queries skip the model load. Every dense search paid one isolated-venv subprocess = one ONNX model load per query (seconds). A new
query_cacheSQLite table (sha256(query) + model → vector,IF NOT EXISTSso no migration, bounded to the 500 most-recent rows) lets a repeated query — the common case for/v:rememberand the recall→action bridge's templated queries — return in milliseconds. A model change misses by key; identity drift (embedder revision change) clears the cache alongside the corpus re-embed, so a stale-revision vector is never served; any cache error falls back to embedding (the cache is an optimization, never a failure mode). Selftest proves hit / miss / model-miss / failed-embed-not-cached with a counting fake embedder. Profiled first: the FTS5 lane (rebuild 0.7 s, search 0.28 s, hooks ≤0.25 s) was left untouched — already fast. Codex cross-model verification caught the stale-vector hazard — the(query, model)key alone can't see an embedder revision change (the same drift the corpus re-embed handles), independently confirming the author's own finding — fixed via_invalidate_query_cacheon the drift branch, plus the extra coverage Codex asked for (different-query miss, cache bound, drift invalidation): 7 cache checks total, all green.
- V-memory DENSE refresh now loads the embedding model once, not per file. The refresh embedded per file —
reindex_fileinvoked the isolated-venv embedder subprocess once per file, and each subprocess rebuilt the ONNXInferenceSession, soNfiles meantNmodel loads (the reason the first full pass overdocs/superpowers/**was slow).cmd_refreshnow uses a newreindex_batchthat chunks all to-index files, flattens their chunks into one embedder call, and slices the vectors back per file — one model load per refresh. The FTS5-only (embeddings-off) path is unchanged and it stays degrade-safe (a failed batch persistsNULLembeddings → FTS5-only; the CORE lexical lane is never affected). Selftest injects a call-counting fake embedder proving the single call + correct per-file vector slicing + degrade — no network/model needed. Codex cross-model verification: ACCURATE on all five claims withfile:lineevidence (single call, offset slicing with no off-by-one, empty-corpus skips the model load, degrade-safeNULLfallback, atomic persistence preserved).
- Third-party skill discovery.
/v:onboardnow recommendsnpx autoskillswhen a project manifest is detected — a newrecommend-autoskillssubcommand inscripts/compound-v-onboard.pyflags applicability (package.json,pyproject.toml,requirements.txt,Gemfile,go.mod,Cargo.toml,composer.json,pom.xml,build.gradle, or a top-level*.tf), with the marker file as evidence; an unknown repo yieldsapplicable: false(no false recommendation). - Present-only, gated
--dry-run, never auto-installs. In DIAGNOSE, onboarding surfaces the recommendation and — behind a human confirm — runs the previewnpx autoskills --dry-runthrough the process-group timeout supervisor withstdin </dev/null(the v2.5.0 external-launch invariant), to show which skills it would install. The real install stays the user's own action (autoskills has its own confirm + SHA-256 verification). - Auto-trigger-degradation caution. Because mass-installing overlapping skills degrades auto-triggering across the whole skill set (the onboarding Skills stance), the recommendation always carries a loud caution to review the dry-run and prefer a focused subset.
- Built with TDD, dogfooded, cross-model Codex-verified. 5 selftest checks (manifest → applicable + evidence +
--dry-runcommand; empty → not applicable;pyproject.toml→ applicable; a top-levelmain.tf→ applicable with the filename as evidence; a directory named*.tf→ not applicable). Dogfood on superpowers-v itself (no standard manifest) →applicable: false— the negative path. Codex cross-model verification (the model that writes ≠ the model that checks) caught two real bugs in the Terraform branch — the evidence was the literal"*.tf"instead of the actual filename, and a directory namedfoo.tfwas a false positive — both accepted and fixed, each with added selftest coverage.
- Compound V Academy — a gamified 3-episode tutorial (Developer · Product Owner · Universal Creator) is now linked prominently from the README: https://amiainative.dev/compound-v. The fastest way to learn the whole pipeline (onboarding → the three scouts → dispatch → the review gates), with the squad as guides.
- Scope gate: bracketed path segments are literal.
[locale]/[uid]/[slug]in awrite_allowedglob were parsed as fnmatch character classes ([locale]= "one ofl,o,c,a,e"), which falsely BLOCKED any Next.js App Router write scope (e.g.app/[locale]/…/[uid]/page.tsx) and raised a regexFutureWarning. Bracketed dynamic segments — the dominant real-world case — now match literally; the selftest covers the App Router case positive + negative. (scripts/compound-v-scope-check.py) /v:review-planschema resolves from the plugin, not the reviewed repo. The cross-model review script defaulted its JSON-schema path to$REPO/schemas/…(the reviewed repo), so/v:review-plandied with "schema not found" in every project except this one. It now resolves the default schema next to the script (its install dir); the--schemaoverride is unchanged. (scripts/compound-v-codex-review.sh+ new regression test)
/v:onboardnow recommends the right external tools for your stack — a newrecommend-mcpsubcommand inscripts/compound-v-onboard.pymaps repo signals → tools from a curated, currency-verified table, with a deliberate CLI-over-MCP bias: agithub.comremote yields theghCLI, never a GitHub MCP server (avoids the broad-PAT toxic flow). Rows: Supabase MCP (--read-only --project-ref), Postgres MCP (--access-mode=restricted), Playwright MCP (pinned>=0.0.40, CVE-2025-9611), Context7, Sentry — every MCP row ships least-privilege flags pre-filled..mcp.jsonvia diff + confirmation, never auto-apply.mcp_json_config()builds the config from the confirmed MCP recommendations, merged additively — it never clobbers an existing same-named server, and CLI recommendations (gh) are surfaced as setup instructions, not.mcp.jsonentries. The write is a gated WRITE-step artifact behind the human approval gate.- Lethal-trifecta warn-only. Any private-data + untrusted-content + external-write server (Supabase / Postgres) emits a named warning with a specific remedy (read-only + dev/branch-scoped + single-repo session). Read-only defaults defuse most at the source; no hard refusal — the user decides.
- Deterministic + evidence-cited + honest. The table is a static curated map (no model guesswork on tool names/flags); each recommendation cites its triggering signal; an unknown stack yields an empty set (no invented tools). Currency (packages / flags / CVE pin) was WebSearch-verified 2026-07-01.
- Built with TDD, dogfooded, cross-model Codex-verified. 21 selftest checks (github→gh CLI, Supabase read-only, Postgres restricted, fast-moving→Context7, Playwright, citation-grade evidence, trifecta warning, additive-merge, no-clobber, empty-on-unknown, Postgres DSN, existing-server warning). Dogfood on superpowers-v itself: exactly one recommendation (
github → gh CLI), no false MCPs, emptymcpServers(the negative path). Codex cross-model verification (the model that writes ≠ the model that checks) caught three real spec/impl gaps the Opus author missed — a Postgres DSN (nopgdep) went undetected, evidence wasn'tfile:linecitation-grade, and trifecta warnings skipped existing.mcp.jsonservers — all three accepted and fixed, each with added selftest coverage. - Onboarding scope:
.mcp.json/ MCP recommender moved from fast-follow to in-scope;.claude/rules/*.md(→ future) and bulk skill generation (deliberately avoided) stay out.
- Liveness probe —
scripts/compound-v-liveness.py. Classifies eachrunningjob in a run'sstate.jsonfrom git + filesystem only (never model-self-report — same ethos as the scope gate):LIKELY-DONE(the worktree has a commit past its recordedbaseline— the work landed and only the completion notification is stuck),STALE(no working-tree progress past the threshold — a suspected hang),WORKING,DEAD(a recorded pid died with no progress), andUNKNOWN(degrade-safe — a missing/unreadable signal never crashes the probe)..gitis excluded from the mtime walk so a commit doesn't mask staleness. Stdlib-only;--selftest(12 checks) covers every class with real git/fs/pid fixtures; exit3when any job is STALE/DEAD. /v:statusgains aLivenesscolumn — every running job shows its class + a hint (LIKELY-DONE → /v:resume / auto-collected,STALE → suspected hang). Degrade-safe: a probe error shows—and never breaks the table.- Dispatcher liveness sweep (
parallel-dispatcherStep 2d). Between batches — and while awaiting a background job whose completion notification never arrived — the dispatcher runs the probe and acts:LIKELY-DONE→ collect now (scope-gate + merge +done), ending the "nudge the dispatcher by hand" failure mode a parked subagent caused; aSTALE/DEADexternal worker → the existingtimeoutfailure-policy (retry cap, then halt — no new mechanism); aSTALEClaude subagent → surfaced (the harness owns the kill), reclassifyingLIKELY-DONEonce its commit is observed. No new phase, no daemon; documented instate-machine.md+failure-policy.md. - Fixed — the one uncapped external-launch path.
scripts/compound-v-codex-review.sh(the cross-model plan review) capped itscodex execvia atimeout/gtimeoutbinary only when one was installed (no binary ⇒ no cap) and signalled only the direct child. It now runs under the shared process-group supervisorcompound-v-run-with-timeout.py(guaranteed hard cap + whole-group kill), matching the three worker scripts. This closes the gap behind a real 44-minute hang this cycle (an ad-hoc codex review that had no cap and blocked on stdin). - Enforced-launch invariant (
backend-launcher/SKILL.md). Every external-CLI invocation — dispatched worker OR orchestrator-level (cross-model review, ad-hoc verification) — MUST run throughcompound-v-run-with-timeout.pywithstdin </dev/null; a barecodex/cursor/agycall is a bug. The probe detects a hang after the fact; this rule prevents it. - Anti-over-engineering. Archaeology confirmed the three worker scripts already enforce supervisor +
</dev/nulland run synchronously under the hard cap, so the probe deliberately does not police external workers or addpidbookkeeping — its unique, non-redundant value is the Claude-subagentLIKELY-DONE/STALEcase that nothing caught before. ~200-line stdlib probe, one purpose; no daemon, no new deps. - Built directly with TDD, dogfooded live. The probe was proven end-to-end on a fabricated run with a real git worktree: a parked job (worktree committed past baseline) classified
LIKELY-DONE, a 40-minute-idle worktree classifiedSTALE, a fresh oneWORKING, adonejob skipped — exit 3 as designed. Full regression (6 script selftests + banner + frontmatter lint + CI version-lockstep) green. - Cross-model Codex verification earned its keep. A GPT reviewer (the model that writes ≠ the model that checks) adversarially reviewed the probe and caught four real issues the Opus author missed:
LIKELY-DONEusingHEAD != baselineinstead of git ancestry (a reset/checkout would read as done),_pid_alivemisreading EPERM (an alive-but-unsignalable process) as dead,_newest_mtimefollowing symlinks (os.stat→os.lstat), and a stale bare-timeout … codex execexample inSKILL.mdcontradicting the new launch invariant. All four accepted and fixed, each with added selftest coverage (15 checks, green).
- Tier→model resolution is now stance-aware. The
cost-awarestance routes Claudestandard-tier implementers (core_slice,tests_new) to Sonnet 5 (via the nativesonnetalias — no concrete ID pinned);balanced(the default) is unchanged (standard → opus). Exactly one built-in cell changed:cost-aware.claude.standardopus → sonnet.cost-aware.claude.deepstaysopus; thecodex/antigravity/cursormaps are identical across stances. - Resolver (
scripts/compound-v-resolve-model.py).DEFAULT_MODELS→DEFAULT_MODELS_BY_STANCEkeyed by stance (with a derivedDEFAULT_MODELS = DEFAULT_MODELS_BY_STANCE["balanced"]alias so existing references are untouched). New--stanceflag (defaultbalanced);stanceis a trailing kwarg onresolve()so every existing caller is unbroken.VALID_STANCESis re-declared locally (mirrorscompound-v-validate-manifest.py, no shared import). - Config backward-compat.
.claude/compound-v.jsonmodelsaccepts BOTH the legacy flat shape{backend:{tier:model}}(applied to every stance) and a new per-stance shape{stance:{backend:{tier:model}}}, discriminated by whether every top-level key is a stance name. Existing seeded configs keep working unchanged. - Every Claude-model-resolving call site threads
--stance <routing_stance>—parallel-dispatcher(the batch resolve and Task 0),phase-3-parallel-opus-dispatch,adapter-claude,/v:status(its per-job model column — without the flag it would displayopusfor a job that actually dispatches assonnetundercost-aware), and the/v:modelsverification loop. No flag ⇒balanced(current behavior). - partition-reviewer stance-gate. Under
cost-aware, astandard-tier Claude Sonnet implementer is the routing-policy default and is exempt from the 8-boxSONNET_UNJUSTIFIEDjunior-task check;balanced/conservative/claude-onlyapply the check as before. Invariants hold in every stance: reviewers ⇒ deep ⇒ opus, sensitive (auth/payments/PII/a11y) ⇒ deep ⇒ opus, the light-tier Sonnet check, and never Haiku. The deterministic validator is unchanged — it never adjudicated implementer Sonnet eligibility. - Fixed — version lockstep (
marketplace.json)..claude-plugin/marketplace.jsonhad lagged at2.1.1whileplugin.jsonadvanced to2.3.1(the 2.3.0 and 2.3.1 releases bumped onlyplugin.json), which the CI lockstep check in.github/workflows/validate.ymlflags as a mismatch. Both are now bumped in lockstep to2.4.0, greening CI. - Built by Compound V, dogfooded, cross-model verified. Implemented through the plugin's own
/v:dispatchpipeline (5 jobs — resolver serial spine + 3 parallel worktrees + integration review; git-derived scope-gate PASS on every job; 3-pass Review Gate APPROVED, 5/5 acceptance criteria). The new behavior was dogfooded live: acost-awaremanifest with astandard-tier Claude job resolved to Sonnet and passed partition-review with noSONNET_UNJUSTIFIEDflag, while the deep reviewer stayed Opus. Codex cross-model verification (the model that writes ≠ the model that checks) returned ACCURATE on all seven claims withfile:lineevidence (incl.cost-aware.claude.deepstays opus, the config-shape discrimination, and fail-closed resolution).
- The dispatch tree and
/v:statusnow show the resolved model per job.parallel-dispatcherannounces each batch as a tree annotated withbackend · model (tier/effort)(resolved viascripts/compound-v-resolve-model.pybefore dispatch), and/v:statusgains aBackend · Modelcolumn — so it is always visible which model each job runs on, both live during dispatch and after the fact.
- Claude Sonnet 5 (
claude-sonnet-5, released 2026-06-30) is picked up automatically wherever Compound V routes to thesonnettier alias (thelighttier on the Claude backend) — the resolver intentionally emits native tier aliases (opus/sonnet), so the new model flows in with no code change. Routing more work to Sonnet 5 (it benchmarks close to Opus 4.8 at lower cost) is a deliberate routing-policy change tracked for a future minor (acost-aware-stancestandard → sonnetroute), not a silent default shift — the default stays Opus by default, reviewers always Opus, never Haiku.
- New
/v:onboardcommand — studies an existing repo and builds a trusted knowledge base (docs/superpowers/architecture/{architecture,business-logic,tech-context}.md) plus cross-tool agent instructions (rootCONVENTIONS.md,AGENTS.mdas the portable source of truth + a thin@AGENTS.mdCLAUDE.mdbridge, conditionalDESIGN.mdfor UI repos), all behind a human approval gate, then feeds them into V-memory. Authority doc:skills/compound-v/onboarding.md; thin command:commands/v-onboard.md./v:onboard --refreshre-checks cited-evidence staleness. - Deterministic toolkit
scripts/compound-v-onboard.py(stdlib-only):pack(repo pack-manifest + advisory secret scan),verify-citations(the two-tier gate),staleness(cited-evidence drift + uncited-new-file heuristic),design-lint(wrapsnpx @google/design.md),detect-ui,scan-output. - Two-tier citation gate (anti-hallucination). Tier-1 (cited path resolves +
1 ≤ start ≤ end ≤ lineCount) is mechanical and blocks 100% of claims; Tier-2 (do the cited lines actually support the claim?) runs on 100% of load-bearing claims (security / fail-closed / concurrency), where an unsupported load-bearing claim is BLOCKING. Generation defaults to read-then-cite. (Live feasibility probe on this repo: read-then-cite scored 23/23 path/range/support under an adversarial verifier; naive free-write failed to even produce valid structured output.) - Untrusted-input rule. Any existing instruction file (
AGENTS.md/CLAUDE.md/GEMINI.md/ cursor / windsurf / copilot rules) is treated as evidence to quote and summarize, never a directive to execute during onboarding; the managed-policy layer is informational-only. - Secret-scan granularity (input advisory, output blocking). The
packinput scan is advisory — it surfaces secret-shaped strings anywhere in the repo (test fixtures, security docs) at the human gate but does NOT halt the run. The blocking refusal isscan-output, run on the GENERATED docs only, enforcing the invariant "no credential reaches a generated, committed file." (Caught while dogfooding: the input scan reports ~40 benign false positives on this very repo — the plugin's own selftest fixtures and secret-scanning scripts — which a repo-wide hard block would have wrongly halted on.) - V-memory engine extension (
scripts/compound-v-memory.py).tracked_files()now also indexes rootAGENTS.md/CLAUDE.md/CONVENTIONS.md/DESIGN.mdvia a scoped secondgit ls-filesunion (DOCS_RELnot widened, fail-closed[]on git error preserved);doc_type_for()gains clean labels for them. (Cosmetic: until the first refresh after upgrade,searchmay report "N new behind".) /v:init+ SessionStart banner./v:init's closing report now suggests running/v:onboard; the banner gains a read-only, fail-silent staleness line ("N architecture docs stale vs HEAD → run /v:onboard --refresh") guarded so it can never abort the banner underset -euo pipefail.- Built by Compound V, dogfooded on itself. Implemented through the plugin's own
/v:dispatchpipeline (5 jobs, all Opus, scope-gate PASS on every job, one transientOverloadedauto-retried, 3-pass Review Gate APPROVED 6/6), then/v:onboardwas run on superpowers-v itself (51 claims, all 11 load-bearing passing Tier-2ok:true, output secret gate clean,DESIGN.mdcorrectly skipped on the no-UI repo). This release ships alongside the repo's own generated knowledge base and theCLAUDE.mdbridge it previously lacked. - Dependencies verified current (2026-06-30):
@google/design.mdv0.3.0 (lintreal, JSON findings + WCAG),repomixv1.16.0 (Secretlint secret-scan built-in). Deliberately fast-follow / out of v1: the MCP recommender (and per the maintainer, GitHub is used via theghCLI, not a GitHub MCP server), path-scoped.claude/ruleswriting, and bulk skill generation.
- Process-tree timeout supervisor (
scripts/compound-v-run-with-timeout.py) — adopted by ALL three external workers (cursor, codex, antigravity). These backends previously capped via an externaltimeout/gtimeout(or a bash watchdog), which signals only the direct child — a tool/shell child the agent spawned could outlive the cap and write after the scope gate (the exact leak the gate exists to stop). The new supervisor starts the command in a new session (setsid) and on expirykillpgs the whole process group (SIGTERM → grace → always SIGKILL — a descendant that ignores SIGTERM is still reaped) →status: timeout(124). It holds no copy of the command's output fds, so a hung child can't hang the dispatcher's$(…)capture. Proven by--selftest(a descendant — incl. one that traps SIGTERM — that tries to write after the cap is reaped first; the write never lands) and live success +--timeout-sec 1 ⇒ status:timeoutthrough all three workers.--timeout-secmust be> 0;--grace>= 0. (Limitation: a descendant that itselfsetsids into a new session escapes — true containment needs cgroups/job-objects; backend tool children don't daemonize.) - Fixed a dispatcher-hang in the watchdog. The watchdog subshell inherited the worker's stdout, so its
sleepheld the dispatcher's$(...)capture pipe open — every Cursor dispatch hung for the full timeout after the job finished. The watchdog's fds are now redirected to/dev/nulland itssleepchild is reaped. Verified: worker E2E back to ~29 s (was hanging ~600 s). - Cursor model default is now
auto. A Cursor Free plan can only use Auto — passing a named model (sonnet-4/gpt-5/ …) errors with "Named models unavailable."resolve-modelnow maps every Cursor tier toauto(works free and paid); named per-tier ids are a paid-plan opt-in via/v:models/ config./v:initStep 1e detectstimeout/gtimeoutand warns when the Codex worker would have no hard cap. - New regression coverage: a full-pipeline seam test (
validate-manifest → cursor worker → merge-back git-apply) — 8/8 live, verifying the merge-back path the isolated worker test never exercised.
cursor-agentas a headless dispatch backend —scripts/compound-v-run-cursor-worker.sh, a Bash-spawned worker in its own git worktree that mirrors the Antigravity adapter (worktree isolation + git-derived scope gate → canonicaljob_result). Verified live (cursor-agent 2025.09.12): the success path (write withinwrite_allowed) and the BLOCKED path (write outside scope →violations) both pass.- Invocation (verified):
cd "$WT" && cursor-agent -p -f --output-format json [--model M] "<prompt>" </dev/null.-fis required — a headless run refuses an untrusted worktree without it. Output is one JSON object:.result→summary,.session_id(a real UUID) → resumable viacursor-agent --resume. Token.usageis ignored (anti-ruflo). - Lower-trust / opt-in — same tier as Antigravity. No kernel write-confinement (
-fgrants arbitrary write+shell); the git-diff gate is detection, not prevention. Prefer Codex (kernel-sandboxed) for untrusted / high-stakes work;backend: cursor⇒isolation: worktree. - Plumbing:
classify-failure.py --backend cursor(OpenAI/Anthropic-style needles + provisional cursor-auth);resolve-model.pycursor map (sonnet-4-thinking/sonnet-4/gpt-5, user-overridable via/v:models);adapter-cursor.mdrunbook;/v:initdetectscursor-agent+ auth and adds it tobackends. Available only when installed AND authenticated (env-aware routing).
- V-memory — a local-first RECALL layer over the
docs/superpowers/**prose (a new subsystem, hence the major bump). It extends the existing two-half memory (machine-generatedtask-outcomes.jsonl/scorecard + human-curatedrouting-lessons.md) and never rewrites them; recall is EVIDENCE for planning + review, NOT a routing input — routing stays the deterministic v1.1 order (routing-lessons.md→ stance table → conservative scorecard → fallback → invariants). Engine:scripts/compound-v-memory.py. Authority doc:skills/compound-v/memory.md. - Two lanes — CORE always on, DENSE opt-in. CORE is SQLite FTS5 BM25 over the git-tracked prose (pure stdlib, the default, always on). DENSE is opt-in embeddings (multilingual-e5-small) used in a rank-union with FTS5, scale-gated, and degrade-safe — when the embeddings are absent or broken the engine silently falls back to FTS5-only. The DENSE venv lives OUTSIDE the repo (
~/.cache/compound-v/memory/<repo-id>/) and is bootstrapped only by an explicit command — never on its own. - Embeddings are PURE PYTHON.
fastembed(onnxruntime + tokenizers) — NO Node, no daemon, no external vector-DB service, no fabricated metrics. - One deterministic, conservative-only recall→action bridge —
recall-check --files <glob>. It counts priorjob_resultrecords (status in{blocked, error, timeout}/ scope violation) on the same file pattern; N ≥ k (default 2) yields the verdicttighten(force worktree / add a review pass / fold into Task 0). It never reroutes to lower trust and never loosens — the prose analogue of the scorecard'sunhealthy → escalate. - Two new commands —
/v:remember(recall search) and/v:memory-refresh(index / bootstrap). /v:initnow configures recall + autonomy (structured questions →.claude/compound-v.json, honored by the engine/skills): the recall lane (memory.embeddings, FTS5 vs opt-in embeddings — the engine adds vectors automatically when on and bootstrapped), the memory autonomy level (memory.auto_recall/memory.auto_tighten), the epic-autonomy cadence (epic.max_features), and an automatic cross-model review default (review.cross_model).- New hook —
hooks/memory-refresh.sh. Silent, self-backgrounds an FTS5-only refresh, never installs/bootstraps; appended toSessionStart+PostToolUse:Write. - Key invariants (all enforced): the cache lives outside the repo; only git-tracked prose is indexed; the FTS5 lane is crash-safe; writes use
flock+ a transaction; hooks never bootstrap the DENSE lane. - New surfaces: engine
scripts/compound-v-memory.py; authority docskills/compound-v/memory.md; commandscommands/v-remember.md+commands/v-memory-refresh.md; hookhooks/memory-refresh.sh.
- Specs are batched UP FRONT — the autonomy-vs-quality tension resolved. The epic now brainstorms a real spec file per feature before the autonomous loop (the one human-interactive phase, approved once), carried as a
spec_pathon every feature infeatures.json/epic-state.json. The loop runs each feature from its pre-approved spec and never pauses to brainstorm.compound-v-epic-state.py --init --require-specsrefuses to start unless every feature has an existingspec_path— deterministic enforcement, not just prose. - Decomposition review gate (one level up from partition-review).
compound-v-epic-state.py --lint --features F.jsonflags structural smells in the feature DAG — an ISLAND feature (nodepends_onand no dependents → a likely missed dependency) and an over-coupled feature (depends on most others → a layer, not a vertical slice) — plus hard validation, before any build. A weak decomposition is the #1 way an epic fails downstream. - Autonomy budget / checkpoint. An epic is N full v1.0 runs, so it runs under a
MAX_FEATURESbudget per/v:epicinvocation (default 1): after the budget is spent it STOPS and reports--stats(done/remaining) for the human to review and re-run — a real cost ceiling + human-in-the-loop point, not an unbounded autonomous burn. - Reconcile-by-resume — no discarded work. A feature stuck
running(crashed mid-pipeline) is reconciled by running its own/v:resume <run-id>first (re-dispatch only that run's incomplete jobs), falling back to full restart (pending) orfailedonly if it can't recover — composing with the per-feature crash-resume instead of throwing away half-built work. compound-v-epic-state.pygains--lint,--stats,--require-specs, and aspec_pathfield (21-case self-test, up from 12). Docs:commands/v-epic.md+skills/compound-v/epic-mode.mdrewritten to the batched-spec, budgeted, decomposition-gated, resume-reconciling workflow.- Validated end-to-end: a real 2-feature epic (
core→cli, dependency-ordered) driven through the full loop with live Codex workers — batched specs →--lint→--init --require-specs→ topological build accumulating on one branch → the integrated app runs (python cli.py → 5,cliimportingcore's code) → fail-fast halt on a scope-blocked feature → reconcile drill — all behaved correctly.
- Epic mode — chain multiple plan-runs into one autonomous, resumable, dependency-ordered multi-feature build (PRD §8). A v1.0 run executes ONE plan (one feature); an epic chains several — an ordered set of features, each run through the FULL v1.0 pipeline (spec → 3 pre-flights → writing-plans + partition → manifest → dispatch → 3-pass review) in dependency order, accumulating onto one branch ("build a whole app"). It is the same discipline one level up — resumable, topological, no daemon.
scripts/compound-v-epic-state.pyis the deterministic state spine overepic-state.json(one level up fromstate.json):--initvalidates feature ids/refs/cycles and writes every featurepending;--nextreturns the next runnable feature (pendingwith alldepends_ondone, in topological order) or a stop reason (runnable|epic complete|epic blocked|epic needs reconcile) — the loop is fail-fast (anyfailedfeature halts the whole epic, even independent pending ones, until reconciled) and reconcile-strict (arunningfeature seen between features means a prior run crashed → reconcile via--update --status pending|failedbefore continuing, never an infinite wait);--updatesets a feature's status/run-id and rolls up the epic status;--summaryrenders the feature table. The new/v:epiccommand (commands/v-epic.md) is the driver: it resolves the epic brief into a feature list ({id, title, depends_on}), inits or resumes fromdocs/superpowers/execution/epics/<epic-id>/epic-state.json, loops--next→ run that one feature through the full v1.0 pipeline →--update --status done --run-id <run-id>, then onepic completeruns a final cross-feature integration review (the whole accumulated diff against the epic's acceptance criteria) and hands tosuperpowers:finishing-a-development-branch; onepic blockedit stops, surfaces the failed feature, and stays resumable (re-run/v:epicafter a fix). Every per-feature concern is reused unchanged (scope gate, model-broker, failure-handling, scorecards). Honesty boundary, stated loudly: epic mode is autonomous chaining, not "guess a product from one sentence" — each feature still needs a real spec; large epics run sequentially feature-by-feature (parallelism is within a feature, no cross-feature parallel dispatch in v1.1); quality is bounded by per-feature spec + partition quality. Docs:skills/compound-v/epic-mode.md(the model, the resumable run-dir layout, the integration review, the honesty boundary);skills/compound-v/SKILL.mdlists epic mode as a capability.
- Antigravity (
agy) is now a real backend, not a stub. A Bash-spawnedagy --printworker (scripts/compound-v-run-antigravity-worker.sh) that mirrors the Codex worker: runs one file-scoped job inside a dedicated$TMPDIRgit worktree at HEAD, then emits the canonicaljob_result. Same CLI shape as the codex worker (--run-id/--job-id/--repo/--prompt-file/--model/--write-allowed/--timeout-sec/[--read-only]/[--network]/[--output-schema]), same id-safety + timeout-int guards, same delegation to the deterministic scope gatescripts/compound-v-scope-check.pyfor git-derived enforcement (baseline SHA captured beforeworktree addso an in-worktree commit can't hide changes), and the same fail-closed status +failure_class/retry_after_secondsemit. Verified live against agy 1.0.13:cd "$WT" && agy --dangerously-skip-permissions --add-dir "$WT" --print-timeout "<sec>s" [--model …] --print "<prompt>"— flag order is load-bearing (--printMUST be last; its value is the prompt). Summary comes from agy's printed stdout;session_idis""(agy exposes no resumable session UUID).--modelis optional (omitted when empty);--read-only/--network/--output-schemaare accepted for CLI parity but advisory/ignored (agy has no kernel sandbox toggle or output-schema flag). ⚠️ Lower-trust, opt-in backend (documented loudly). Unlike Codex's--sandbox workspace-write(a kernel-level write-confinement root),agyhas NO kernel write-confinement, and headless writes require--dangerously-skip-permissions— which lets the agent run arbitrary shell and write outside the worktree. The worktree + post-hocgit diffgate enforces file-scope inside the worktree (detection) but cannot prevent an out-of-worktree write/shell side-effect. So Antigravity is opt-in / lower-trust — prefer Codex (kernel-sandboxed) for untrusted / high-stakes work.antigravity ⇒ worktreeinvariant added toscripts/compound-v-validate-manifest.py(mirrorscodex ⇒ worktree): an external worker with no kernel sandbox must be worktree-isolated. New self-test:antigravity+isolation: direct→ INVALID.- Failure classifier (
scripts/compound-v-classify-failure.py) gains anantigravitybackend with Gemini/agy error rules. Gemini reusesRESOURCE_EXHAUSTEDfor both quota exhaustion and per-minute throttling, so theout_of_creditsneedles are deliberately quota/billing/credit-specific (billing,exceeded your current quota,insufficient credit, …) — barequota/exceeded your/usage limitare excluded so throttle text like "Quota exceeded for quota metric … per minute" or "exceeded your rate limit" classifies asrate_limited(transient retry), notout_of_credits(which would force a needless backend reroute); ambiguous exhaustion falls through to the saferrate_limited. Alsoauth(permission_denied/401/403),context_length,overloaded(503/500/unavailable),network. Self-tests cover the quota-vs-throttle split (per-minute quota → rate_limited; hard billing → out_of_credits; permission_denied → auth). - Model resolver (
scripts/compound-v-resolve-model.py) carries a fallback antigravity map (deep → Gemini 3.1 Pro (High),standard → Gemini 3.1 Pro (Low),light → Gemini 3.5 Flash (Low)) used when no discovered map is present; the worker omits--modelif the resolved value is empty. New self-test forantigravity/deep. - Docs:
skills/backend-launcher/adapter-antigravity.mdreplaced the stub with the real adapter runbook (6 load-bearing steps, verifiedagyinvocation + flag order, worktree + git-diff scope gate, no resume, and a prominent Safety section);skills/backend-launcher/SKILL.mdupdated (real adapter, lower-trust caveat);skills/compound-v/routing-policy.mdlists antigravity as a selectable alternative forlarge_isolated(env-aware: only whenagyis installed) with theantigravity ⇒ worktreeinvariant and the prefer-Codex safety note;commands/v-init.mddetectsagyand records it as a lower-trust backend.
- Antigravity model auto-discovery.
agy models </dev/nullreturns the live catalog headlessly (it just waits on stdin — the same</dev/nullfix already used foragy --print; no TTY needed, ~2s), andscripts/compound-v-discover-models.py(pure parse + rank — the caller fetches the catalog and pipes it in; the script never calls a backend) ranks it into a deep/standard/light proposal written to.claude/compound-v.json(--write-configmerges into themodels.antigravityblock, preserving the other backends)./v:modelsand/v:inituse it —agy models </dev/null | python3 scripts/compound-v-discover-models.py --backend antigravity --write-config .claude/compound-v.json— so the tier map tracks the live catalog instead of a hand-curated list. Against agy 1.0.13 the proposal isdeep: Gemini 3.1 Pro (High),standard: Gemini 3.1 Pro (Low),light: Gemini 3.5 Flash (Low). Corrected the earlier "agy modelshangs / needs a TTY" claim — it does not; it is auto-discoverable headlessly.
- Worker scorecards — a data-driven routing signal from measured outcomes (PRD §8). Routing was a static guess (a task-type → a fixed backend/tier, applied the same in every repo); scorecards make it adaptive.
scripts/compound-v-scorecard.pydeterministically aggregatesdocs/superpowers/memory/task-outcomes.jsonlintodocs/superpowers/memory/worker-performance.jsonl— one row per(backend, type)with{total, success, blocked, error, timeout, avg_rework, block_rate, error_rate, success_rate, health}, wherehealth ∈ {insufficient_data, healthy, watch, unhealthy}(a cell needs ≥5 samples to be judged; below that it staysinsufficient_data). CLI:--update [--outcomes P] [--out P]regenerates the file;--query --backend B --type Tprints one cell's stats + health. Before assigning a task-type's static-default backend, the router queries the measured health of that(backend × task-type)in this repo and acts on it:unhealthy→ escalate UP a fixed trust ordering (claude≥codex≥antigravity) to an equal-or-higher-trust seat (Opus is the safe escalation) with a one-line justification — it never auto-routes to a lower-trust backend (a Codex-unhealthy cell escalates to Opus, never silently to Antigravity, which is explicit opt-in only);watch→ keep the default but note it;healthy/insufficient_data→ static default unchanged. Scorecards are a hint layered on the static policy, not a replacement, and only ever make routing more conservative (escalate up), never weaker — the HARD invariants (reviewers⇒opus, Codex⇒worktree, unclear⇒planning, sensitive surfaces⇒deep) are untouched. The scorecard never modifies the human-curatedrouting-lessons.mdand emits no cost/token metrics (anti-ruflo).worker-performance.jsonlis machine-generated, regenerated each run bycompound-v-scorecard.py --updateafter the dispatcher appends fresh outcomes — never hand-edited. Wired intoagents/parallel-dispatcher.md(post-run memory step + per-job routing query),skills/compound-v/routing-policy.md(§Scorecard-aware routing), andskills/compound-v/SKILL.md(memory layout).
- Cross-model (Codex) adversarial review of the v1.1 diff, fixed to convergence. A read-only Codex
gpt-5.5/high pass reviewed the new code over two rounds; every real finding was fixed: epic-state fail-fast + crash-reconcile semantics, scorecard trust-ordering (escalate up, never auto-downgrade to a lower-trust backend), and the classifier's quota-vs-throttle needle narrowing (all reflected in the entries above). Two findings were follow-up-tracked, then completed below. - Newline-safe path transport in both backend workers.
compound-v-run-codex-worker.shandcompound-v-run-antigravity-worker.shnow pass the scope gate's.changed/.violationsJSON arrays straight through (jq --argjson) instead of a newline-joined round-trip, so a filename containing a literal newline stays one element infiles_changed/violations; the blocked decision keys off a gate-derivedviol_count(a JSON[]is non-empty). The gate itself (compound-v-scope-check.py) was already NUL-correct, so the BLOCK decision was always right — this fixes the reported arrays. Newscripts/test-worker-path-transport.shself-test; fixed byte-identically in both workers. - TMPDIR / worktree-root canonicalization (both workers). Require an absolute
$TMPDIR, canonicalize the tmp root up front and build the worktree parent from the real path, reject a symlinked parent, and assert the worktree lives outside the repo — defense-in-depth on the existing id-character + symlink-safe containment guards. The deterministic$RUN_ID/$JOB_IDworktree path is kept on purpose (resume/cleanup locate the worktree by it, so no randommktemp -d).
Compound V graduates from a description-driven skill-pack into a lightweight execution orchestrator. The three pre-flights and /v:archaeology are behaviourally unchanged; the orchestrator extends the tail of the flow (manifest → dispatch → scope-gate → collect → review → memory) with multi-backend execution, per-job isolation, and crash-resume. No daemon, no MCP server, no vector DB, and no fabricated token-cost metrics (the anti-ruflo charter). Built by dogfooding the Compound V pipeline on this repo.
- Execution manifest (
skills/compound-v/execution-manifest.md,examples/manifest.example.yaml). A machine-readablemanifest.yamlof file-scoped jobs — backend · optionaltier/effort· isolation ·write_allowed/read_allowed· per-job and feature-level acceptance criteria — materialized from the verified Partition Map immediately afterwriting-plans. A job carries an optionaltier+effort;modelbecomes an optional override. A job MUST havemodelortier(backward-compatible: existing explicit-modeljobs stay valid); reviewer jobs must resolve totier=deepormodel=opus. This is the contract between planner and executors. - Backend Launcher sub-skill (
skills/backend-launcher/). Onejob_spec → job_resultcontract (schemas/job_result.schema.json) that every adapter implements; the orchestrator speaks only this contract and never sees backend-specific flags. Adapters:adapter-claude.md(Task-based, model override,maxTurns: 15),adapter-codex.md(headlesscodex execin a git worktree),adapter-antigravity.md(stub — see dispositions below). - Headless Codex worker (
scripts/compound-v-run-codex-worker.sh). Runs one file-scoped job oncodex execinside a dedicated$TMPDIRgit worktree, then emits the canonicaljob_result. Verified againstcodex-cli 0.130: the flag set is--cd / --sandbox / --skip-git-repo-check / --model / --output-last-message / -c sandbox_workspace_write.network_access(plus optional--output-schema).--ask-for-approval neveris invalid forcodex execand is omitted —execalready defaults toapproval: never. Resume iscodex exec resume <uuid>. The cosmetic[features].codex_hooks is deprecatedstderr is suppressed. - Scope gate (
scripts/compound-v-scope-check.py). The deterministic authority behind the proseSCOPE LOCK. After every job it unionsgit diff --name-only <baseline>withgit ls-files --others --exclude-standardand the gitignored set (git ls-files --others --ignored --exclude-standard -- .) and tests each changed path againstwrite_allowed. A violation is BLOCKED — the job never merges and the run halts. Enforcement fields (files_changed/violations/blocked) are git-derived, never model-self-reported. - Manifest validator (
scripts/compound-v-validate-manifest.py). A deterministic invariant gate thepartition-reviewerruns: disjointwrite_allowed, Codex⇒worktree, reviewers⇒Opus/deep, shared resources in the serial Task 0. Extended for the model-broker:tier ∈ {deep,standard,light}andeffort ∈ {low,medium,high}when present, and every job must carrymodelortier. - State machine + crash-resume (
skills/compound-v/state-machine.md). A lightweightstate.json(not an FSM engine) tracks phase + per-job status underdocs/superpowers/execution/<run-id>/./v:resumereconcilesstate.jsonagainst git reality (git-wins tie-break) and re-dispatches onlypending/failed/blockedjobs. Resume lives in Engine A so it survives a hard crash. - Result collector + lean memory (
scripts/compound-v-collect-results.py,scripts/compound-v-update-memory.py,docs/superpowers/memory/routing-lessons.md). Normalizes heterogeneous worker output into schema-conformingjob_results, folds in the scope verdict, and appends one line per job totask-outcomes.jsonl.routing-lessons.mdis human-curated. No semantic search, no scorecards in 1.0. - Routing policy (
skills/compound-v/routing-policy.md). task-type → (tier, effort) + backend/isolation (no concrete model strings in the table). Balanced default; Conservative and Cost-aware stances; env-aware Claude-only fallback when Codex is absent. Documents the configmodelsmap, the resolver, and/v:models. Citesrouting-lessons.mdas a consulted input. /v:init(commands/v-init.md). Detects Codex CLI / Context7 MCP / required skills, walks through any missing installs one at a time, re-probes the Codex flag set againstcodex exec --help, sets the routing stance, and saves config: project.claude/compound-v.json(stance + a seeded defaultmodelsmap so routing works out of the box — mentions/v:modelsfor refresh/customization) + user~/.claude/compound-v-capabilities.json(capability cache).- New commands
/v:orchestrate,/v:collect,/v:status,/v:resume,/v:models(commands/). - Skill escalation policy (
skills/compound-v/skill-escalation.md). Gated pull-in of deep-research / playground / avoid-ai-writing, plus forced Context7 — only when genuinely needed, each logged in the run's reasoning. - Strict
job_resultschema (schemas/job_result.schema.json) and committed fixtures (examples/) so CI validates real data. - New CI gates in
validate.yml: schema validity, manifest-invariant check, collector schema-conformance, and a no-fabricated-cost-metric grep. - Cross-model plan review (optional, gated). A different model family (Codex/GPT) adversarially reviews a high-stakes plan/manifest before dispatch — the value is error decorrelation (a second Opus shares Opus's blind spots; Codex has different priors). Policy in
skills/compound-v/cross-model-review.md; the read-only reviewer isscripts/compound-v-codex-review.sh, emitting findings againstschemas/plan-review.schema.json. Advisory only — the orchestrator arbitrates every finding; Codex is never the authority. Gated by stakes (security/auth/payments/migrations/shared data model, large/coupled partition, architectural change, or human request); skipped for small/mechanical plans. Wired in after thepartition-reviewerPASS inphase-3and surfaced by thepartition-revieweragent; manually triggerable via the new/v:review-plancommand. - Graceful backend-failure handling (classify → retry / reroute / halt). When a dispatched job returns non-success, the dispatcher runs a deterministic two-stage pipeline instead of guessing or blindly retrying.
scripts/compound-v-classify-failure.pyclassifies the failure from exit code + stderr (codex) or the stream-jsonapi_retry.errorenum (claude) into one of{out_of_credits, rate_limited, overloaded, auth, context_length, timeout, network, other, none}— by error TYPE, not HTTP status (OpenAIinsufficient_quotaand a throttle are both 429; the Anthropic credit error is a 400/402, not 429).scripts/compound-v-failure-policy.pyis the static decision table:out_of_credits/authnever retry (out_of_creditscircuit-breaks the backend for the run and re-routes the remaining jobs via the env-aware codex→claude rewrite — the SAME runtime rewrite, not just/v:init;authhalts for re-auth); transient classes retry the same backend with exponential backoff + jitter (honoringretry-after), capped per-class AND by a run-levelmax_total_retries(anti retry-storm);context_lengthre-routes withescalate_tier(bigger tier, or split the job).job_resultgains afailure_classfield (Codex worker emits it;nullon success/blocked). The "circuit breaker" isstate.jsonfields read at batch boundaries (no daemon):attempts/cooldowns/circuit_open/total_retries/max_total_retries; a transient failure only deprioritizes (short cooldown, probed half-open next batch) while a confirmedout_of_credits/authopens the breaker for the run. A failed job past its retry budget is markedfailedand the batch continues (ralph-tui-style — independent jobs don't die because a sibling 429'd); the run halts only when the last viable backend is exhausted (→/v:resumeafter top-up). Every re-route/circuit-break is loud — surfaced in/v:statusand the run summary with the cost direction; never a silent cheap→expensive swap. Policy inskills/compound-v/failure-policy.md; wired intoagents/parallel-dispatcher.md(Step 2c),phase-3,state-machine.md,routing-policy.md,commands/v-status.md, and the backend-launcher contract. claude has no further local fallback in 1.0 (antigravity is 1.1), so anout_of_credits/authon claude halts rather than re-routes.
A second independent Codex review went deeper and surfaced eight more findings — including a critical enforcement bypass and two regressions introduced by round 1; all are fixed:
- Commit-inside-worktree bypass of the scope gate (CRITICAL). The gate keyed off uncommitted
git diff HEAD∪ untracked, so an executor that COMMITTED its changes inside its worktree left a clean tree and slipped past enforcement.compound-v-run-codex-worker.shnow captures the baseline SHA withgit rev-parse HEADBEFOREgit worktree addand passes--baseline <sha>(notHEAD) tocompound-v-scope-check.py, so agit diff <baseline-sha>still includes the committed change and BLOCKS it. New scope-gate self-test: a file committed inside a worktree, outsidewrite_allowed, must block. - Timeout argv-injection guard.
--timeout-secis interpolated unquoted into thetimeoutargv in both bash wrappers; a crafted value like5; touch /tmp/PWNEDinjected argv. Bothcompound-v-run-codex-worker.shandcompound-v-codex-review.shnow reject any non-^[0-9]+$value withdie. - macOS-symlink-safe containment (REGRESSION fix). Round 1's containment assertion compared a canonical (
pwd -P) parent against a raw$WTprefix; on macOS$TMPDIRis/var/folders/...while its canonical form is/private/var/folders/..., so the prefix check falsely rejected every valid run. The worker now canonicalizes BOTH sides before comparing; the id-character regex (no/, no..) remains the real traversal defense. - Direct-mode pre-existing snapshot (REGRESSION fix). Round 1's gitignored/untracked union made direct-mode checks flag PRE-EXISTING untracked/ignored files a job never created.
compound-v-scope-check.pygains--preexisting <file>(paths present before the job, one per line) that are excluded from the changed/violation set;parallel-dispatcher.mddocuments the dispatcher snapshotting pre-existing untracked+ignored for a direct job and passing--baseline <sha> --preexisting <snapshot>. New self-test: a snapshotted pre-existing file is not flagged, while a new out-of-scope file still BLOCKS. (Worktree mode is unaffected — a freshworktree add HEADhas no pre-existing untracked.) - Backend enum aligned to
antigravity.compound-v-validate-manifest.pyaccepted an undocumentednoneand rejected the documented stub backendantigravity. The job-backend enum is now{claude, codex, antigravity}(noneis the routing "return to planning" sentinel, never a dispatched job);execution-manifest.mdandrouting-policy.mdwording matches. - Validator requires the remaining top-level fields.
compound-v-validate-manifest.pynow also requires top-levelspec_path,plan_path, andaudits(joining the round-1run_id/feature/acceptance_criteria/routing_stance/max_parallelset);examples/manifest.example.yamlstill validates. - Collector job-id traversal guard.
compound-v-collect-results.pybuilds<run-dir>/results/<job-id>.json;--job-idis now validated against^[A-Za-z0-9._-]+$(rejecting./..) before any path is built, exiting non-zero on a bad id (same class as the round-1 worker guard, previously missed here). - Empty write-scope allowed for review jobs.
compound-v-run-codex-worker.shno longerdies on an empty--write-allowed; an empty allow-list means NO writes are permitted, so the gate treats any changed path as a violation.adapter-codex.mddocuments empty write-scope = read-only/review job.
A second hardening pass on the graceful backend-failure feature, tightening the executable behavior and the docs that describe it:
- Fail-closed enforcement faults. A worker
error/timeoutstatus can no longer carryfailure_class: none— a genuine failure can't masquerade as success and skip the policy loop. - Fallback-health-aware reroute.
compound-v-failure-policy.pygained--fallback-open: anout_of_creditswhose only fallback is itself circuit-open now returnshalt(both causes surfaced) instead of a doomed reroute. The dispatcher passes it whencircuit_open[<fallback-backend>].openis true. - Deepest-tier context guard. The policy gained
--current-tier {deep|standard|light}: acontext_lengthfailure escalates a tier unless already at the deepest tier (deep), where it halts and the job is split (back to planning) rather than escalating into a model that doesn't exist. - Real claude enum parsing. The classifier now parses the claude stream-json
api_retry.errorenum and maps the exact value (billing_error→out_of_credits, etc.); the claude substring needles are a narrow fallback used only when the output isn't JSON (no barecontext/invalid_request, which would mis-escalate). Run the adapter with--output-format stream-json. Retry-Afterhonored. The classifier extracts the provider wait;job_resultcarries it asretry_after_seconds(int), which the dispatcher passes as--retry-afterso a retry sleeps the provider's stated time instead of synthetic backoff.- Circuit breaker is a reconciled object.
state.jsoncircuit_openis now{ "<backend>": { "open", "reason": "out_of_credits|auth", "opened_at", "cleared_by" } }(not a bare bool)./v:resumereconciles it byreason—out_of_creditsstays open until a top-up or a liveness probe,authuntil re-auth (/v:init) — and never silently re-dispatches to a still-open breaker. - Per-(job, class) attempts.
state.jsonattemptsis keyed{ "<job>": { "<failure-class>": n } }, so a budget consumed by one class doesn't starve another; the counter resets/forks on a backend re-route or class change. The dispatcher passesattempts[job][class]as--attempts.
Docs updated to match the scripts (no behavior is encoded in prose that the scripts don't enforce): skills/compound-v/failure-policy.md, skills/compound-v/state-machine.md, agents/parallel-dispatcher.md, commands/v-resume.md, skills/backend-launcher/adapter-claude.md.
A third pass closing what a cross-model review of the round-2 code surfaced:
- Collector parity (critical regression fix).
compound-v-collect-results.pynow emits the new requiredfailure_class+retry_after_secondsfields, so a normalizedclaude/directjob_resultsatisfiesjob_result.schema.json(its hand-rolled conformance checker now also handles nullable["string","null"]types). - Auth opens the breaker. Opening
circuit_open[<backend>]is keyed on the policy'scircuit_break: true— true forauthas well asout_of_credits— not only the out_of_credits reroute path. - Retries write a cooldown timestamp. The
retryaction recordscooldowns[<backend>] = now + backoff_secondsbefore sleeping, so the resume/half-open logic has a real timestamp to probe. - Mid-batch circuit-break is check-before-launch. Before launching each job the dispatcher checks
circuit_open[backend]; in-flight jobs on a newly-broken backend complete and fail-fast (a no-daemon dispatcher can't un-launch them). - Codex 5xx → overloaded.
server_error/5xxfrom codex now classify asoverloaded(retryable), notother.
A third independent Codex review pass (0 critical, 3 high, 5 medium) produced quick real fixes plus honest documentation of inherent limits:
model: haikuexecution-layer override rejected. The never-Haiku policy was only checked in frontmatter (lint-frontmatter.py), but a manifest job could pinmodel: haiku(orclaude-haiku-...) as an execution-layer override and slip through.compound-v-validate-manifest.pynow flags ANY job whose explicitmodelcontains "haiku" (case-insensitive) as a violation. New self-test: a job withmodel: haikuis INVALID.depends_ongraph validated (refs + cycles).compound-v-validate-manifest.pynow validates each job'sdepends_on: every referenced id must exist among the manifest job ids (a dangling ref is a violation), and the dependency graph must be acyclic (cycle detection via DFS, naming the jobs on the cycle). New self-tests: dangling ref INVALID, cycle INVALID, valid DAG OK.- Manifest structural type-checks. Required fields are now type-checked, not just presence-checked:
jobsnon-empty list,acceptance_criterialist,auditsmapping,max_parallelint,run_id/feature/spec_path/plan_pathstrings, and per-jobwrite_allowed/read_allowed/acceptancelists. A wrong-typed field is its own specific violation;examples/manifest.example.yamlstill validates. - NUL-safe scope-gate path handling.
compound-v-scope-check.pyswitched all three git probes to NUL-delimited output (git diff --name-only -z,git ls-files --others --exclude-standard -z, and the-zignored variant) and splits on\0, so a filename containing a newline cannot smuggle additional paths past the gate. New self-test: an unusual filename (a name with a space, and a name with a literal newline where the FS allows) is attributed as a single path and BLOCKS correctly. - Documented inherent limit:
read_allowedis advisory. Onlywrite_allowedis git-enforced; git cannot track reads, soread_alloweddocuments intent and scopes the prompt but is NOT a hard boundary. Stated plainly inexecution-manifest.md,backend-launcher/SKILL.md,adapter-codex.md, andadapter-claude.md. - Documented inherent limit:
direct-mode dirty-tree caveat → preferworktree.isolation: directgates against a baseline minus a pre-existing untracked/ignored snapshot, so a job that MODIFIES a pre-existing untracked/ignored file is not flagged.worktree(a fresh checkout with no pre-existing files) is the exact-gate safe default for anything untrusted or on a dirty tree;directstays serial-only and is for trusted, clean-tree jobs. Documented inexecution-manifest.mdandrouting-policy.md. - Stale merge-back instructions corrected. Removed the remaining
git diff HEAD | git apply(drops untracked additions) merge-back forms inadapter-claude.md,phase-3-parallel-opus-dispatch.md,compound-v/SKILL.md, and this CHANGELOG's model-broker note, replacing them with the index-based patch (git add -A && git diff --cached --binary HEAD | git apply --index) used everywhere else. - Clarified deliberate design: agent-driven flow, deterministic enforcement. Added a note in
phase-3-parallel-opus-dispatch.mdthat the orchestration flow is intentionally agent-driven (Engine A, anti-ruflo: no daemon) while enforcement lives in deterministic scripts (scope-check / validate-manifest) — the safety guarantees are in the scripts, not the flow.
A pass of an independent Codex code review surfaced eight correctness/security findings in the orchestrator scripts and docs; all are fixed:
- Path-traversal guard on
run_id/job_id(CRITICAL).compound-v-run-codex-worker.shbuilt a worktree path from these ids and rangit worktree remove -f || rm -rfon it — a../in an id could escape$TMPDIRand delete arbitrary directories. Ids are now validated against^[A-Za-z0-9._-]+$(rejecting./..) before any path is built, and the worktree path is asserted to live strictly under$TMPDIR/compound-v/before any removal.compound-v-validate-manifest.pyrejects the same unsafe ids (andrun_id) so a malicious manifest never reaches dispatch. - Worker delegates enforcement to the Python gate. The worker previously derived
violations/files_changed/statuswith a bashcase-glob matcher that was weaker than the Python authority (bash*matches/) and diverged from it. The bash matcher is deleted; after the codex run the worker now callscompound-v-scope-check.py(parsed withjq) as the single source of truth, layering timeout/error exit codes on top. - Scope gate now sees gitignored writes.
compound-v-scope-check.pyonly probedgit ls-files --others --exclude-standard, which excludes ignored files — a worker could write a gitignored path (dist/,.env) undetected. It now also unionsgit ls-files --others --ignored --exclude-standard -- ., so any ignored write outsidewrite_allowedis reported and BLOCKS (covered by a new self-test). - Allowed new files survive merge-back. The documented merge-back
git diff HEAD | git applysilently dropped untracked (new) files — an allowed new file passed the gate but was lost. Replaced everywhere with an index-based patch that includes additions (git add -A && git diff --cached --binary HEAD | git apply --index) acrossbackend-launcher/SKILL.md,adapter-codex.md,parallel-dispatcher.md, and the PRD/plan. - Direct-mode scope check requires
--baseline. A--repo(direct) job's baseline must be the recorded pre-dispatch commit, not a defaulted (possibly-moved) HEAD; the gate now errors if--baselineis omitted in direct mode. Worktree mode keeps the HEAD default (worktrees are fresh from HEAD). - Validator enforces all required fields +
parallel ⇒ worktree.compound-v-validate-manifest.pynow validates every required top-level and per-job field and their enums (backend/isolation/run/routing_stance/tier/effort) before the invariant checks, and rejects anyrun: parallel+isolation: directjob (per-job scope attribution requires worktree isolation). The example manifest's parallel claude jobs moved toisolation: worktree;execution-manifest.mdandrouting-policy.mdstate the rule crisply (parallel ⇒ worktree; direct ⇒ serial). - Collector can no longer override the scope verdict. In
compound-v-collect-results.pythe--files-changed/--violations/--blockedflags are now additive-only when a scope verdict is present:blocked= scope OR flag,violations/files_changed= union(scope, flag). A flag may force a block or add entries but can never clear a scope-gate block or drop a scope violation.
validate-manifest.pyglobs_overlapsoundness fix. The manifest validator's write-glob overlap test (rule 1, disjoint writes) had a soundness bug — caught on the first real cross-model review run when Codex read the repo and flagged it. Hardened so overlappingwrite_allowedglobs are reliably detected.
Stops hardcoding model strings. Jobs route by intent, not by a literal model name, so the plugin survives model churn and gains Codex's reasoning-effort dimension.
- Tier + effort vocabulary — a stable routing vocabulary that never changes when models churn.
tier ∈ {deep, standard, light}(deep = strongest reasoning: architecture, security/auth/payments, designing tests, external APIs, all reviewers, the shared-foundation Task 0; standard = bounded core/feature build incl. large isolated Codex work; light = mechanical single-file / docs / i18n).effort ∈ {low, medium, high}is an orthogonal hint with a sensible default pairing (deep→high, standard→medium, light→low) that stays independently tunable per task-type. - Refreshable config model-map —
.claude/compound-v.jsongains amodelsmap (claude/codex/antigravity, eachdeep/standard/light→ a concrete model). The map is not committed in the repo — it is documented and seeded by/v:init, then refreshed via/v:models. Claude uses native tier aliases (opus/sonnet), Codex is a curated+user-overridable list (it has nomodelslist command), and Antigravity values are auto-discovered fromagy models(see the 1.1 auto-discovery note). Neverhaiku, anywhere. - Model resolver (
scripts/compound-v-resolve-model.py). Generic — no backend-specific Codex/Antigravity logic baked into routing. CLI:--backend {claude|codex|antigravity} --tier {deep|standard|light} [--effort {low|medium|high}] [--config PATH] [--explicit-model M]. Carries a built-in default map so it resolves with no config file; amodels.<backend>.<tier>entry in--configoverrides the default;--explicit-model(a manifest override) always wins. Emits one JSON object on stdout —{ "backend", "tier", "model", "effort" }— and exits non-zero when a tier can't be resolved. Python 3.9-safe, stdlib only. - Codex
--effort—scripts/compound-v-run-codex-worker.shgains an optional--effort {low|medium|high}arg that appends-c model_reasoning_effort=<effort>to bothcodex execinvocations (with and without--output-schema). Everything already there is preserved: the</dev/nullstdin redirect, stdout capture, scratch-outside-worktree handling, no--ask-for-approval never, bash 3.2 safety, shellcheck-clean. /v:models(commands/v-models.md). Discovers available models per backend —agy modelsfor Antigravity (when present), a curated list for Codex, native tiers for Claude — shows them, lets the user assign tier→model, and writes themodelsmap into.claude/compound-v.json. This is the "skill picks the models and offers you options" surface.
plugin.json+marketplace.json→1.0.0in lockstep; added theorchestratorkeyword.SKILL.mdevolved to orchestrator-as-default — the description now mentions manifest materialization and the scope-enforced, resumable pipeline, without weakening the auto-fire triggers (every existingevals.jsoncase still passes)./v:dispatchevolved to be manifest-aware backward-compatibly: it accepts a bare plan path (auto-materializing a manifest), a manifest, or a run-id. The 0.1.x plan-path flow — and theplan-saved-nudgehook — keep working.- Agents evolved:
parallel-dispatcheris manifest-driven and multi-backend — for each job it runscompound-v-resolve-model.pywith(backend, tier, effort, config)before dispatch to get the concrete model, passes--model <resolved>(+--effortfor Codex) to the worker, then callsscope-check.pyafter every job and HALTS on BLOCKED (an explicit manifestmodelskips resolution);partition-reviewerrunsvalidate-manifest.pyas its deterministic backing gate;spec-reviewerruns the three-pass Review Gate (spec acceptance criteria · quality/no-regression/no-fabricated-metrics · final integration). All reviewers remainmodel: opus. The agent's ownmodel: opusfrontmatter is unrelated to execution-layer resolution; resolved manifest models (gpt-5.5, etc.) are execution-layer data and never appear in frontmatter. - Phases evolved:
phase-2emitsmanifest.yaml(not only prose);phase-3is manifest-driven multi-backend dispatch with per-job isolation and the scope gate. - Hooks evolved:
session-banner.shadds a/v:inithint when.claude/compound-v.jsonis absent;plan-saved-nudge.shmentions/v:orchestratealongside the existing dispatch path. Both keep all three platform JSON branches and stayshellcheck-clean.
- Antigravity adapter = stub, deferred to 1.1. Assessed, not assumed. Google's official
agyCLI fits the contract, but two blockers keep it out of 1.0: headlessagy --printreturns empty stdout when piped/redirected (#408, #318) and there is no non-interactive auth (#223).adapter-antigravity.mdships as a stub returningunsupported; the 1.1 spike targets the Antigravity Python SDK first. - Workflows accelerator = kept in 1.0 as opt-in (Engine C).
skills/compound-v/workflows-accelerator.mdis a capability-probed fast-path for large parallel batches (16-wide) that auto-falls-back to Engine A's batchedTaskdispatch when Workflows is absent or disabled. The scope gate andstate.jsonresume stay in Engine A even when C runs, so file-scope enforcement and crash-resume never regress. Engine B (claude -pshell-out) was rejected (rate-limit cascades + third-party-orchestrator policy).
- All helper scripts — including the new
compound-v-resolve-model.py— target stock-macOS bash 3.2 and python 3.9 (stdlib only; pyyaml optional with an embedded-subset fallback) and areshellcheck-clean and executable. - The
modelsmap is documentation + seeded config, never committed in the repo.compound-v-resolve-model.pyships with a built-in default map so routing works even with no config file present. - Worktrees live in
$TMPDIR/compound-v/<run-id>/<job-id>; merge-back on PASS is an index-based patch that includes new files (git -C <wt> add -A && git -C <wt> diff --cached --binary HEAD | (cd <repo> && git apply --index)) into the main tree — a plaingit diff HEAD | git applywould drop allowed untracked additions. - Honestly not auto-tested (documented + manually verified, no CI gate): the worker-prompt pre-emptive STOP behaviour (only the post-hoc scope-check is gated), Codex-session resume re-attachment, the
/v:initflag-probe, capability-cache staleness, and the Workflows probe-fails→fallback path.
- Marketplace name renamed from
superpowers-v-marketplacetoprocoders. End-user install command is now/plugin install superpowers-v@procoders(was the awkwardsuperpowers-v@superpowers-v-marketplace). Theprocodersname is also future-proof — additional procoders plugins can ship via the same marketplace. - README install section trimmed to one path at the top; local-clone /
--plugin-dirdev flows moved to a new Development section lower in the doc.
- Install instructions in README were wrong. Claimed
/plugin install <github-url>works directly; it does not. Real path is the documented two-step:/plugin marketplace add <url-or-path>first, then/plugin install <plugin>@<marketplace-name>. Reported by user trying to install v0.1.1 from GitHub and getting "Marketplace not found."
- Marketplace name renamed from
superpowers-v-devtosuperpowers-v-marketplace(mirrors the upstreamobra/superpowers→superpowers-marketplacenaming convention; cleaner for end-user-facing install command). - README install section now shows three install paths: marketplace + GitHub, marketplace + local clone, and
--plugin-dirlive-edit mode.
Honesty pass after an independent verification audit caught several fabricated CLI/env-var references that I had baked into hooks and docs without verifying against the official Claude Code documentation.
- Hook scripts no longer read fabricated environment variables. Rewrote
session-banner.shandplan-saved-nudge.shto follow the documented Claude Code hook interface: input read from JSON on stdin (viajq), output emitted as JSON foradditionalContextcontext injection. Pattern adapted from upstreamobra/superpowers v5.1.0reference hooks. Previous scripts read$CLAUDE_HOOK_MATCHERand$CLAUDE_TOOL_INPUT_FILE_PATH, neither of which exists in the official hook spec — the hooks were technically running but always silently no-op'd. - SessionStart matcher corrected from
*to the documented patternstartup|clear|compact(matches upstream superpowers).
compound-v:doctoragent +/v:doctorslash command — clutter for typical sessions; manual debug instructions in TROUBLESHOOTING.md cover the same ground.SubagentStophook configuration +sidekick-nudge.shscript — theSubagentStopevent is not in the official Claude Code hooks reference and the reference pluginobra/superpowersdoes not use it. Replaced with description-based auto-fire (which was always the primary mechanism) plus thePostToolUse(Write)plan-saved nudge.gemini-extension.json— manifest schema was not verifiable against official Gemini CLI docs; removed rather than ship a fabricated config.
- Multi-harness shims (AGENTS.md, GEMINI.md) marked 🧪 experimental / untested. Previous wording implied verified support; honest reality is the shims are based on documentation patterns but were not exercised on a real Codex or Gemini install. The README compatibility table now reflects this.
- README install steps: removed fictional
/mcp add context7command; correct install path is/plugin install context7@claude-plugins-officialor manual~/.claude.jsonMCP config. Context7 demoted from step 1 to step 3 (recommended, not required). - Phase 3 dispatcher announce string toned down (was "going Supe"; now neutral "dispatching N implementers").
- SKILL.md auto-fire caveat rewritten honestly: skill invocation is description-driven; hooks provide reminders but do NOT enforce the trigger.
.github/workflows/validate.ymlno longer validatesgemini-extension.json(file removed).
- Hard citation-rigor rules in
agents/domain-expert.md: ≥10 distinct community posts OR 1 official source for consensus claims; isolated reports flagged explicitly; no fabricated URLs; verbatim quotes only; empty section > padded section.
The verifier could not find official documentation for several Task tool parameters used throughout the plugin (subagent_type: "<plugin>:<agent>" plugin-namespaced syntax, maxTurns, run_in_background: true). These remain in the plugin's prompts and docs because they are observably functional in Claude Code as of v0.1.1, but should be revisited if they break in a future CC version. Tracked for future verification.
Initial public release.
Core skill (skills/compound-v/):
- Three-trigger interceptor for Superpowers transitions (after brainstorming, inside writing-plans, before execution)
- Phase 1A: code-archaeology pre-flight (five-phase audit of existing-code reality)
- Phase 1B: domain-expert advisor with three-layer parallel WebSearch (official docs, practitioner channels, audience/persona forums)
- Phase 1C: library/doc validator via Context7 MCP (catches stale deps, abandoned libraries, outdated API signatures)
- Phase 2: Disjoint File Partition Map enforcement inside writing-plans
- Phase 3: batched parallel Opus dispatch with strict scope locks;
model: opusby default,model: sonnetonly when a task ticks every box of the strict 8-box junior-task taxonomy
6 first-class agents (agents/) — invokable as subagent_type: "compound-v:<name>":
code-archaeologist,domain-expert,doc-validator,partition-reviewer,parallel-dispatcher,spec-reviewer
2 slash commands (commands/):
/v:archaeology <topic>,/v:dispatch <plan-path>
Hooks (hooks/) — sidekick auto-fire (text-printer only, no side effects):
SessionStartbanner reminding parent Claude that Compound V is loadedPostToolUse matcher=Writenudges when a plan or spec is saved
Operational:
.github/workflows/validate.yml— JSON schema, agent frontmatter (with no-Haiku project policy), dead-link scan, shellcheck on hooksscripts/lint-frontmatter.py— Python frontmatter linter for local pre-commitevals/evals.json— 8 trigger eval test cases (3 positive, 2 negative, 3 edge) for the compound-v skill.cclintrc.json— config for@felixgeelhaar/cclintTROUBLESHOOTING.md— common issues- All code blocks tagged with explicit language
Realistic concurrency limits documented: 4-6 foreground / 5-10 background Task calls per message; batched dispatch for larger plans; maxTurns: 15 cap; run_in_background: true recommended for implementer batch.
Output convention: docs/superpowers/{archaeology,expert,library-audit}/ with _knowledge-base/ subdirectories for cross-feature knowledge persistence.
Initial public release.
Core skill (skills/compound-v/):
- Three-trigger interceptor for Superpowers transitions (after brainstorming, inside writing-plans, before execution)
- Phase 1A: code-archaeology pre-flight (five-phase audit of existing-code reality)
- Phase 1B: domain-expert advisor with three-layer parallel WebSearch (official docs, practitioner channels, audience/persona forums)
- Phase 1C: library/doc validator via Context7 MCP (catches stale deps, abandoned libraries, outdated API signatures)
- Phase 2: Disjoint File Partition Map enforcement inside writing-plans
- Phase 3: batched parallel Opus dispatch with strict scope locks;
model: opusby default,model: sonnetonly when a task ticks every box of the strict 8-box junior-task taxonomy
6 first-class agents (agents/) — invokable as subagent_type: "compound-v:<name>":
code-archaeologist,domain-expert,doc-validator,partition-reviewer,parallel-dispatcher,spec-reviewer
2 slash commands (commands/):
/v:archaeology <topic>,/v:dispatch <plan-path>
Hooks (hooks/) — sidekick auto-fire (text-printer only, no side effects):
SessionStartbanner reminding parent Claude that Compound V is loadedSubagentStop matcher=brainstorming|writing-plansnudges with next-step dispatchPostToolUse matcher=Writenudges when a plan or spec is saved
Multi-harness compatibility shims (experimental):
AGENTS.md(Codex CLI)GEMINI.md+gemini-extension.json(Gemini CLI)
Operational:
.github/workflows/validate.yml— JSON schema, agent frontmatter (with no-Haiku project policy), dead-link scan, shellcheck on hooksscripts/lint-frontmatter.py— Python frontmatter linter for local pre-commitevals/evals.json— 8 trigger eval test cases (3 positive, 2 negative, 3 edge) for the compound-v skill.cclintrc.json— config for@felixgeelhaar/cclint(silences CLAUDE.md-specific false-positives)TROUBLESHOOTING.md— 11 documented common issues- All code blocks tagged with explicit language (
plaintext,markdown, etc.)
Realistic concurrency limits documented: 4-6 foreground / 5-10 background Task calls per message; batched dispatch for larger plans; maxTurns: 15 cap; run_in_background: true recommended for implementer batch.
Output convention: docs/superpowers/{archaeology,expert,library-audit}/ with _knowledge-base/ subdirectories for cross-feature knowledge persistence.