feat: the inbox page — one click from a prepared review to a live diffity session - #83
Merged
Conversation
…ne click Serves a self-contained dashboard at the daemon's port: it polls /api/inbox and shows what's ready (smallest first), what's preparing, and what was skipped or failed. Clicking a ready pull request hits /open/<id>, which brings its prepared review up as a live diffity session — a server over the kept worktree, diffing against the pull request's base, with the prepared findings imported — and redirects the browser to it. A healthy session already serving that worktree is reused; the import is idempotent and best-effort, so a stale anchor never blocks opening the diff. Part of #79 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PkYQzbsnMihHesafWvXKs
…ce is guarded The whole request handler is wrapped, and a bad percent-escape in /open answers 400 instead of throwing uncaught and taking the long-running daemon down. Every request must carry this loopback server's own Host, so a page that rebinds its hostname to 127.0.0.1 cannot read the reviewer's pull requests; a state-changing /open refuses a cross-site fetch. An import that fails on open is now logged with its reason rather than swallowed. The real ensureServer path gains tests — the hash it looks a server up by matches what diffity registers, and the start times out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PkYQzbsnMihHesafWvXKs
The page's links are built from the host the reader actually reached it by, so opening from 127.0.0.1 is not a cross-site click against a localhost link. resolveOpen moves inside the open handler's try, so a store failure answers 500 rather than leaving the request to hang. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PkYQzbsnMihHesafWvXKs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #79, the last of the three inbox PRs (after #80 bundles and #82 the daemon). This is the pinned-tab surface and the open flow.
What it adds
The daemon's server now serves more than JSON:
GET /— a self-contained dashboard (no build step, no external requests; theme-aware): polls/api/inboxevery few seconds and renders Ready to review (smallest diff first, each a one-click link), Preparing, and Other (skipped/failed with the reason).GET /open/:id— brings a prepared review up as a live session: reuses or starts a diffity server over the kept worktree, diffing against the pull request's base (read from the bundle), imports the prepared findings, and 302-redirects the browser to the session. The session runs in your own diffity (shows indiffity list), so from there it behaves like any review you opened yourself. Import is idempotent and best-effort — a stale anchor never blocks opening the diff.Shape
inbox/open.ts— pureresolveOpenguard (must be prepared/stale with a worktree + bundle).inbox/open-session.ts—openPreparedSessionorchestration with its side effects injected; the real deps reuse a healthy instance (findInstanceForRepo, realpath-hashed) or start one, and import via the CLI.inbox/page.ts— the HTML.inbox/daemon.ts— the two new routes;startInboxServer/runDaemontake the open deps (injectable for tests).Tests
inbox-open.test.ts: the resolve guard (accept / 404 / 409),baseRefOf(reads/refuses),openPreparedSession(ensures at base then imports, and still opens when import throws), and the routes against a real server (page HTML,/api/inbox,/open302 to the session URL, 409 for a not-ready PR). Smoke-tested the whole chain live: daemon prepared a PR, the page listed it,/openredirected to a real diffity session started at the bundle's base commit with the prepared finding imported (idempotent on a second import).