test(parsers): a new parser could land with no test and no CI objection - #116
Merged
Conversation
added 2 commits
July 26, 2026 05:08
Closes the harness half of #104. Coverage here is inverted relative to risk. CLAUDE.md:169-172 records the historical failure modes — dedup failures, cached-input semantics, snapshot-vs-cumulative confusion — as 1.6x to 7x magnitude errors in the numbers the whole product exists to report. Four parsers (hermes, codebuddy, craft, kimi) had no test at all, and README:116 invites contributors to add more. test/parser-conformance.test.js ENUMERATES THE PARSERS FROM SOURCE, not from a hand-written list, so a parser added without a fixture fails rather than being silently skipped. 24 found. Each must have either a conformance fixture or an allowlist entry saying why not, and the allowlist is a ratchet: the test fails if it grows past max_size AND fails if it is shorter, telling you to lower max_size in the same commit. An allowlist that can grow is a TODO list. Fixtures for three of the four named parsers: craft, kimi, codebuddy. Hermes is on the allowlist because it reads a SQLite database across profile directories, which needs a real schema rather than a JSONL line. Per fixture: the column invariant, non-negative counts, a model, a 30-minute UTC bucket, and that re-parsing unchanged input adds nothing (the "run sync twice" lesson, which is what catches dedup-key instability). TWO THINGS FOUND WHILE BUILDING IT, both recorded rather than smoothed over: 1. The column invariant does NOT catch cache-read double-counting. A parser that folds cache reads into input_tokens and inflates total_tokens to match is internally consistent, so the sum balances while the user's number is ~4x too big — precisely the class this issue is about. There is now a test pinning that blind spot so nobody reads the harness as covering more than it does. Only a real sample log with known-correct values catches it. 2. The kimi fixture initially produced ZERO rows and would have passed as a green test proving nothing. The harness asserts a fixture yields rows before checking them; that guard caught it. Cause: kimi's timestamp is epoch SECONDS and the fixture omitted it entirely. The conformance checker is itself tested against four known-bad rows plus a valid control, so it is not a checker nothing has ever failed. ci:local exit 0: 918 root tests, 256 dashboard.
This was referenced Jul 25, 2026
Merged
pitimon
added a commit
that referenced
this pull request
Jul 26, 2026
15 commits since v0.39.43. Tracker: #125. User-facing: - The Projects panel honours the date range. It read no query parameter at all, so picking "24h" narrowed every other card while Projects kept showing all-time totals with nothing on screen saying so (#118). - Per-repo cost, with rows written before the change named as unpriced rather than shown as a confident $0 (#121). - Sources with no per-repo attribution are named, instead of their absence reading as "that tool cost nothing here" (#118). - A plan-value card answering what README:32 already promised, labelled as list-price-equivalent rather than a saving (#122). - The Codex quota chip no longer vanishes on a 401 (#119). - `sync --compact`, which reclaimed 34,924 lines to 5,636 on a real install (#117). Behind the scenes: outbound-privacy validator hardening (#111, #114), avatar proxy per-hop revalidation, port-aware allowlisting and a real download cap (#109, #112), a parser conformance ratchet (#116, #120), scripts/graph out of the product gate (#115), and a usage-limits fixture capture tool (#124). Version bumped in all four lockstep locations by the `version` hook: package.json, package-lock.json, both TokenTrackerBar targets, and the Windows csproj. validate:version-lockstep passes. WATCH AFTER SHIPPING: #121 migrates cursors.json on every user's first sync after upgrading, re-keying project buckets from project|source|hour to project|source|model|hour. Without that the old bucket strands and its usage is counted twice. Verified read-only against a real 2,015-bucket state — every key migrated, total_tokens preserved exactly at 6,281,653,062, and all 2,015 queuedKey markers survived, the loss of which would re-append every row. That is one machine; a differently-shaped state is the residual risk. ci:local exit 0: 972 root tests, 302 dashboard. Co-authored-by: itarun.p <itarun.p@somapait.com>
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
Closes the harness half of #104.
Coverage here is inverted relative to risk.
CLAUDE.md:169-172records the historical failure modes — dedup failures, cached-input semantics, snapshot-vs-cumulative confusion — as 1.6× to 7× magnitude errors in the numbers the whole product exists to report. Four parsers (hermes,codebuddy,craft,kimi) had no test at all, andREADME:116invites contributors to add more.The ratchet
test/parser-conformance.test.jsenumerates the parsers from source, not from a hand-written list — 24 found. Each must have either a conformance fixture or anallowlist.jsonentry saying why not. A parser added without either fails; it cannot be silently skipped.The allowlist fails if it grows past
max_size, and fails if it is shorter — telling you to lowermax_sizein the same commit so the ratchet records the ground gained. An allowlist that can grow is a TODO list.Per fixture: the column invariant
total = input + output + cache_creation + cache_read + reasoning, non-negative counts, a model present, a 30-minute UTC bucket, and that re-parsing unchanged input adds nothing — the "run sync twice" lesson, which is what catches dedup-key instability.Fixtures for three of the four named parsers:
craft,kimi,codebuddy.hermesis on the allowlist because it reads a SQLite database across profile directories, which needs a real schema rather than a JSONL line.Two things found while building it
Both are recorded in the code rather than smoothed over.
1. The column invariant does not catch cache-read double-counting. A parser that folds cache reads into
input_tokensand inflatestotal_tokensto match is internally consistent — the sum balances while the user's number is ~4× too big. That is precisely the class this issue is about, and this harness does not catch it. There is now a test pinning that blind spot so nobody reads the harness as covering more than it does. Only a real sample log with known-correct expected values catches it, which is what the per-tool tests are for.2. The kimi fixture initially produced ZERO rows and would have passed as a green test proving nothing. The harness asserts a fixture yields rows before checking them, and that guard caught it. Cause: kimi's timestamp is epoch seconds and the fixture omitted it entirely.
Why the codebuddy fixture is the interesting one
CodeBuddy's
prompt_tokensis OpenAI-style and includes cached tokens, soinput_tokens = prompt_tokens - cache_read. The fixture feeds 3000 prompt tokens of which 2400 are cache reads, and the parser emitsinput_tokens: 640— the subtraction, held still.Test plan
ci:localexit 0 — 918 root tests (+9), 256 dashboardCONTRIBUTING.mdstep 4 documents the fixture requirement and, explicitly, what it does not proveStill open on #104
A
hermesfixture, and fixtures for the 21 allowlisted parsers. The ratchet is what makes those get written as anyone touches them.