B4. The Mac picks a GitHub repository for each plan, and the Mac folder path is deleted
B4. The Mac picks a GitHub repository for each plan, and the Mac folder path is deleted
Stacked on coding-agents; B3 (#1868) is merged beneath it.
The repository chip
HostedPlanClientgainsrepositories()(GET /api/github/repositories) andsetRepository()(PATCH /api/plans/{id}with{repository}), andcreate()now answers the service'srepository-not-reachableandgithub-sign-in-requiredrefusals as the failures they are, besideunanswered.- The host's planning composer answers two new Gateway methods,
planning.repositoriesandplanning.setRepository, andplanning.starttakes the plan's repository. A plan given its repository is drawn on the list's row and the open document as the service answered, nothing read again. - The desktop's new-plan composer's folder chip is now a repository chip (
renderer/planning/repository-chip.tsx): the GitHub mark andowner/name, starting on the repository the newest plan was about. Its menu lists the recent repositories, thenSearch all repositories…(filters the service's list), thenChoose which repositories Luke can see ↗. With the App installed nowhere, the chip readsInstall Luke on GitHub ↗and opens the installation page directly. Arrows, Escape, and focus leaving behave as the folder menu did. Refusals show under the card. - The list is read when the chip mounts and again whenever the window takes focus, which is how it picks up an App installed in the browser; nothing polls.
- A plan with no repository (the 13 old folder plans among them) opens on the same chip, under the title in the toolbar; a pick is saved through the PATCH and a refusal said beside the chip. A plan can be used without a repository.
- The plan menu's
Reveal in Finder/Change folder…becomeChange repository…(opens the chip's menu, opening the plan first where it is not the open one) andOpen on GitHub(https://github.com/<owner>/<name>, through agithub.openact whose schema admits GitHub's addresses alone). The ⌥⌘R shortcut and its menu item are gone. The sidebar's rows and the plan header showowner/repository.
The Mac folder path, deleted
plan-folders.json and planFoldersFile, PlanningView.folders, planning.setFolder, the planning.chooseFolder / planning.setFolder / planning.revealFolder acts, the system folder dialog and shell.showItemInFolder in register-desktop-ipc.ts, folderLine / folderName / recentFolders, the folder glyphs, and their tests.
The Code tab now reads from the sandbox (agreed with the orchestrator)
The Code tab was fed by show_code, whose lines the Mac read from the plan's folder on disk, so it had to move with the folder:
show_code(apps/web/server/hosted/show-code.ts) now reads the lines itself, from the planning session's checkout of the plan's repository, through the shell's helpers (checkedOutSandbox,runInSandbox): the file probed (resolved inside the checkout root, a regular file, under 1 MiB, text) and then a window of at most 200 lines around the lines pointed at, each cut to 400 characters (SHOWN_CODE_BOUNDS). The path reaches the scripts as a variable, never as shell text; a.envfile is refused before any read.- The lines are the call's answer, so they are journaled with the call;
projectTurnEventstellscode_shownonce the call has answered, carrying theShownCode(ref,repository,firstLine,lineCount,lines), and theplan.codeframe carries it to the Mac. A missing, binary, secret, or unreachable file answersrejectedwith why, and nothing goes on screen: nothing unread is described as shown. repository-shell.ts's refusal reasons are now worded once (REPOSITORY_REFUSAL) with each tool's verb ahead of them (Not run:/Not shown:);REPOSITORY_SHELL_REFUSAL's values are unchanged.- The Mac's
plan-code.tsis now a pure highlighter over the lines that arrived; the disk read,CODE_UNREADABLE, and the pane's unreadable states are gone. The pane names the repository beside the path. - Goldens:
live-contract-planCodeFrameSchema.jsonre-recorded for the new frame (codein place ofref), andplan-wire-shownCodeSchema.jsonadded.
Docs
docs/PLANNING.md (New plan, Header with the plan menu, Side panel, How the planning model reads code, the journey, the acts row) and PRIVACY.md's "Code on screen during a planning call" (the code now comes from the service's checkout of the GitHub repository, not from the Mac's disk).
Tests
Hermetic throughout: the client methods (plan-client.test.ts), the host's repository methods and the sent code (compose-planning.test.ts, plan-code.test.ts), the acts (planning-acts.test.ts, acts.test.ts), the chip's states — installed, not installed, empty, refusal, search, keyboard — and the composer (new-plan-form.test.ts), the null-repository plan and the menu actions (plan-actions.test.ts, use-plans-tab.test.ts), the server read from a sandbox double (show-code.test.ts), the projection (hosted-turn-events.test.ts), and the voice package's frames.
./scripts/check.sh exits 0. CI could not verify the Mac: verify.sh was waived for this rollout.
Follow-ups, not in this PR
packages/hosted/CLAUDE.md's "A turn's events are a projection" still says the queued question is "the one call whose words are told"; shown code's lines are now told too. Left for Dean, since the agent guides are not edited without approval.
🤖 Generated with Claude Code