Skip to content

feat: every agent run is logged with what it spent, and the daemon waits out the Claude session limit - #97

Merged
fiddur merged 2 commits into
developfrom
feat/93-run-log
Sep 7, 2026
Merged

feat: every agent run is logged with what it spent, and the daemon waits out the Claude session limit#97
fiddur merged 2 commits into
developfrom
feat/93-run-log

Conversation

@fiddur

@fiddur fiddur commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Item (2) of #93: the reviewer can see what the inbox costs, and a session limit no longer eats a pull request's retries.

What

  • inbox_runs in the inbox store: pull request, head, phase (prepare | validate | answer), model, start and end, duration, turns, cost, tokens, outcome (prepared | skipped | validated | answered | failed | timeout | rate-limited), note. recordRun, runs({ since, prId }), runTotals(since), latestRunsFor(prId, headSha).
  • preparePr now reports run: { startedAt, endedAt, stats } on every outcome and tags a failure with what it failed at (worktree | timeout | budget | rate-limit | agent | bundle); the tick logs one run per prepare, and skips the one where no agent ran. Live answers are logged as answer runs through a recordRun handed to realAttendantDeps.
  • rateLimitOf(text, now) reads "You've hit your session limit · resets 2pm (Europe/Stockholm)" into the next instant that clock shows, in the zone named or the reviewer's own; unparseable means no time. A rate-limited prepare re-queues the row with its reason, counts no attempt, and pauses preparing until then (or 30 min). Polling and reconciling carry on.
  • diffity inbox runs [--json] [--since <days>]; the cost on a prepared card (· 8 min · $1.20, each run on hover) and totals plus the pause in the page footer, /api/inbox and inbox status --json.
  • README: the run log, the cost on the card, the session-limit pause.

Why

The plan measured the inbox by hand out of session logs, and three pull requests sat failed on "You've hit your session limit". Both are now the daemon's own bookkeeping.

How verified

npm test green (cli 62 files / 498 tests, ui 433, +31 tests here), npm run typecheck clean, and inbox runs / inbox status eyeballed against a seeded store.

One deviation from the plan

The plan has runDaemon keep pausedUntil in memory; it is kept in the store instead (inbox_state), so a restart does not spend a run rediscovering the limit and inbox status can print it without the daemon. The daemon still exposes it through DaemonStatus.

Items 3–5 of #93 (CI fields, the validate pass, the compare script) are untouched.

Part of #93

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bwp5QefjsjMFeq6CK6cT6w

fiddur and others added 2 commits September 7, 2026 16:11
…its out the Claude session limit (#93)

The inbox keeps an `inbox_runs` row per agent run: the pull request and head, the pass
(`prepare` or `answer`), the models it used, its duration, turns, cost and tokens, and how it
ended. `diffity inbox runs [--json] [--since <days>]` prints that log with totals; a prepared
card carries its own share ("· 8 min · $1.20", each run on hover) and the page's footer keeps
the day's and the week's.

A run that ends on the reviewer's Claude session limit no longer costs the pull request an
attempt: the row goes back to queued as "waiting: Claude session limit until 14:00", and no
further preparation starts until the reset time the message named — or half an hour ahead when
it named none. Polling and reconciling carry on meanwhile, and the pause is kept with the inbox
so a restart does not spend a run rediscovering the limit.

Part of #93.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bwp5QefjsjMFeq6CK6cT6w
…r, and a reset can be a duration

Three findings from the review of #97:

- The reconcile clears a queued row's reason every tick, so a paused inbox showed a bare "queued"
  and only the footer said why. The tick now writes "waiting: preparing paused until 14:00" onto
  every row it holds back, and takes the moment itself from the deps rather than a boolean.
- An answer killed through the abort signal — the daemon stopping, the reader leaving — closes its
  child as cleanly as one that answered, and was logged as `answered`. It is a `failed` run with
  "stopped before it answered".
- `resets` now takes an optional "at", and a duration ("resets in 3 hours", "resets in 1 hour
  30 minutes") counted from the moment the run ended.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bwp5QefjsjMFeq6CK6cT6w
@fiddur
fiddur marked this pull request as ready for review September 7, 2026 17:50
@fiddur
fiddur merged commit 58914b7 into develop Sep 7, 2026
2 checks passed
@fiddur
fiddur deleted the feat/93-run-log branch September 7, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant