payload: add-only stale marker for read-only stale menubar serves - #1100
Conversation
isSessionHydrationComplete() (parser.ts, PR #937) already reports when a read-only serve skipped or staled real on-disk changes; thread it through buildMenubarPayload as an optional stale field, present and true only on a stale serve, always absent otherwise, so older/newer CLI-app pairs stay compatible. Mirrors the field into the desktop renderer types and the macOS menubar's Codable payload model (data layer only, no view change). Drafted with minimax/MiniMax-M3 via local gateway.
isSessionHydrationComplete() (parser.ts) reads a module-level global that is only safe immediately after the parse it describes, with no intervening awaits. The stale-marker wiring landed in the prior commit read it at the bottom of buildMenubarPayloadForRange, after several awaits -- including, on the claude-config-scoped branch, a second parseAllSessions call for the 365-day history block that runs after the parse producing the headline data. That let the history backfill's hydration outcome silently overwrite the headline's. Capture the flag into a local right after each branch's primary parse resolves (the claude-config-scoped parse, or buildDurablePeriod) and thread that captured value to buildMenubarPayload instead of re-reading the global at the end. This also closes a cross-request race: once captured synchronously, no other in-flight request's parse can flip it under us. Regression test simulates a complete primary parse followed by an incomplete bystander parse and asserts the payload reflects the primary outcome, not the bystander's. Drafted with minimax/MiniMax-M3 via local gateway.
|
Pushed a fix for the review's stale-global-read bug (commit e19c099). Bug: Fix: capture New test ( Verified: full suite (226 files / 3142 tests passed, 2 files / 5 skipped — pre-existing), |
Summary
Closes #771. The warm-refresh gate (#743) serves the prior complete snapshot when the refresh lock times out or is unavailable — correct and fail-closed, but the payload's only time field was the render timestamp, so a stale serve was indistinguishable from a fresh parse.
Since #771 was filed,
src/parser.tsgainedisSessionHydrationComplete()(PR #937) which already returnsfalseexactly when the last completed parse in-process was a read-only serve that had to skip or stale real on-disk changes. This PR threads that signal through to the payload:src/menubar-json.ts: new optional top-levelstale?: booleanonMenubarPayload;buildMenubarPayloadgains a trailing optionalstaleparam and setspayload.stale = trueonly when passedtrue— never emitted asfalse.src/usage-aggregator.ts:buildMenubarPayloadForRangepassesisSessionHydrationComplete() ? undefined : true.app/renderer/lib/types.ts: mirrors the field (add-only, per this repo's CLI-app payload contract).mac/Sources/CodeBurnMenubar/Data/MenubarPayload.swift: mirrors the field in the Codable data model (let stale: Bool?, decoded viadecodeIfPresentso older CLI payloads that never emit it decode fine asnil).Absence of the field always means "assume fresh," including for payloads from a CLI version that predates it — the fail-safe direction for a marker like this.
Follow-up (not in this PR)
The macOS menubar UI has no visible staleness indicator yet — this PR is data-model only for the Swift side. A subtle indicator (small dot/tooltip near the header, matching the pattern of
#1096's cold-state signaling) is a reasonable small follow-up once there's a view spot picked for it; scoped out here to keep this change minimal and reviewable.Test plan
tests/menubar-json.test.ts: new cases assertstale: truewhen passed, andstaleisundefined(notfalse) on a normal build. Verified revert-proof — reverting thesrc/menubar-json.ts+src/usage-aggregator.tschanges fails the newstale:truetest.tests/usage-aggregator.test.ts: new assertion that a normal (env-isolated, always-complete) parse leavespayload.staleundefined.npm run test(full suite, excluding the separately-gated lock tests): 225 files / 3141 tests passed, 2 files / 5 tests skipped (pre-existing skips, unrelated).tsc --noEmitclean on both the CLI (tsconfig.json) and the desktop renderer (app/tsconfig.json).swift buildandswift test --filter MenubarPayloadCombinedTestsclean inmac/.