You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(review): remote review works end-to-end via HTTP+heartbeat (#213)
* fix(review): remote review works end-to-end via HTTP+heartbeat
Remote review was broken top-to-bottom:
- `HAIKU_REVIEW_SITE_URL=` empty string fell through `config.ts:str()`'s
`??` check and produced `review.siteUrl = ""`, so `buildReviewUrl`
generated tokens starting at `/review/#…` instead of
`https://haikumethod.ai/review/#…`. `str()` now treats empty as
"use default".
- `withE2E` crashed on any 204/205/304 response with "Invalid response
status code 204" from Node's Response constructor. E2E wrapping now
short-circuits for null-body statuses.
- CORS only allowed `Content-Type` in request headers and nothing in
response headers. The SPA's `bypass-tunnel-reminder` request header
tripped preflight rejection, and the browser couldn't read
`X-E2E-Encrypted` / `X-Original-Content-Type` from responses. Added
`HEAD`, `bypass-tunnel-reminder` to allowed headers, and the two E2E
headers to `Access-Control-Expose-Headers`.
- Custom-subdomain requests to loca.lt hang indefinitely when the
upstream reservation service is unhappy. Dropped the per-process
subdomain entirely — stable subdomains aren't worth fighting for.
- WebSocket presence detection never worked through loca.lt (browser
UA sniffing serves the interstitial on WS upgrade, and browsers can't
set custom headers on `new WebSocket()`). Replaced WS with a HEAD
heartbeat every 10s from the SPA to `/api/session/:id/heartbeat`. A
per-session sweep on the server marks sessions as `presenceLost` when
no heartbeat for 25s and wakes the waiting `_openReviewAndWait` so
it can reopen the browser without burning a retry attempt. Simpler
client code, no tunnel-specific workarounds, presence detection
within ~20s of tab close.
- Review SPA now hides site header/footer and renders full-pane with
zero chrome. Scoped via a `data-haiku-review` attribute on `<html>`
that's torn down on unmount, so no impact on any other route.
Also adds `scripts/smoke-remote-review.ts` for end-to-end local testing
of the review transport without driving a full intent flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(review): StatusBadge crashes when status is undefined
Frontmatter fields like `status` / `discipline` / `stage` are optional
on some units and intents. The SPA was calling `.toLowerCase()` on
whatever came through, crashing the whole review render with
"Cannot read properties of undefined (reading 'toLowerCase')" on any
intent/unit where a referenced field was missing.
StatusBadge now treats undefined/null/empty as "unknown" and keeps
rendering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style(http): biome formatting for multi-line condition
CI's biome check wanted the 204/205/304 guard broken across lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(review): address code review comments
- sessions.ts: evictSessions now clears heartbeat state on both TTL
and cap-based eviction, and sweepPresence cleans up presenceLost
entries when the backing session is gone.
- http.ts: withE2E skips encryption for null-body responses (HEAD
endpoints), removing redundant crypto + allocation on every
heartbeat. OPTIONS preflight no longer double-wraps withCors since
the network-layer middleware covers it.
- server.ts: _openReviewAndWait's timeout throw path now calls
clearHeartbeat alongside clearE2EKey.
- useReviewSession.ts: heartbeat fetch has an 8s AbortSignal timeout
so a hung tunnel can't stack up concurrent requests.
`isConnected` starts as `null` (not-yet-checked) so the
"Reconnecting…" banner doesn't flash on first render while the
initial beat is in flight.
- ReviewShell.tsx / ReviewSidebar.tsx: only show the reconnecting
indicator when `isConnected === false`, so the `null` initial
state renders as "connected" until proven otherwise.
- smoke-remote-review.ts: SIGINT/SIGTERM handlers close the tunnel
before exit so Ctrl-C doesn't leave dangling resources.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 commit comments