fix(status): serve menubar-json status from a disk-persisted snapshot to eliminate per-poll re-parse latency - #1
Open
dgabehar wants to merge 399 commits into
Open
Conversation
…lish # Conflicts: # CHANGELOG.md
…e-republish cache: stop republishing unchanged month shards on ranged runs
Hold Ink stdout columns/rows frozen during a SIGWINCH burst and emit one settled resize, then rerender. Do not intercept writes, so a mid-burst state update still paints even when net size is unchanged. Fixes getagentseal#977.
Stop writing OAuth caches as Application Support JSON. Persist CodeBurn-owned items in Keychain, secure-read and migrate leftover 0644 files only after read-back verification, and keep Claude from storing a refresh token.
GLM-5.3 is missing from LiteLLM, so Hermes/Cline sessions priced at $0. Alias it to the glm-5p2 sibling and keep the display name GLM-5.3. The root Hermes profile was shown as project "default". Use "hermes" for that profile only; named profiles and cwd inference are unchanged. Scan Hermes transcripts for https GitHub pull URLs and attach them so the Pull requests page can attribute those sessions.
Limit session_meta model extraction to direct payload fields so nested provenance metadata cannot overwrite turn_context. Invalidate cached parses and cover model switches with a regression test.
Hermes stores Cline Pass as cp/cline-pass/glm-5.3. One prefix strip leaves cline-pass/glm-5.3, which missed the bare glm-5.3 alias and stayed $0. Price the last path segment through the same aliases.
Tool dumps (gh pr list, changelog grep) were smearing one session across dozens of pull URLs. Capture links from user and assistant text only.
Price any OmniRoute/Cline/cmd/antigravity wrapper by peeling prefixes and, for GLM 5.x, falling back to the newest priced sibling. Do not require a new alias per model id. Classify Hermes sessions by surface and workspace: ACP is the Buzz app; project comes from git root or a real cwd, never $HOME or a profile name.
Extra High review held getagentseal#1039. Stop pricing every future glm-5.x as 5.2, stop collapsing unknown provider/org/model trees onto a priced leaf, and stop calling ACP Buzz — source=acp is a transport and the DB has no client field. Strip trailing punctuation on PR URLs and bump the Hermes parse version so old cache rows reparse.
A valid Keychain item plus a leftover JSON used to skip chmod, so a failed unlink could leave 0644 secrets on disk. Failed Disconnect also cleared bootstrap and hid the retry. Repair leftover files to 0600, keep bootstrap when Keychain delete fails, revalidate the opened fd, and loop the secure read.
SuperGrok Heavy is a $300/mo preset compared to parsed API-equivalent spend on a calendar month. CodexBar's live weekly window is a different class. TUI and desktop copy now say budget / calendar month and deny a live provider window. No Grok Connect is invented.
Extra High held getagentseal#1039 again. An unknown provider/model still became a bare-model price via getCanonicalName's first-segment strip. Relative cwd values like '.' could inherit the invoking repo. PR scrape treated fenced dumps and other repos as attribution. Peel only known vendor/router namespaces. Require an absolute platform path before a Hermes cwd is a workspace. Ignore fenced URLs and, when a git root exists, keep only that repo's pull links. Bump the Hermes parse version so old cache rows reparse.
Extra High held 91754c5. z-ai/glm-5.2 went unpriced (Cline's real vendor spelling). Forward-slash UNC became a workspace on POSIX. PR matching used basename, so evil/codeburn collided. Tilde fences were still scanned. Add z-ai to known namespaces. Reject // UNC on POSIX. Attribute PRs only when origin yields owner/repo. Strip ``` and ~~~ fences. Bump Hermes parse version to v4.
Hermes and token-plan sessions store mimo-v2.5-pro. The snapshot row is xiaomi/mimo-v2.5-pro. Same class as the existing mimo-v2-flash alias. No invented rate. Looking up the display name on the stripped leaf before following a pricing alias, so cline-pass/mimo-v2.5-pro cannot recurse strip → alias → last-segment forever.
Extra High MERGE AFTER FIX on 252ea92. Pathname chmod was unverified. Disconnect hid retry when only the legacy file survived. Secure read stopped at exactly maxBytes. Tighten leftovers via opened-fd fchmod+fstat. Keep bootstrap unless both Keychain and legacy deletes succeed. Read maxBytes+1 so growth past the limit is rejected.
Extra High MERGE AFTER FIX on d3f86f5. mimo-v2.5 aliased to xiaomi/mimo-v2.5 then last-segment recursed forever. Looking up SHORT_NAMES before resolveAlias also froze user remaps of known ids (gpt-4o still displayed as GPT-4o). Follow user aliases first. Break strip→alias→leaf cycles. Do not invent a Kimi rate. Do not paper over this with a mimo-v2.5 SHORT_NAMES row.
CI typecheck failed: sessions-report maps getShortModelName, and the Extra High cycle Set was a second parameter. Array.map fed the index as `seen`. Cycle tracking stays on an internal helper. Display and alias behavior unchanged. No second Extra High.
…d month-shard cache re-layout Upstream restructured the session cache from a flat session-cache.v7.json to a month-sharded-by-provider layout (v8, now v9 via envelope re-layout) and split parseAllSessions into a sync wrapper plus an AsyncLocalStorage-scoped parseAllSessionsInCacheScope for correctness around concurrent cache-dir changes. - src/session-cache.ts: cleanupOrphanedTempFiles conflict resolved by keeping both sweeps upstream added (parent-dir versioned-cache-file sweep, and shard-dir blanket .tmp + unreferenced-shard sweep) but extending the parent-dir sweep to also catch status-snapshot.json.*.tmp, since that file lives in the parent cache dir (orthogonal to the month-shard subdirectory) and upstream's shard-dir sweep never sees it. Also repointed the two remaining getCacheDir() call sites in the status-snapshot section to getCodeburnCacheDir(), the renamed/relocated version of that helper. - src/parser.ts: kept CorpusFingerprint/computeCorpusFingerprint verbatim (untouched by upstream's diff), took upstream's parseAllSessions / parseAllSessionsInCacheScope split entirely. Verified: tsc --noEmit clean; tests/cli-status-menubar.test.ts (15/15), tests/session-cache*.test.ts + tests/session-cache-shards.test.ts (119/119), tests/parser*.test.ts (34/34) all pass. A full `vitest run` also surfaced 3 pre-existing failures confined to app/renderer/*.test.ts (missing jsdom in that nested workspace's own node_modules) — unrelated to session-cache.ts/ parser.ts and outside this merge's scope.
…h-verification ci: verify the 0.9.20 upgrade path on every platform
The `mimo-v2-flash -> xiaomi/mimo-v2-flash` alias shipped before this branch and already cycled through display-name resolution, so getShortModelName threw RangeError on every real MiMo v2 Flash session. The new cycle-safe resolver fixes it, but nothing pinned the ids that actually crashed in production: cover the four spellings found in a real session cache, including the unnamespaced `mimo/mimo-v2-flash`. Add the base `mimo-v2.5` display name so the row reads next to "MiMo v2.5 Pro" instead of showing a raw slug; SORTED_SHORT_NAMES is longest-first, so the Pro tier still wins its own entry.
The codex parse version and CODEX_CACHE_VERSION bumps in getagentseal#1040 make codex sessions re-parse, but the daily cache has no per-provider invalidation, so every day already finalized keeps its old per-model rows - and usage-aggregator serves every day before today from that cache, with ten-year retention. Raise DAILY_CACHE_VERSION and MIN_SUPPORTED_VERSION to 20 so history re-derives once off the warm session cache. The re-derivation test now seeds v19, the last shipped version, so it models the real 19 -> 20 path, and the upgrade-path check expects daily-cache.v20.json. Measured on a real 110-day cache: no day lost value, none disappeared, 100 came back identical, and 9 grok days rose by $19.80 in total from rollups an earlier parse change had left stale. Every codex model row was unchanged - that corpus predates the provenance field the fix corrects.
docs: add Omarchy plugin installation
A turn anchored before the range start survives range slicing whole, so the aggregator can emit a residue day the parse never covered (getagentseal#1130, found in getagentseal#1127's root-cause work). The merge guards already defuse the overwrite; this stops the residue from reaching durable history at all. Applied at both ranged call sites (re-derive and gap); the aggregator and slicer semantics are untouched — whole-corpus callers still aggregate undated parses freely.
…ission The getagentseal#1126 test pinned basename presence; under getagentseal#1128's fail-closed provenance the same fixture (no trusted provider-recorded cwd) correctly omits the field entirely — stronger privacy, same zero-paths guarantee. The positive basename-on-the-wire case is covered by sync-project-provenance.
…-filter fix(daily-cache): ranged parses never write days outside their range
…ect-privacy fix(sync): trust only proven outbound project metadata
…an tab Ports the desktop app's Gemini Code Assist quota flow (loadCodeAssist tier + retrieveUserQuota buckets, ~/.gemini/oauth_creds.json read-only, in-memory-only refresh) to the menubar. Plan tab and empty-state gating now treat Gemini as plan-capable.
…ogo rows Replaces the tab strip with a NavigationSplitView shell: search field, General/About tile rows, and a data-driven Providers section whose real logo marks render gray at rest and accent-tinted on hover, with green connected dots and an N-on counter. Bundles template icon assets.
…an tab Ports the desktop app's Copilot flow (editor-plugin OAuth token from ~/.config/github-copilot, copilot_internal/user snapshot) read-only, with github.com-host preference and UI-string sanitization.
…icon, resource-bundle packaging Settings window now replicates CodexBar's System Settings layout (880x620 fullSizeContentView window, transparent titlebar, fixed 260pt sidebar, SF Symbol gradient chips, native search field, grouped About pane with Updates/Links). Status item renders the 0101 flame template with severity tints. package-app.sh ships the SwiftPM resource bundle, fixing the Bundle.module launch crash. Sidebar layout modeled on CodexBar (MIT, steipete/CodexBar).
# Conflicts: # src/main.ts # src/parser.ts
…Settings pane, Plan tab Ports the desktop app's Antigravity flow: ps+lsof discovery of the local language server, Connect-RPC quota/status probes over loopback HTTPS with the self-signed-cert trust override scoped strictly to 127.0.0.1, disconnected-state UX when the app is not running. Also swaps all provider sidebar marks to the official LobeHub icon set.
Popover header now renders the full CodeBurn wordmark filled with the site's .flame-text gradient (#ff6a00 > #ffda44 > #e8553a > #ff8c00, 300% width, 3s ease sweep). Static gradient under Reduce Motion.
Status item returns to the SF flame (the 0101 mark blurs at menubar size); About hero shows the full-color binary flame and the About sidebar row its bare white template, no chip; footer credits Resham Joshi (iamtoruk) / AgentSeal, MIT; About tagline matches the website (Your AI Bill, Itemized); UI copy drops em-dashes (currency rows use middots); Settings window is resizable and fullscreen-capable and recenters when restored off-screen.
The 0101-detail flame collapses into a faint smudge at 20px; the row now uses a solid silhouette cut from the website flame's own outline.
…upe, em-dash sweep Settings window titles itself after the visible pane like System Settings; the popover subtitle adopts the website tagline (Your AI Bill, Itemized); About stops printing dev (dev) when version and build match; remaining user-facing em-dashes across popover and service strings become sentence breaks.
…uota-parity Menubar: Gemini/Copilot/Antigravity live quota, CodexBar-style Settings, brand refresh
…pshot Second-round review fixes for the persisted menubar status snapshot (getagentseal#999, ozymandiashh review 2026-08-21): - queryKey now folds in getPricingGenerationKey() (src/models.ts): the live LiteLLM cache's freshness, the bundled snapshot's own content digest, and the existing CACHE_SCHEMA_VERSION pricing-behavior version. None of these moved the corpus fingerprint before, so a repricing fetch or pricing-logic fix could keep serving old rendered costs against an unchanged session corpus. - The optimize path (default; --no-optimize unaffected) no longer reads or writes the persisted snapshot at all. Optimize findings depend on mutable project/config/prompt/hook state with no single enumerable fingerprint, and computing them already requires the same full parse the snapshot exists to skip, so excluding them from the snapshot loses no additional performance versus fingerprinting that input surface. Adds two regression tests exercising both fixes end-to-end.
Version 0.9.21 across CLI, desktop app, and Windows menubar; refreshed LiteLLM pricing snapshot and fallback (catches the 2026-08-24 OpenAI GPT-5.6 cut and DeepSeek v4 repricing mid-rollout, getagentseal#1134 tracks re-tightening); CHANGELOG stamped for 0.9.21; README release links to 0.9.21. Snapshot-pinned model tests updated to current catalog truth.
…ind an instant paint The renderer memo was an LRU capped at 8-24 entries, so cycling periods evicted payloads and returning to one forced a skeleton plus a 1.5-6s recompute. The memo is now one latest payload per key for the app session: a revisited period paints its cached numbers in the same commit, skips the CLI entirely when the payload is under 30s old, and otherwise revalidates behind the painted data and swaps in place. Failed revalidates keep last-good data with the existing error surface; first visits keep the loading state. (getagentseal#1135 part 1)
Ports the menubar's Kimi flow to the Electron quota module: read-only ~/.kimi-code/credentials (KIMI_CODE_HOME honored), api.kimi.com usages snapshot, terminal expired-login state that never refreshes the CLI's short-lived token, 5-hour/weekly window mapping with over-limit clamp, membership plan label, and the standard per-provider Settings toggle.
… dirs, env-var invisibility Round 3 adversarial-panel fixes for PR getagentseal#999: - Snapshot writes now go through a per-queryKey file plus a CAS re-read guard before rename, closing the last-writer-wins race where a slower recompute against an older corpus could clobber a fresher save, and where distinct queryKeys used to evict each other's single shared slot (B-G1). - cline-cli's discoverSessions now fingerprints the growing <sessionId>.messages.json sibling instead of the static <sessionId>.json metadata file, so new turns in a live session are no longer invisible to computeCorpusFingerprint (C-G1). - computeCorpusFingerprint folds in computeEnvFingerprint per discovered provider, so non-discovery env vars like CODEBURN_CURSOR_MAX_BUBBLES and KIMI_MODEL_NAME can no longer serve a stale-forever snapshot (A-G1). - collectFilesRecursive now resolves symlinked subdirectories and recurses into them (with a visited-inode guard against cycles) instead of misclassifying them as leaf files (C-G2). - Added tests/session-cache-status-snapshot.test.ts covering concurrent writers against the snapshot file, mirroring session-cache-shards.test.ts's existing coverage for the main cache (D-G9).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
codeburn status --format menubar-jsontook 25-90+ seconds per call regardless of cache state, causing the polling CodeBurnMenubar.app to stall/stop refreshing. Root cause (confirmed via live profiling, not the original findings doc's hypothesis): the menubar spawns a fresh CLI process per poll, so every call re-parses the full session-cache blob and re-runs the full aggregation pipeline with zero cross-process reuse. This fix adds a disk-persisted status snapshot keyed by a cheap corpus fingerprint, with a settle-window debounce so rapid-fire source writes coalesce into one recompute instead of one per poll.Changes
src/session-cache.ts— disk-persisted status snapshot keyed by a cheap corpus fingerprint (stat-only, no content read)src/parser.ts— settle-window debounce so rapid-fire source writes coalesce into one recompute instead of one per pollsrc/main.ts— wires thestatus --format menubar-jsonpath to read from the persisted snapshot instead of re-parsing + re-aggregating on every polltests/cli-status-menubar.test.ts— new coverage for the snapshot/fingerprint/debounce behaviorSPEC-perf-cache-fix.md— fix design specRally Story
N/A
Test Plan
npm test(includes newtests/cli-status-menubar.test.tscoverage for the snapshot/fingerprint/debounce path)References
PERF-DEFECT-FINDINGS.md(repo root)Checklist
docs/release-notes.md+README.md)context-packs validatepasses (if directory files changed)npm testgreenGenerated by Vera (GitHub Backplane) — Session: b05c*** | Dispatch: 9*