Skip to content

feat: evidence-driven review hub, agent review chains, and process layer v2#1022

Open
web3dev1337 wants to merge 23 commits into
mainfrom
feature/review-inbox-and-tier-workflows
Open

feat: evidence-driven review hub, agent review chains, and process layer v2#1022
web3dev1337 wants to merge 23 commits into
mainfrom
feature/review-inbox-and-tier-workflows

Conversation

@web3dev1337

@web3dev1337 web3dev1337 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the tier-workflow vision from the optimal-agent-orcestration-system research 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:

  • New evidence field 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 stats
  • evidenceService collects it from fenced agent-evidence blocks in PR bodies/comments (travels with the PR), .agent-evidence.json in worktrees, or direct API; PR diff stats are computed server-side, never agent-claimed
  • Queue detail gets an Evidence card (badge row + review verdicts + media lightbox + data table) and a Refresh button
  • docs/agents/EVIDENCE_PROTOCOL.md teaches 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%)
  • reviewWorkflowService spawns each stage into an idle worktree, detects its GitHub verdict, records the outcome into evidence.reviews[], advances/blocks/stalls; run state persists across restarts
  • Per-role model routing: claude --model <alias> support added; codex uses existing model/reasoning flags

Bugs found & fixed (the "spammed out, untested" batch)

  • PR review auto-spawn never worked: startAgentWithConfig was called with {provider, skipPermissions} instead of {agentId, mode, flags} — failed validation every time; also wrong repo name in session ids, and prompt+\n in one write (bracketed paste, never submits)
  • Commander /clear (and all slash commands): forwarded as a single "/clear\r" paste chunk — now text + delayed \r
  • Codex unattended launches lacked --dangerously-bypass-approvals-and-sandbox → stalled on approvals
  • Plugin POST routes never saw req.body (no JSON parser on plugin routers)
  • Server launch was hardcoded hytopia start for every project type

Resurfacing the hidden process layer

  • UI Mode presets (Settings → UI Mode): one-click Simple ↔ Power/Process switch over the ~40 ui.visibility flags that had no UI at all; the Review Hub (📥 Review) header button is visible by default again (a hardcoded display:none was overriding its true flag)

Prompt-cache freshness

  • Feedback routing by cache age: warm sessions (<55 min) get review feedback inline; cold/missing sessions get a fresh fixer seeded with the review feedback + evidence handoff notes (also implements the previously-stubbed autoSpawnFixer); 🧊 cache-cold chip in Queue detail

Context-switch telemetry (local-only)

  • JSONL log of worktree focus / workspace switches / workflow mode / review timers with refocus-cost estimate and top thrash pairs, surfaced in the dashboard Telemetry overlay; nothing leaves the machine

Plugins made real

  • First working example plugin: youtube-transcript (Commander tools button or command → yt-dlp subtitles → plain-text transcript in ~/Downloads/transcripts/)
  • New post_route slot action (local routes only, optional prompted input), new commander.tools slot renderer, Settings → Plugins admin (failed plugins finally visible) + reload, client.slots finally documented in plugins/README.md

Play buttons revived

  • Launch commands resolve from the cascaded config (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

  • Multi-commander: seam analysis + recommended path in 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
  • Live smoke on a scratch port: review-workflow config API, visibility presets, plugin load (youtube-transcript loads, zero failures), client-surface slot, context-switch track/summary, evidence PUT→normalize→GET roundtrip, media path-traversal rejection, all new client files served
  • Manual: open Queue → select a PR → Evidence card + Review workflow block render; Settings → UI Mode / Plugins sections
  • Defaults unchanged for existing users: everything new is additive or behind flags except the Review button and the (previously broken) automation fixes

🤖 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 in config/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/reasoningFlag templates). 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)

CommanderManager runs N independent commander instances keyed by id — the primary keeps id commander so 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/remove routes (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):

  • HIGH shell-injection in custom-agent command construction and in the server-launch {{gameMode}}/{{commonFlags}} templating → shell-safe allowlist on all config-derived values; unsafe configs rejected at load / fall back to safe defaults.
  • HIGH evidence media endpoint was lexical-only → realpath-confines target under the worktree root (blocks symlink-out) and rejects non-regular files.
  • HIGH refresh() trusted an arbitrary request-body worktreePath → an explicit path is honored only if it resolves to a known workspace worktree.
  • MEDIUM review-workflow poll serialization (in-flight guard + CAS), stage-review attribution prefers the agent's marked review, prompt-cache routing treats missing timestamps as warm-if-live-session, and post_route is restricted to the plugin's own /api/plugins/<id>/ namespace (rejects //host).

All 679 unit tests pass (114 suites).

web3dev1337 and others added 23 commits July 15, 2026 07:00
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant