Readiness review of the coding-agents branch: sweep bound for agents, one door per wire module, stale docs, and the merge checklist
Summary
The coding-agents branch (#1862, #1863, #1866, #1868, #1870, #1871, #1872, plus the merges of main) reviewed as one feature against main, with fixes. Nothing here is a new feature. ./scripts/check.sh, pnpm --filter @luke/web test:store (local Postgres 17), and pnpm --filter @luke/web test:agent were green on the branch before the review and are green with these changes.
Dean waived ./scripts/verify.sh for this work; CI builds nothing for the Mac, so the two small renderer changes (Settings › Coding agents wording, the code pane's masking class) have no visual evidence.
What was wrong, and what this PR changes
A coding agent that ran past an hour would have been failed by the sweep. /api/maintenance/sweep settles every running turn started more than an hour ago as failed for abandonment, regardless of the conversation's kind. A coding agent is one turn for its whole run, and its sandbox lives up to 24 hours. Past the hour the agent would have read failed in its tab, the long-poll would have stopped holding, every later relay event would have been dropped as "repeated" against a terminal row, and Stop would have found nothing to stop while eve and the sandbox kept running. A coding agent's turn now runs under its own bound, 25 hours (TURN_ABANDON.CODING_AGENT_AFTER_MS), matching the sandbox's life plus the planning grace. Test: hosted-store-abandoned-turns.test.ts. The bound is a judgment call; see "Decisions for Dean" below.
Three wire modules were behind both doors. coding-agent-view, coding-agent-wire, and models-wire were subpath exports of @sidecar/hosted and re-exported from its barrel, against packages/CLAUDE.md's "nothing is behind both doors"; the desktop imported the door and the web the barrel. The barrel re-exports are gone and every importer reads the door. The server also redeclared MODEL_PROVIDER, ModelChoice, and CatalogModel in model-catalog.ts; it now imports them from the wire, with OfferedModel adding the context window the wire does not carry.
Dead code the branch left. CodingAgentMessagesAnswer (never used), CODING_AGENT_CLIENT_DEFAULTS and the heldReadTimeoutMs option nothing passed, GITHUB_REPOSITORY_BOUNDS (used in its own file only), and an identity map in applyMessagesPage. Folder-era comments in json-state-file.ts, plan-store.ts, desktop.css, and the api-callers.test.ts route table (which still named plans/commands/claim).
Settings › Coding agents. A models read that failed was indistinguishable from one still loading ("Reading the models…" forever), and a stored default whose model had left the catalog drew an empty effort row. The menu now says the models could not be read, and the effort row keeps the stored effort. Tests in coding-agents-page.test.ts.
Masking and logs. The code pane now carries ph-no-capture like the agent tab, and session-replay.ts's inventory lists both. The two eve-unreachable warnings in coding-agents-app.ts log describeUnreachable(failure) as every other caller does, rather than the error object (which is safe today only because Effect's HttpClientRequest.toJSON redacts the authorization header).
A double-click on Start sends one request (React flushes each click before the next); a test now says so.
Docs. docs/PLANNING.md, PRIVACY.md, apps/web/server/README.md, and the root README.md described the Mac folder flow, "Connect GitHub" and the old OAuth App's repo scope, GitHub's MCP endpoint, a test file that no longer exists, two Vercel services instead of three, GITHUB_CLIENT_ID instead of GITHUB_APP_CLIENT_ID, and the hour-long sweep. Each now describes the code. PRIVACY.md also says that the agent's sandbox has open internet and runs the repository's checks, that web_search runs on the provider's own search, that a deleted plan takes its agents' transcripts with it, and that Vercel Sandbox holds checkouts and snapshots.
Verified and left alone
- Migrations 0061–0064 are in order in
_journal.json, apply on Postgres 17, anddrizzle-schema.test.tsis green. vercel.json's/api/routes matchfunction-rewrites.ts(generated and tested); thecoderservice's/eve/coder/v1/*transform matchesEVE_MOUNT.CODER.- Status vocabulary
starting|running|completed|failed|cancelledagrees acrosscoding-agent-wire.ts,coder-host/status.ts,transcript.ts, and the renderer'sAGENT_STATUS_LABEL/FOLLOWED_STATUSES. effect-edges.jsonand AGENTS.md list the same sixapps/web/coder/edges and the coder eval shim; no other new file runs an Effect.- Secrets: installation tokens reach only the sandbox firewall (
cloneRepository, eve'sauthenticateGitHub), never the sandbox's env, a command, a log, or a fixture; provider keys are revealed only into the SDK clients; refusals that reach the client are enum words; fixtures carry only obviously synthetic tokens and a keypair generated at test time. - Deleting a plan stamps its agents' conversations deleted, so the 30-day purge takes them.
- The transcript long-poll's 20-second hold runs on the default function group with no explicit
maxDuration, exactly as the old planning claim's 20-second hold did onmain, so it is not a regression; see the checklist.
Merge checklist for Dean
Things that have to happen around merging coding-agents into main, in rough order.
Before the merge
- GitHub App registration. Confirm the Luke GitHub App has Metadata read, Contents read & write, Pull requests read & write, and Email addresses read; callback
https://tryluke.dev/api/auth/callback/github; webhooks off; "Request user authorization during installation" off. Set the Setup URL tohttps://tryluke.dev/api/github/installed(the branch added that route; it was to be set once the page existed). - Production environment variables (Vercel, Production and Preview):
GITHUB_APP_ID,GITHUB_APP_SLUG,GITHUB_APP_CLIENT_ID,GITHUB_APP_CLIENT_SECRET,GITHUB_APP_PRIVATE_KEY(PEM;\n-escaped is read),ANTHROPIC_API_KEY,OPENAI_API_KEY,BETTER_AUTH_SECRET. Sign-in now readsGITHUB_APP_CLIENT_ID/GITHUB_APP_CLIENT_SECRET;GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRETare read by nothing after the merge and can be removed once production is on the branch. Theeveandcoderservices receive the same project variables; both need the fiveGITHUB_APP_*andBETTER_AUTH_SECRET(the planning checkout mints its token throughGitHubApp). - Vercel project settings. Framework Preset must be Services (three services:
web,eve,coder). Thecoderservice's build runseve buildwithEVE_PUBLIC_ROUTE_PREFIX=/eve/coderand prewarms its sandbox snapshot, so the build needs Vercel Sandbox credentials (VERCEL_OIDC_TOKEN, orVERCEL_TOKEN+VERCEL_TEAM_ID+VERCEL_PROJECT_ID); a prewarm that fails stops the deployment. Thecoderprepareinstallsgh, pnpm, and Python on the image; it has never run on Vercel. - Function duration.
GET /api/agents/:id/messagesholds a request open for 20 seconds (client deadline 35 s) on the default function group, which declares nomaxDuration. This is the same shape as the deleted planning claim route onmain, so it has worked in production, but confirm the project's default function duration is at least 30 s (Fluid compute's default is 300 s). If the project is not on Fluid compute, giveagents/messagesits own group infunction-durations.ts. - Sweep bound. This PR sets a coding agent's abandonment bound to 25 hours. If you'd rather exclude coding agents from the sweep entirely (the plan says "no timeouts"), that is a one-line change in
abandoned-turns.ts.
The desktop release
- Release the desktop together with the merge.
planSummarySchema.repositoryis a required key (null allowed), so a desktop from before the branch refuses every plan the merged service answers; and the Mac command path (plan_command, claim/settle routes) is gone, so an old desktop's planning reads fail. A desktop built from the branch against the current production fails the other way (norepositorykey, no/api/models). Merge and release in one window, and expect every existing plan (13 at the time of the plan) to open on the repository chip until a repository is chosen. - Migration 0064 drops
plan_command; 0061–0063 addcoding_agent, the preferences columns, andplan.repository. All run onpnpm db:migratein the web service's build. Nothing is reversible without a hand-written down migration.
Nothing in this review could verify without real credentials
- Sign-in through the GitHub App against a deployed Preview or production, token refresh, the install Setup URL round-trip, and
GET /api/github/repositoriesagainst real installations. - A real checkout in a Vercel Sandbox, the firewall-delivered token,
gh/git pushfrom inside the sandbox, and thecoderpreparescript. - A coding agent on a real Anthropic or OpenAI model: the coder eval runs a scripted model that calls no tool, so no sandbox opens. Nothing has exercised eve's
defineDynamicmodel switching, effort mapping (anthropic.effort,openai.reasoningEffort), or provider-nativeweb_searchlive. - AI Gateway's public catalog shape (
reasoning_options,context_window,tags): the filter is tested against a fixture of it. - Vercel's snapshot retention ("thirty days after last use" in PRIVACY.md) has no source in the repository.
Known limitations recorded by the earlier PRs, plus ones found here
- Messaging an agent after Start and changing its model mid-run are not offered (the data allows both).
- No limit on concurrent agents, no spending cap, no daily allowance; eve's input-token budget is off; the sandbox has open internet.
- Branch rules (
luke/…, never push the default branch) are instructions only. - Stop while an agent is
starting(no turn row yet) is a no-op; the tab hides Stop until the turn starts, and an agent with no turn after 5 minutes readsfailed. - An agent's tab is unmounted when another tab is shown: switching back re-reads the transcript from the start, and the outstanding held read runs out on its own (up to 35 s). Hoisting per-agent transcript state into
useCodingAgentswould fix it; not done here. - The Start button's model menu, like Settings before this PR, shows "Reading the models…" on a failed catalog read until the menu is reopened (which reads again).
- eve's
authenticateGitHubfirewall rule is host-wide forgithub.com/api.github.com, not path-scoped like Luke's own clone rule; the token is cut to one repository, so GitHub bounds it. show_coderefuses.env*only; other secret-looking files in a developer's own repository (*.pem,.npmrc) can be shown orcated by the planning model. These are the developer's own repository contents, not Luke's credentials.- Sandbox output is stored and answered verbatim; a secret a repository itself contains would reach the transcript. No pattern scrub exists.
BRAIN_RUN_EVENT.CODE_SHOWN's body was reworked by the branch but nothing builds that event (shown code travels asTURN_EVENT_KIND.CODE_SHOWN); predates the branch.- Desktop seams without tests:
host-operator.ts's seven coding-agent methods, the side panel's agent tabs and "Agents · Try again" row. - AGENTS.md's "The scheduled sweep" paragraph still says "every turn still running an hour after it started"; it is one sentence out of date with this PR's bound and was out of scope to edit.
Test plan
-
./scripts/check.shexits 0 -
pnpm --filter @luke/web test:storeagainst Postgres 17 (migrations 0061–0064 applied) -
pnpm --filter @luke/web test:agent(brain-host 3/3 gates, coder 1/1) - New tests: a coding agent's turn survives the hourly sweep and is settled past 25 h; Settings' failed models read and out-of-catalog default; double-click on Start; the code pane's masking class
-
./scripts/verify.sh— waived by Dean; CI could not verify the two renderer changes
🤖 Generated with Claude Code