diff options
| author | Adam Malczewski <[email protected]> | 2026-06-02 14:51:16 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-02 14:51:16 +0900 |
| commit | 40b0b6a23a5cbd494f9956315c2e424d16edb282 (patch) | |
| tree | 03bfb2833cc0c5f5ffdf6d7687a921ee933dd20e | |
| parent | d022877e3a4c954adafce3894f70c176dee76433 (diff) | |
| download | dispatch-40b0b6a23a5cbd494f9956315c2e424d16edb282.tar.gz dispatch-40b0b6a23a5cbd494f9956315c2e424d16edb282.zip | |
docs: handoff for cr/claude-reset-fix (wake probe body fix)
| -rw-r--r-- | HANDOFF.md | 118 |
1 files changed, 64 insertions, 54 deletions
@@ -1,63 +1,73 @@ -# Handoff — sb/status-bar +# Handoff — cr/claude-reset-fix -Add a status bar beneath the chat input that houses the send button and shows -generation status + context-window usage. +## Objective +Make the Claude Wake Schedule ("Claude reset") system work reliably end to end. + +## Root cause found +The two issues named in the task brief (toggle endpoint ignoring client intent; +server-side request reordering desyncing the UI) were **already fixed** in this +worktree from prior rounds — the toggle endpoint requires an explicit +`action: 'on' | 'off'`, and the frontend serializes mutations behind a global +`pendingHour` lock (verified: tests + code present, suite green). + +The *actual* live failure (reproduced by the user: `✗ Last wake 4 min ago — +failed`, blank reason, then `Retrying (6 left…)`) was in the **wake probe +itself**, not the scheduler or UI: + +`wakeAllClaudeAccounts()` POSTed a bare body +`{ model, max_tokens, messages: [{role:"user",content:"hi"}] }` with **no +`system[]`**. These accounts are OAuth (Claude Pro/Max) subscriptions, and +Anthropic validates `system[]` on Claude-Code-billed OAuth requests — it +rejects (401/403) any request whose system block lacks the verbatim Claude Code +identity string. So **every** scheduled wake and the manual "Wake now" button +failed. The old code recorded only `ok: res.ok` with no error text, surfacing as +a blank "— failed" that then burned the 6×5-min retry budget. ## Files changed -- `packages/frontend/src/lib/components/ChatInput.svelte` — restructured into - **two stacked bars** (wrapped in `flex flex-col`): - - **Top bar:** the existing auto-resizing textarea + a single, fixed-width - (`w-20`) send/stop button that morphs in place so the layout never shifts. - Three states: - - not generating → `btn-primary` **Send**, disabled when the box is empty - (unchanged look). - - generating + empty box → **Stop** (`btn-error btn-outline`, spinner + - "Stop"), calls `tabStore.stopGeneration(tabId)`. - - generating + text in box → enabled **Send** (queues the message via - `tabStore.sendMessage`). - - **Bottom bar:** agent status icon on the left (✓ idle / spinner running / - ✗ error), a context-window fill `progress` bar filling the middle, and a - compact token count + percent on the right (e.g. `12.3k / 200k · 6.1%`). - When the model's max context is unknown the bar renders inert/disabled - (`opacity-40`, no value) and the right side shows the bare current token - count with no percent. Before the first response it reads `— tokens`. -- `packages/frontend/src/App.svelte` — pass the already-computed `contextLimit` - into `<ChatInput {contextLimit} />` (same value handed to the sidebar). +- **`packages/core/src/credentials/claude.ts`** — new pure + `buildWakeProbeBody(model)` that mirrors a genuine Claude Code request: + `system: [{billing-header}, {identity}]` + `messages:[{role:"user",content:"hi"}]`, + `max_tokens: 16`. Reuses existing `buildBillingHeaderValue` + `SYSTEM_IDENTITY`. +- **`packages/core/src/credentials/index.ts`** — export `buildWakeProbeBody` + (reachable as `@dispatch/core`). +- **`packages/api/src/routes/models.ts`** — `wakeAllClaudeAccounts` now sends + `buildWakeProbeBody(WAKE_PROBE_MODEL)` plus the CLI session headers + (`X-Claude-Code-Session-Id`, `x-client-request-id`), and on `!res.ok` records + `HTTP <status>: <message>` via new `describeFailedResponse(res)` so the panel + never shows a bare "failed" again and breakage stays debuggable. +- **`packages/core/tests/credentials/wake-probe.test.ts`** — new: 4 unit tests + asserting the probe body shape (model/tokens, billing-first/identity-second + system[], single "hi" user message, determinism). ## Public surface changed -- `ChatInput.svelte` gained one optional prop: `contextLimit?: number | null` - (defaults to `null`). No other exported API/type changes. -- Reuses the shared `computeContextUsage()` helper (`lib/context-window.ts`) and - the same fill-color thresholds (calm→warning→danger) as the Context Window - sidebar panel, so the two displays always agree. Compact `k`/`M` token - formatting is local to `ChatInput` (the sidebar keeps full `toLocaleString`). - -## Design decisions (agreed with requester) -- Two stacked bars; send button kept (not dropped) for discoverability. -- Send and Stop are the **same button** at a fixed width — no layout jump. -- Bottom bar: status icon left; context number + percent right; progress bar - fills the remaining width; disabled/inert bar when the model has no known max. -- Token format: compact (`12.3k`) for the slim bar. - -## Verification status — PASS -- `bun run check` (biome): **PASS** — "Checked 163 files… No fixes applied." -- `bun run test` (vitest): **PASS** — 35 files, 552 tests. -- `bun run --cwd packages/frontend typecheck` (svelte-check): **PASS** — 0 - errors, 0 warnings. -- Re-verified all three after `git merge --no-edit dev` — still all-green. -- Note: `bun install` was required first; deps were not present in the worktree. +- New export `@dispatch/core` → `buildWakeProbeBody(model: string)`. +- No API route signatures changed. `POST /models/wake`, `POST + /models/wake-schedule/toggle`, `GET /models/wake-schedule` request/response + shapes are unchanged. The only externally visible behavior change: failed + wakes now carry a descriptive `error` string (`HTTP <status>: <message>`) + instead of an empty one, which the panel already renders. +- DB schema: unchanged. + +## Verification +- `bun run check` (biome) → clean, 164 files. +- `bun run test` (vitest) → **568 passed** (post-merge with dev; +4 from this + branch's new probe-body tests). +- `tsc -p packages/core` and `tsc -p packages/api` → exit 0. +- `svelte-check` (frontend) → 0 errors, 0 warnings. ## Published -- Yes. Feature commit `2756730`, merged `dev` down (`f0207a7`, clean merge — - picked up another agent's CacheRatePanel/KeyUsage changes + their HANDOFF.md), - pushed fast-forward `3f0bfe7..f0207a7 → dev`. -- This HANDOFF.md was rewritten from the incoming `m1/minor-fixes` handoff (that - content remains preserved in git history on its own merge). +Yes. Merged `dev` down into `cr/claude-reset-fix` (clean merge, no conflicts), +re-verified all-green, then `git push . HEAD:dev` (fast-forward, accepted). ## Assumptions / known gaps -- User visually confirmed the UI before merge ("sweet, merge it in"). -- No component-render tests were added for the Svelte markup, consistent with - the existing repo (these panels have no render tests). Logic is exercised - indirectly via the unchanged `computeContextUsage` unit tests. -- Context usage reflects the most recent turn (`last.inputTokens + - last.outputTokens`) — identical semantics to the sidebar panel. +- **Live API not testable from the agent sandbox.** The fix is verified offline + (body shape + headers match the real `transformClaudeOAuthBody` provider path + and unit tests). The actual "200 OK wake" requires real OAuth credentials and + was deferred to the user-test gate. If a probe still fails, the panel now shows + a concrete `HTTP <status>: <message>` reason to diagnose from. +- **Probe model** is hardcoded `claude-3-5-haiku-20241022` (`WAKE_PROBE_MODEL` + in `models.ts`). Cheap/small by design — only needs to register activity. +- **Deferred (unchanged design trade-offs, pre-existing):** DST drift on the + 24h advance; no background snapshot polling (UI may show stale "Retrying…" + until next user interaction); retry storm re-probes already-succeeded accounts. + None of these block reliable wakes; the probe fix addresses the live failure. |
