Skip to content

Fix review findings and restructure the gateway and harness modules - #43

Merged
greenpolo merged 46 commits into
mainfrom
fix/review-2026-10
Oct 5, 2026
Merged

greenpolo merged 46 commits into
mainfrom
fix/review-2026-10

Conversation

@greenpolo

@greenpolo greenpolo commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Fixes from the October 2026 full-codebase review (Mods usage, native-behavior alteration, gateway, providers), a regression-review pass on top of them, and a cleanup refactor of the gateway and harness structure. 46 commits, 155 files. CHANGELOG.md has 29 Unreleased entries with the details.

Behavior fixes

  • Native Claude sessions are left alone until a harness runs. Display tools register lazily per session instead of ~70 at session start. The gateway is transparent to native requests, and Claude's own auto-mode classifier checks no longer get "Missing or ambiguous pending review action" in mixed sessions.
  • Provider workers. Custom agents pinned to multi/openai or multi/zen, and workers that inherit a provider model, are now registered with the gateway and attributed through tool.call.
  • Session lifecycle. State is forgotten on session end, not on client detach. Idle records are evicted across Mods, permissions, Antigravity, and Grok.
  • Gateway security. Requests with a non-loopback Host are refused (DNS rebinding). Mods control-plane calls need a per-session mod key. Unknown multi/ provider prefixes are reviewed.
  • Network. Proxies, custom base URLs, and every Anthropic path are honored, and NO_PROXY gains the loopback names. Slow non-streamed replies are kept alive, and a failure after the keepalive is reported as an assistant message.
  • Nested claude. Nested runs stay native, and nested multi launches forward to the user's upstream.
  • Providers. Antigravity permissions are an allowlist in every mode and its hook files are protected. Grok fails closed on its toolset and on non-end_turn stops. Cursor recovers its state across restarts. Zen Chat blocks are sequential, and OpenAI bills stop-sequence replies.
  • Windows and state files. .cmd launchers refuse cmd.exe metacharacters and unsafe prompts. Process trees stop gracefully. Writes are atomic with fsync. Taking over a stale state lock tolerates hostname changes. The nested shim works in Git Bash.
  • Harness reports in auto mode. Claude Code 2.1.288+ delivers a background worker's result only through a SubagentHandback call in auto mode. The gateway now issues that call for Cursor, Antigravity, and Grok workers. It answers bounce and result-only requests itself, and strips the handback from history on resume. The report's review is left to Claude Code's own classifier.
  • GPT-6.1 Sol on OpenAI. Codex now serves gpt-6.1-sol on ChatGPT plans, so the OpenAI Sol row runs it in place of gpt-6-sol.
  • Concurrent launches. The Antigravity hook install waits for another launch's lock instead of failing with "State file is locked by another gateway".

Refactor

  • The Mods hooks were reworked around $.state, $.clock, and a single gateway client (hooks/gateway.ts).
  • Antigravity and Grok share a CLI harness base (harness-cli.ts) and a failure classifier (harness-failure.ts).
  • Responses translation and the SSE reader moved into multi-core (gateway/responses.ts).
  • Provider request construction moved into the provider plugins (gateway-request.ts), with the contract in provider-request.ts.
  • isRecord and nativeSpelling were consolidated, and the responses shim was removed.
  • AGENTS.md, ARCHITECTURE.md, and docs were updated to match the new modules.

Verification

  • npm run check: banner, Biome, Knip, tsc, 645 unit tests all pass, with no DEP0190 warnings (Node 24.18).
  • npm run test:e2e against the pinned Claude Code 2.1.283: 105 pass, 7 known todos. The first CI run caught a regression: an unreadable classifier request passed to Anthropic while an OpenAI worker's action awaited review. It now fails closed again. The run also caught three stale fixtures. Windows e2e then caught that npm's claude.cmd shim for claude.exe went through cmd.exe, where the new metacharacter refusal broke every harness worker. Such shims now run the binary directly.
  • npm run test:mod: 95/95 hook tests pass.
  • Live E2E (Claude Code 2.1.288/2.1.289, claude-multi in tmux):
    • Sonnet in auto mode ran parallel OpenAI, Cursor, Zen, and Antigravity workers on a small repo, and npm test passed. After the handback fix, both harness reports arrived with no re-prompts.
    • GPT-6 Luna as the main loop drove an Explore subagent on Haiku.
    • Sonnet in acceptEdits ran an Antigravity worker.
    • Three overlapping launches all started.

Not yet verified live

  • Grok workers: the handback path is shared, but there was no Grok Build login to test it.
  • Windows on a real host (NashPC): the Git Bash nested shim, graceful taskkill, and the agy hook guard. CI covers the cmd.exe refusal and the npm shim in Windows unit and e2e jobs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6

greenpolo and others added 30 commits October 1, 2026 16:34
Add isCmdSafeArgument, argvSafe and promptArgvSafe so providers can send
prompt text over stdin when a non-shim .cmd/.bat would re-parse it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
… total output

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
… per session

The policy route can hold its reply until discovery ends (bounded), so a hook
asks once instead of polling. Unconsumed policy jobs expire after a minute, and
the policy and snapshot tables evict their least recently used idle session
instead of refusing new ones until restart. Display tools are acknowledged per
session, and ToolSearch tool_reference blocks naming them are stripped from
forwarded history.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…on uninstall

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…nge cap; document Windows behavior

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Grok and Antigravity send prompts through a file or stdin when a non-shim
.cmd launcher cannot carry them, and classify failures with the shared
classifier. Codex app-server flag no longer embeds quotes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Session cleanup moves from session.detach (any client leaving the roster) to
session.end, which covers /clear, exit and resume; detach is no longer hooked.
Tool-call reviewer attribution moves to tool.call, which carries the loop's
agentId, so provider workers in a Claude session are attributed with the mode
from the prompt snapshot. Agent types are classified native only on a Claude
model, built-ins locally, and a definition pinned to a provider model keeps
gateway registration; the Agent description names provider models only while a
provider worker is offered. Display tools register lazily (at a harness
prompt, before a harness worker spawns, before a harness step) and
re-register after a /clear. The streamed Agent input rewrite is gone.

Hook state (policy snapshot, agent and spawn models, offer classification,
display tools, usage panes) lives in $state and survives a hot reload, and
drawings subscribe to it instead of invalidating. Every gateway call goes
through one client with an explicit method, a byte bound and a $clock
deadline; the status poll is a $clock timer cancelled at turn.complete and
session.end; the policy wait is one held request. An unchanged Claude prompt
snapshot is not reposted, Claude steps send no telemetry, and the usage pane
honours isPlaced. Types build against .claude-plugin/types and include the
hook tests; test:mod forces the Mods rollout flag locally.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ad record()

A main-loop compaction boundary is no longer written into the main
snapshot, so workers are no longer resolved as toolless compaction turns,
and a failed or cancelled compaction clears it. The permission context
tables evict their oldest entry at the cap instead of refusing new work.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ncher

Nested claude runs get the start environment back through a PATH shim and
the bootstrap; a caller ANTHROPIC_BASE_URL and proxy are honored; discovery
runs in parallel; a Claude that exits early ends the launch with its exit
code; a failing plugin listing no longer aborts it; agent view is scoped
through the session settings alone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Claude's own classifier goes to Anthropic unless a provider action matches,
non-streamed provider replies keep their connection alive, raw routes cover
every non-Multi path, bodyless requests stay bodyless, encodings are kept,
observation is bounded per line, failures leave a diagnostic, the mod
control plane requires a per-session key once the mod holds one, and the
dead agent catalog rewriting is removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
A worktree's node_modules symlink was committed because node_modules/ only
matches directories.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Drop the early-access framing (opt-in variable, /plugin-types as the type
source, $store), record the engine's static rules for hooks files, explain why
test:mod forces the rollout flag locally, and update the API map for the
changed hooks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Zen and OpenAI now share gateway/responses.ts; forAnthropic takes the
provider signature prefixes to strip instead of hard-coding Zen's.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
greenpolo and others added 14 commits October 1, 2026 17:19
harness-cli.ts owns handle, exchange keying, leases, eviction, close and the
bounded wait (Cursor reuses it with its own timeout). Providers supply model
selection, key fingerprint, error type and the turn.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ng helpers

OpenAI and Zen request construction, endpoints, model lookup and auth help move
behind a PreparedRequest interface in their provider folders. One providerOf table
replaces the duplicated prefix routing. The shared settings check and permission
merge get neutral names in core. handleModRoute takes a context object, and
cancelling a compaction now also clears the main loop's permission boundary.
isRecord and the bounded-map eviction helpers each have one definition.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…s shim, harden mod keys

The gateway imports the shared Responses translation directly and gets each
provider's signature prefixes from its own request module. Detaching a session now
drops its mod key, and cap eviction removes unconfirmed entries before confirmed ones
so a token holder cannot evict a real session's key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…launch, Windows shim, proxy, keepalive failure

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Two claude-multi launches started together both reinstall the Antigravity
hook under lockStateFile, which refused a live owner at once. The hook
install now waits up to 5 s (new waitMs option; session files keep failing
fast), and a marker released between lstat and read is retried.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
The gateway did not review the report, so it reports the verdict as
unavailable rather than not_flagged; Claude Code's classifier decides.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
Copilot AI balanced review requested due to automatic review settings October 5, 2026 03:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

greenpolo and others added 2 commits October 5, 2026 00:06
- An unreadable classifier request (Claude 2.1.283 omits the severity tag)
  went to Anthropic even while an OpenAI worker's action awaited review; it
  fails closed again in that case.
- The Grok routing fixture ended with stopReason 'stop'; real Grok ends with
  'end_turn', which the fail-closed check now requires.
- The Grok [1m] wire drives a session of its own and follows the mod-key
  protocol, since the Claude session's key now guards mod writes.
- The Windows install test passes a settings path, not quoted JSON, to its
  .cmd fake Claude, which the launcher now correctly refuses.
- The e2e rejecting proxy ignores client resets of refused tunnels, which
  crashed local runs.
- Codex now serves gpt-6.1-sol on ChatGPT plans, so the OpenAI Sol row runs it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
npm links Claude Code's bin/claude.exe through claude.cmd. The shim
resolver only unwrapped .js targets, so the gateway's plugin-permission
probe ran it through cmd.exe and the metacharacter refusal rejected its
inline --settings JSON: every harness worker failed on Windows with an
npm-installed Claude. An .exe shim target now runs directly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
@greenpolo
greenpolo merged commit 3dcb057 into main Oct 5, 2026
12 checks passed
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.

2 participants