Repository navigation
Fix all findings from the 2026-09 code audit - #15
Merged
Merged
Conversation
Disk-scanned ':' steps entries had their cache reads collapsed to each conversation's peak while RPC '#' entries of identical per-call shape were summed, so the agy CLI's cost depended on the capture path and dropped when a CLI stem flipped from RPC to disk ownership. On a real CLI-heavy ledger the 30-day figure moves from $713.72 to $1,046.39.
integrations/claude_code/statusline_capture.py wrote ~/.tallybar/claude_statusline.json but nothing read it. build_snapshot now uses it when the claude.ai cookie path yields no live limits (Cloudflare 403/429, signed out, wallet locked, --no-network): expired windows dropped, >6h captures ignored, >15min flagged stale, a newer capture replaces a carried reading. tests/conftest.py isolates every test from the developer's live capture.
A timed-out or failed cost scan leaves no costSummary, which read as $0 month-to-date; the budget block then discarded every armed key and the next good refresh replayed the alert. Budget evaluation is now skipped (armed state held) when the scan timed out/errored or no provider carries a costSummary.
carry_forward_provider_last_good deep-copied the cached entry including its costSummary, and apply_cost_summaries only setdefault'ed, so the fresh local scan was dropped for the whole 15-minute grace window. The carried copy now omits costSummary and apply_cost_summaries assigns (a None summary clears).
The cost-availability check read snapshot.get("diagnostics") twice, which mypy
can't narrow; bind it once.
…file cookiejar_for_domains merged every browser profile; the last store read (Firefox) won each (domain, path, name) collision and expiry was ignored. On the dev machine a Firefox profile unused for four months contributed 16 claude.ai and 35 openai cookies (8 and 23 of them expired) that shadowed live Chrome ones. Readers now normalize expiry/last-access to unix seconds (Firefox expiry is ms), select_session_cookies drops expired cookies and picks the single most recently used profile, and missing-cookie checks use the same view.
The byte-exact render tests failed on any day but the capture day (screenshot.qml rebased the fixture's week/month buckets onto the real today) and whenever the desktop icon theme changed (the kde platform theme reads it from the developer's kdeglobals). A today= argument (Makefile SHOT_DAY) and a repo-local XDG_CONFIG_HOME pinning breeze-dark make renders byte-stable; the cost popouts re-render identical to the committed PNGs, and the two widget shots are regenerated with breeze-dark footer icons (checked by eye).
commit() stripped every comma before parseFloat, so a decimal-comma locale's "12,50" (accepted by the DoubleValidator) saved as $1250. parseLocaleAmount (ui_helpers.js, node-tested) strips the locale's group separator, maps its decimal point to '.', and reads a C-style "12.50" typed in a comma locale as 12.5; verified under qml6 with de_DE/fr_FR/en_US.
The billing event is only logged while the grok CLI runs, so after a quiet week the newest event describes an ended period and the bar kept last week's percentage with "Reset due". run_grok_local now zeroes the bar and projects the reset to the current weekly period; grok_billing_period rolls the "This week" token window forward the same way. Both take now= (tests with past-dated periods pin it).
Codex re-emits token_count with an unchanged cumulative total_token_usage and the same last_token_usage; every repeat was counted again (100 of 13,809 real events, 3.37M tokens). The parser now skips an event whose cumulative total hasn't moved, and a total-only event contributes its delta instead of the running sum. Codex parse-cache version bumped so caches rebuild.
main.qml abandoned a refresh after 25s and dropped its result, but the backend can legitimately take cookies (t+1) + providers (t+1) + the lazy Codex cookie fallback (t) = 38s at t=12. backend.refresh_worst_case_seconds names that bound and the fallback is capped to the remaining budget so it is a guarantee; main.qml derives --timeout from one backendTimeoutSeconds and waits refreshWatchdogMs=45s. Tests pin the QML/backend relation and drive build_snapshot with every phase stalled to prove the bound holds.
Plasmoid.status raised NeedsAttention for any limit at >= 90%, including isExtraUsage rows (Claude overage, credit pools) that the backend's notifications deliberately skip, and CompactRepresentation's bars/pulse could pick up a credit row as the secondary window. ui_helpers.usageLimits / needsAttention (node-tested) now drive both.
The notification server renders body markup (<b>, <a href>, <img>) and bodies carry provider/API error messages, so a '<' or '&' garbled the text and an anchor or image in a response would render live. Bodies now pass through ui_helpers.escapeNotificationMarkup (the summary is plain text per spec and is left as is).
get_pricing scanned _active_pricing and wrote _resolve_memo outside the lock, so a refresh_pricing landing mid-scan (another OS thread) could have its memo.clear() undone by a stale write — the exact race the lock's comment claimed to prevent. A catalog generation counter (bumped on every rebuild, including invalidate_cache, now locked) gates the memo write; the scan stays lock-free. A test forces a refresh mid-scan and fails without the guard.
/proc/<pid>/cmdline is world-readable, so on a shared machine the language- server scan also matched another user's Antigravity process: it lifted that server's CSRF token from the cmdline, queried its loopback API and folded that user's usage into this user's ledger. Both the psutil path (real uid) and the /proc path (_owned_by_current_user, fail-closed) now filter by owner.
CompactRepresentation carried its own copy of the bad-status switch (same 13 statuses as lib/ui_helpers.js today, but free to drift) against the 'shared QML helpers live in lib/, don't re-inline' contract. It now delegates, and a test fails if any QML statusIsBad re-implements the set. Verified by an offscreen render: an unauthorized provider still dims the glyph and shows the amber badge.
When _pb_generations couldn't date a gen_metadata blob, the fallback ran the recursive _pb_find_usage, which returns every usage record twice (field 4 and its field-17.2 copy): measured on 14 real undated CLI blobs, exactly 2.00x the uncached input. The fallback now reads field-4 records structurally without the timestamp requirement (marker 26 only), and the recursive scan — kept for unknown layouts — drops exact duplicates first.
Follow-up to f8d71f9, which was committed with a failing test and a mypy error: _pb_generations' local list must allow secs=None, and the exact-dict test of its output now includes the new mk marker key (24 for that fixture).
run_claude_api tried /usage_summary before /usage on every refresh; for this account it 404s (2026-07), so each refresh paid an extra authenticated claude.ai GET — the call volume that trips Cloudflare. A definite miss (404/410 or 200 without limits) is now stamped as usageSummaryUnavailableSince and later refreshes go straight to /usage for 24h, then re-probe. Transient 403/429/5xx never stamp it.
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.
Fixes every finding from a full-codebase audit of
main(d1defc0). Each was reproduced with a script or measured on real data before it was fixed, and each fix carries a regression test.Cost accuracy
:) ledger entries had cache reads collapsed to each conversation's peak while identical RPC (#) entries were summed; cost depended on the capture path. All paths now bill cache reads per call. (Real ledger: 30-day $713.72 → $1,046.39.)token_countevents were counted twice (100 of 13,809 real events); total-only events now contribute their delta.gen_metadatagenerations: the fallback read each record twice (field 4 + its field-17.2 copy) — measured 2.00× on real blobs.Features that didn't work
integrations/claude_code) was written but never read. It is now a fallback when claude.ai can't be read (Cloudflare 403/429, signed out, locked wallet,--no-network).Correctness
expiryis now milliseconds; normalized.3·timeout + 2, 38 s); the Codex cookie fallback is capped to the remaining budget.usage_summarymisses are remembered for 24 h instead of costing an extra claude.ai GET per refresh.CompactRepresentationuses the sharedstatusIsBadhelper.Tests / tooling
screenshot.qmltakestoday=(MakefileSHOT_DAY) and a repo-localXDG_CONFIG_HOMEpins the breeze-dark icon theme; the two widget screenshots are regenerated (footer icons only). Cost popouts re-render byte-identical to the committed PNGs.tests/conftest.pyisolates every test from the developer's live statusline capture.Verification
make check(flake8, qmllint full, mypy): green.pytest tests/: 719 passed; withCI=1: 714 passed, 5 skipped (render tests).mainand this branch.make upgrade; the live-desktop check is still pending (the session was locked when plasmashell restarted).