Skip to content

ci: verify the 0.9.20 upgrade path on every platform - #1036

Merged
iamtoruk merged 4 commits into
mainfrom
ci/upgrade-path-verification
Aug 19, 2026
Merged

ci: verify the 0.9.20 upgrade path on every platform#1036
iamtoruk merged 4 commits into
mainfrom
ci/upgrade-path-verification

Conversation

@iamtoruk

Copy link
Copy Markdown
Member

What

A CI workflow (upgrade-path.yml, matrix ubuntu/windows/macos × Node 22.13.0/22, on PRs touching src/** + dispatch) and npm run verify:upgrade that runs the same script locally. It:

  1. Generates a deterministic multi-provider corpus (Claude incl. > 32 KB line + sidechains, Codex, Gemini, Kiro, DSH, Grok, a WAL-mode cursor SQLite) into an isolated HOME.
  2. Installs the published codeburn@0.9.20 into an isolated prefix and runs it → session-cache.v7.json + daily-cache.v17.json baseline and payloads.
  3. Installs this build (npm pack → isolated prefix, invoked through the npm bin symlink so dist/parse-worker.js resolution is exercised) against the same cache dir → asserts v7 retired, session-cache.v9/ + envelope, daily-cache.v19.json re-derived with v17 kept, history days not shrunk.
  4. Per-provider parity vs the baseline (calls/tokens exact, cost within 0.5% for offline pricing drift): claude/codex/cursor/gemini/kiro identical; grok "changed by design" (fix(grok): read the CLI's own completed-turn usage instead of estimating it #1015, only call count asserted stable); dsh "new".
  5. Serve smoke: ready frame, byte-parity vs one-shot for menubar-json and models, exits on stdin close.
  6. Worker determinism: CODEBURN_PARSE_WORKERS=0 vs 3 → identical payloads and shard bodies, pool confirmed engaged.
  7. Warm second run: nothing re-parsed, shard bodies unchanged; republished-with-identical-content shards are reported (was cache: date-ranged queries republish the month shards they skipped, byte-identical, on every run #1032, fixed by cache: stop republishing unchanged month shards on ranged runs #1035).
  8. Never-lose across partial source aging — the release blocker found by the hardening pass (daily-cache: never lose a day's history when its transcripts have partially aged out #1033): deletes 2 of 3 transcripts for two old days and all for a third, then upgrades; asserts partial days >= baseline and the fully-sourceless day == baseline at (date, provider) granularity. Failed with LOST 76.6% before daily-cache: never lose a day's history when its transcripts have partially aged out #1033; passes now.

Local run on macOS/Node 26 against current main: PASSED, ~17 s.

Not covered (stated)

DSH .zstd variant (Node 22.13 lacks zstd — legs wouldn't be comparable); the worker auto-gate thresholds (workers are forced); the .cmd shim on Windows gets a --version smoke only (Node won't spawn .cmd without a shell); a synthetic corpus, not a real user cache — the real-cache verification was done manually on the maintainer's machine.

Everyone upgrading from the last published CLI crosses the session-cache
v7 -> v9 re-layout (#1005/#1007) and the daily-cache v17 -> v19
re-derivation (#1015) on their first run. That path was covered by unit
tests on one platform against caches the tests wrote themselves.

`npm run verify:upgrade` (and the matching matrix job over
{ubuntu, windows, macos} x node {22.13.0, 22}) instead installs the real
codeburn@0.9.20 into an isolated global prefix, points it at a generated
seven-provider corpus in a HOME whose path contains a space, and lets it
write a genuine session-cache.v7.json + daily-cache.v17.json. This build
then runs against that same cache dir, installed the same way, so
dist/parse-worker.js has to resolve from an entry point outside the
checkout.

It asserts the v7 file is retired, the v9 envelope and shards publish,
the daily history does not shrink, and per-provider calls/tokens/cost
match the baseline exactly for claude, codex, gemini, kiro and cursor.
grok is reported rather than asserted (its accounting changed in #1015)
and dsh is required to be new. It also smoke-tests serve --stdio against
the one-shot payloads, pins CODEBURN_PARSE_WORKERS to 0 and 3 and
requires identical shards and payloads either way, and checks that a
second run re-parses nothing.
Claude Code deletes its transcripts after ~30 days, so between one run and
the next a day can go from fully sourced to PARTIALLY sourced. On such a
day the daily cache re-derives a smaller slice from the surviving files
and that slice REPLACES the baseline one rather than being unioned with
it, so the aged-out portion is lost. A day that aged out completely is
carried forward correctly, which is what makes this a hole in never-lose
rather than a missing feature.

The check ages the corpus the way retention does: two days keep a single
anchoring transcript each, a third loses every one. It then compares the
(date, provider) slices in daily-cache.v19.json against the ones the
0.9.20 baseline recorded in daily-cache.v17.json, requiring the partial
days never to shrink and the fully sourceless day to come back exactly.

This is a hard failure, not a note. It is expected to be red until the
fix lands: on the generated corpus the two partial days currently lose
76.6% and 56.8% of their cost, while the fully sourceless control day
returns to the cent.

It runs last and on its own cache dir, so mutating the corpus cannot
disturb the payload parity comparison. The shard-republication defect
found earlier stays a note, now naming its pending follow-up issue.
@iamtoruk
iamtoruk merged commit c9ad778 into main Aug 19, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant