The headline was cost visibility per backlog phase. I started recording per-phase token usage and active time, so each phase of an item now carries how many tokens it burned and how long it was actively running. That reshaped a good chunk of the backlog data layer — loadRelations and rowToItem were pulling everything through one tangled query, so I split the relation lookups into a dedicated relationQueries module and added the token/time fields to the item types. On the web side it surfaces as a PhaseUsageLine under each PhaseCard and an ItemUsageTotal rolling it up for the whole item, with formatTokens and formatActiveTime doing the human-readable formatting.

With that data flowing, I used it to seed the session timer. When a run reuses a session, seedRunningMsFromUsage and loadPhaseActiveMs pull the accumulated active time out of the usage records so the timer picks up where the phase left off rather than resetting to zero — the elapsed time now reflects real work done across resumes, not just the current sitting. Smaller but handy alongside it: a new assist backlog set-status command for setting an item’s status directly, which I’d been wanting whenever an item ended up in the wrong state and I just wanted to nudge it without going through a phase transition.

The rest of this release cycle was about making sessions behave the way I expect. The one I’d been circling longest: stopping a session from continuing past a point where it’s asked me something and I’m not there to answer. I first wrote it as a hard rule in CLAUDE.md — never continue after “No response after 60s — continued without an answer” — but a prose warning is exactly the kind of thing a session can rationalise its way past. So I replaced it with the real fix: setting CLAUDE_AFK_TIMEOUT_MS to max int in settings.json, which just stops the AFK timeout from ever firing. If a session asks a question, it waits for me, full stop.

Two more status fixes rounded it out. The review button on a session card was opening the wrong PR, so I pulled the target-PR resolution into a reviewTargetPr helper (with tests) and tightened prStatus and createCachedGhJson so the button links to the PR that actually belongs to the session’s branch. And resuming a waiting session and typing a new prompt should flip the card to running immediately — it was staying waiting until some later signal caught up — so I added a narrow watchPromptSubmit watcher hooked into writeToSession: if a restored session is waiting and the keystrokes include an Enter, it flips straight to running, without second-guessing status anywhere else.