feat: evidence-driven review hub, agent review chains, and process layer v2 - #1022
feat: evidence-driven review hub, agent review chains, and process layer v2#1022web3dev1337 wants to merge 27 commits into
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…esis) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…arted
- prReviewAutomationService passed {provider, skipPermissions} but
startAgentWithConfig validates {agentId, mode, flags} — spawn always
failed validation, so PR review automation could never launch a reviewer
- session id now uses the workspace terminal's local repo name instead of
the GitHub slug (they can differ)
- prompt is submitted with a separate \r write (a trailing \n inside the
paste is not a submit)
- codex launches get the yolo flag in both reviewer + batch launch paths;
sandboxed codex stalls on approval prompts in unattended flows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/standards/handoff/diffStats) Normalized nested evidence object per the 2026-07-15 plan: what a human reviewer needs at a glance before approving/merging. Populated by evidenceService (next commit) from PR bodies/comments, worktree files, or direct API. All strings capped, arrays bounded, verdicts validated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Parses fenced ```agent-evidence JSON blocks from PR body, issue comments, and review bodies (travels with the PR, cross-machine; reviewer agents append their own blocks as comments) - Reads .agent-evidence.json from the task's worktree for local flows - Merge semantics: later sources win per section; reviews/media/data accumulate with de-dupe; standards union - Aggregates PR diff stats (files/additions/deletions) server-side — agent-supplied numbers never trusted for PRs - Media endpoint resolves only inside the server-recorded worktree root (agent blocks cannot set worktreePath), extension whitelist, traversal rejected - Routes: POST /api/process/evidence/:id/refresh, PUT /api/process/ evidence/:id, GET /api/process/evidence/:id/media/:idx Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e/merge Badge row (tests/app-ran/agent-reviews/media/data/diff-size) + expandable sections: per-role review verdicts with findings/fixed counts, screenshot thumbnails with a full-screen opaque viewer (no darkened-overlay modal), before/after data table, standards list, handoff notes, and a Refresh button that re-collects from PR comments + worktree. New focused files (client/queue-evidence.js + styles/queue-evidence.css); two-line wiring into showQueuePanel's renderDetail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- config/review-workflows.json: named chains (standard/hardened/full-gate)
with per-stage role + agentId + model + effort; riskDefaults map risk →
chain length per the research (p_chain = Πp: 30% → 9% → 2.7%); user
override deep-merged from ~/.agent-workspace/review-workflows.json
- reviewWorkflowService: sequential stage runner — spawns each reviewer
into an idle worktree, detects its GitHub review verdict by polling,
records the outcome into evidence.reviews[], advances / blocks on
needs_fix / stalls on timeout; run state persists on the task record
(reviewWorkflow field) so restarts resume polling
- agentSpawnHelper: extracted the worktree-locate + agent-launch mechanics
shared with prReviewAutomationService (one spawn path, DRY)
- sessionManager.buildClaudeCommand supports --model for per-role Claude
routing; codex stages use existing model/reasoning support
- Queue detail: 'Review workflow' block — chain picker (default chosen by
task risk), run/skip-stage/cancel, live stage chips with verdicts
- Routes: GET /api/process/review-workflows, POST .../:id/{start,advance,
cancel}
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
docs/agents/EVIDENCE_PROTOCOL.md is the canonical reference (schema, three reporting channels, reviewer-stage obligations, self-orchestrated chains, handoff notes for fresh-window reprompts). A compact self-contained snippet is appended to every batch-launched task prompt (toggle: global.ui.tasks.evidencePromptEnabled) — the snippet's own example block round-trips through the evidence parser, verified by test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The open-source release hid the whole workflow layer behind ~40 individual
ui.visibility flags with no Settings UI to flip them. Settings → UI Mode
now switches presets in one click: 'simple' = shipped defaults, 'power' =
process banner + workflow modes + tier filters + PRs/Review Route/Activity/
Diff + dashboard process cards + commander controls (intent hints stay
opt-in since they can call a model API).
Also removes the hardcoded display:none that overrode header.queue=true —
the Review Hub ('📥 Review') is visible by default again as the single
review entry point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anthropic prompt caches expire after ~1h idle. Review feedback now routes by cache age: warm original session (<55m since last prompt) gets the feedback inline as before; cold/missing sessions get a FRESH fixer agent spawned in a free worktree, seeded with the review feedback + the evidence handoff notes (this also implements the previously-stubbed autoSpawnFixer path — it only logged 'would spawn fixer'). Queue detail header shows a 🧊 'cache cold — fresh window on reprompt' chip past the threshold. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Local-only JSONL log (~/.agent-workspace/telemetry/context-switches.jsonl, never leaves the machine) of worktree focus, workspace switches, workflow mode changes and review start/end. Summary endpoint estimates refocus cost (research default 10 min/switch) and surfaces top thrash pairs + review focus minutes in the dashboard Telemetry overlay. Client hooks are fire-and-forget and deduped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Captured slash-commands were forwarded to the Commander PTY as a single '/clear\r' chunk, which agent CLIs receive as a bracketed paste — inserted as literal text, never executed. Split into two writes (text, 300ms, \r) per the established two-write submit pattern. Normal keystrokes were unaffected because they stream char-by-char, which is why only slash commands appeared broken. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- plugins/youtube-transcript: first working example plugin — paste a YouTube URL (Commander tools button or 'youtube-transcript-transcribe' command) → yt-dlp subtitles → clean plain-text transcript saved to ~/Downloads/transcripts/. Helpful error when yt-dlp is missing. - New 'post_route' slot action type (local routes only, optional single prompted input) — lets manifest-only plugins trigger their own server routes from UI buttons; validated in the loader, executed by plugin-host - New 'commander.tools' slot renderer: plugin buttons strip in the Commander panel (results echo into the Commander terminal) - Plugin routers get express.json() so plugin POST routes actually see req.body (previously undefined — app uses per-route parsers) - Settings → Plugins: loaded AND failed plugins visible (bad manifests no longer fail silently) + reload button - plugins/README.md: documents client.slots + action types + rendered slots (previously entirely undocumented) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oded hytopia)
The server-control start path had 'hytopia start' hardcoded for every
project type. Launch commands now resolve from the cascaded config:
serverCommand template + {{gameMode}}/{{commonFlags}} substitution
(gameModes/commonFlags were already merged by the cascade but nothing
consumed them — the templating the docs promised now exists). Fallbacks:
hytopia-game → 'hytopia start', everything else → 'npm run dev'.
Client: un-commented the Start Server ▶ dropdown (game modes from
getDynamicLaunchOptions) + Launch Settings gear, still gated behind the
existing terminal.startServer/serverLaunchMenu visibility flags (hidden by
default; the Power preset enables them).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
package.json was pinned to 1.13.4 in the supply-chain fix but this lockfile entry kept the caret range. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r post-mortem Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt model/effort syntax
Any CLI agent (Gemini, OpenCode, Grok, aider, ...) can now be registered
in ~/.agent-workspace/custom-agents.json (skeletons + docs in
config/custom-agents.example.json) with zero code changes:
- agentManager merges custom agents into the registry at boot (validated;
built-ins protected unless override:true); they surface automatically in
the /api/agents picker, batch launches, and review-workflow stages
- Per-agent CLI syntax for model/effort: modelFlag/reasoningFlag templates
('--model {model}' etc.); codex keeps -m + model_reasoning_effort,
claude keeps --model aliases; reasoning is only appended for agents that
declare support
- agentSpawnHelper resolves launch flags (agent's defaultFlags) and init
delay from the registry instead of hardcoded claude/codex branches, so
reviewer/fixer/workflow-stage spawns work for any registered agent
- statusDetector already carries gemini/opencode busy-idle heuristics —
custom agents with those ids get proper status lights out of the box
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…chestrating AIs)
CommanderManager holds N commander instances keyed by id; the primary keeps
id 'commander' so every existing route (no commanderId → primary) and the
single-panel behavior are byte-for-byte unchanged. Additional commanders
each get their own PTY + Claude launch + data dir (own CLAUDE.md persona).
- Server: id-aware PTY-lifecycle routes (status/start/start-claude/input/
resize/stop/restart/output/clear default to primary), scoped socket
payloads ({...,commanderId}), new list/spawn/remove routes, limit of 6,
primary is un-removable, unknown ids never auto-create ghosts
- Client: tab strip in the commander titlebar rebinds the one xterm to a
different backend PTY (tab-style, lowest DOM risk); output/exit socket
events filtered by active id; hidden by default (commander.tabs), on in
the Power preset
- 10 manager/instance unit tests
Full N-panel side-by-side rendering remains the documented follow-up; this
delivers the capability (multiple independent commanders) via switching.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An independent gpt-5.5 review of the full diff surfaced 8 issues; all fixed with tests (new server/utils/shellSafety.js + tests/unit/securityFixes.test.js): HIGH 1. Custom-agent command construction was shell-injectable — model/reasoning values + flag/template strings are now validated against a shell-safe allowlist; unsafe values are dropped and unsafe custom-agent configs are rejected at load. 2. serverLaunchCommandResolver interpolated repo-config flags/gameArgs into a shell command — same allowlist; a template carrying metacharacters falls back to the safe built-in default. 3. Evidence media endpoint was lexical-only — a symlink inside the worktree pointing out (…/leak.png -> ~/.ssh/id_rsa) could be served. Now realpath- confines target under root and rejects non-regular files. 4. refresh() trusted an arbitrary req.body.worktreePath as the media root — an explicit path is now honored only if it resolves to a known workspace worktree; the task-identity worktree:<path> stays trusted but is still realpath-confined at serve time. MEDIUM 5. Review-workflow poll cycles weren't serialized — added an in-flight guard + re-read/CAS on run state before patching (no double reviewer spawn). 6. Stage completion could be misattributed to a stray human/bot review in the window — now prefers a review whose body carries the stage's agent-evidence marker/role, falling back to latest. 7. Missing promptSentAt was treated as cold cache — now unknown+live-session delivers to the session; fresh fixer only on a known-stale timestamp. 8. post_route allowed '//host' and arbitrary same-origin paths — restricted to the plugin's own /api/plugins/<id>/ namespace (server + client). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generic PUT /api/process/task-records/:id accepts a raw evidence object, so a crafted record could plant an arbitrary worktreePath and have the media endpoint serve any readable file with an allowed extension. resolveMediaPath now re-checks the recorded root against the managed-worktree set regardless of how the record was written. Also: nosniff on the media route and forced download for SVG (script-capable on direct navigation in our own origin). Test fixture now registers its temp worktree as managed; adds a regression test for the planted-path case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ancellable advanceWorkflow now no-ops unless the run is live (running/blocked_fix/ stalled) — force-advancing a cancelled or complete run used to skip a stage and spawn a real agent into a worktree for a run the UI reported dead. spawnAgentInSession returns a cancel handle for its delayed prompt-injection timers, and cancelWorkflow uses it so cancelling inside the agent-init window no longer types the review prompt into the terminal seconds later. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er default - Evidence review/appRun URLs from PR bodies are only rendered as links when they are plain http(s) — entity-escaping alone still made javascript: URIs clickable. Non-http URLs render as text. - The client now listens for the server's review-workflow socket events and refreshes the open Queue detail, so stage chips progress without the user having to click start/skip/cancel again. - Evidence lightbox removes its document-level keydown listener on every dismiss path (click previously leaked one listener per open). - terminal.startServer ships false by default: the Start Server button was commented out before this branch, so default-on would have surfaced it for every existing user — the Power preset is what turns it on, matching the documented intent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Swept as part of the open-PR review (two scoped reviewers + fixes). Branch was already rebased on current main with zero drift. Fixed and pushed: (1) SECURITY — evidence.worktreePath could be planted via the generic task-records PUT and steer the media endpoint at any readable directory; resolveMediaPath now re-validates the recorded root against the managed-worktree set at read time (+ regression test), plus nosniff and forced download for SVG; (2) advanceWorkflow now refuses to resurrect cancelled/complete runs (it used to spawn a live agent for a dead run), and cancelWorkflow clears the in-flight spawn's delayed prompt-injection timers; (3) evidence review/appRun URLs are scheme-allowlisted (javascript: URIs were rendered clickable), the client now listens to the review-workflow socket events so stage chips update live, the lightbox keydown-listener leak is fixed, and terminal.startServer ships default-off so the Power preset is what enables it, matching the PR description. Full suite: 681/681 passing. |
Summary
Implements the tier-workflow vision from the
optimal-agent-orcestration-systemresearch on top of the existing (mostly hidden) Jan–Feb process layer. Full design rationale:PLANS/2026-07-15/EVIDENCE_REVIEW_WORKFLOWS_PLAN.md.The evidence system (centerpiece)
Everything a human needs at a glance before approve/merge, per finished task:
evidencefield on task records: tests (ran/command/passed/failed), appRun proof (puppeteer/server-smoke/studio/manual), agent review chain verdicts with findings/fixed counts, screenshots/video, before/after data for balance changes, standards reviewed against, handoff notes, diff statsevidenceServicecollects it from fencedagent-evidenceblocks in PR bodies/comments (travels with the PR),.agent-evidence.jsonin worktrees, or direct API; PR diff stats are computed server-side, never agent-claimeddocs/agents/EVIDENCE_PROTOCOL.mdteaches agents to self-report; a compact snippet is auto-appended to batch launch prompts (media endpoint only serves from the server-recorded worktree root — traversal rejected, extension whitelist)Multi-agent review chains (data-driven)
config/review-workflows.json: standard (1 general) / hardened (security→general) / full-gate (security+perf+general) with per-stage agent, model, effort; risk level picks the default chain (research: p_chain = Πp — 30% → 9% → 2.7%)reviewWorkflowServicespawns each stage into an idle worktree, detects its GitHub verdict, records the outcome intoevidence.reviews[], advances/blocks/stalls; run state persists across restartsclaude --model <alias>support added; codex uses existing model/reasoning flagsBugs found & fixed (the "spammed out, untested" batch)
startAgentWithConfigwas called with{provider, skipPermissions}instead of{agentId, mode, flags}— failed validation every time; also wrong repo name in session ids, and prompt+\nin one write (bracketed paste, never submits)/clear(and all slash commands): forwarded as a single"/clear\r"paste chunk — now text + delayed\r--dangerously-bypass-approvals-and-sandbox→ stalled on approvalsreq.body(no JSON parser on plugin routers)hytopia startfor every project typeResurfacing the hidden process layer
ui.visibilityflags that had no UI at all; the Review Hub (📥 Review) header button is visible by default again (a hardcodeddisplay:nonewas overriding itstrueflag)Prompt-cache freshness
autoSpawnFixer); 🧊 cache-cold chip in Queue detailContext-switch telemetry (local-only)
Plugins made real
~/Downloads/transcripts/)post_routeslot action (local routes only, optional prompted input), newcommander.toolsslot renderer, Settings → Plugins admin (failed plugins finally visible) + reload,client.slotsfinally documented inplugins/README.mdPlay buttons revived
serverCommand+{{gameMode}}/{{commonFlags}}templating — the substitution the docs promised now exists); Start Server dropdown re-enabled behind existing visibility flags (Power preset turns them on)Follow-up
PLANS/2026-07-15/MULTI_COMMANDER_FEASIBILITY.md(PR-sized follow-up)Test plan
npm run test:unit: 652 tests / 111 suites green (39 new tests across 8 new/extended suites)node scripts/check-command-surface-drift.js: clean🤖 Generated with Claude Code
Agent-agnostic launches (added after review)
Any CLI agent — Gemini, OpenCode, Grok, aider, anything — registers via
~/.agent-workspace/custom-agents.json(documented skeletons inconfig/custom-agents.example.json) with zero code changes: it appears in the agent picker, batch launches, and review-workflow stages, with its own launch flags, init delay, and per-agent model/effort CLI syntax (modelFlag/reasoningFlagtemplates). Reviewer/fixer/stage spawns resolve flags from the registry instead of hardcoded claude/codex branches. Status-light heuristics for gemini/opencode already existed and apply automatically.Multiple Commanders (added after review)
CommanderManagerruns N independent commander instances keyed by id — the primary keeps idcommanderso every existing route and the single-panel behavior are byte-for-byte unchanged. Additional commanders each get their own PTY + Claude launch + data dir (own CLAUDE.md persona). Id-aware PTY-lifecycle routes (default to primary), scoped socket payloads,list/spawn/removeroutes (limit 6, primary un-removable), and a titlebar tab-switcher that rebinds the one xterm to another backend PTY. Hidden by default (commander.tabs), on in the Power preset.Independent security review (Codex/gpt-5.5) + 8 fixes
Ran an independent review of the full branch diff; all 8 findings fixed with tests (
server/utils/shellSafety.js,tests/unit/securityFixes.test.js):{{gameMode}}/{{commonFlags}}templating → shell-safe allowlist on all config-derived values; unsafe configs rejected at load / fall back to safe defaults.refresh()trusted an arbitrary request-bodyworktreePath→ an explicit path is honored only if it resolves to a known workspace worktree.post_routeis restricted to the plugin's own/api/plugins/<id>/namespace (rejects//host).All 679 unit tests pass (114 suites).