Three review follow-ups.
1. The rotation guarantee is now pinned by a real parse, not by two unit
tests that both stub the fingerprint. The original defect was a wiring
bug — right value, wrong selector — and a stubbed fingerprint cannot see
a wiring bug. The new case warms the cache under key K1 (one JetBrains
turn, one call), rotates the key file to K2, appends a second turn, and
re-parses in a fresh module registry: exactly two calls, no K1-era dedup
key anywhere, on disk or in the result. Restore the substring-sniff
selector and it reports three calls for two turns — the user-visible
shape of the bug.
2. The test-helper guard compared UNRESOLVED paths, so a symlink at a
sandbox-shaped location (HOME=$TMPDIR/codeburn-test-env-x -> /Users/me)
passed the prefix test and the write landed in the real home anyway. Both
sides are realpath'd before comparing, which also stops a sibling
directory sharing the tmpdir prefix from matching. Same refusal message.
3. CHANGELOG records the two API breaks that were missing: decodeCopilot
throws on an empty privacyKey for JetBrains records, and
SOURCE_REF_KEYED_PARSE_VERSION is no longer exported (KEY_DERIVED_PROVIDERS
replaces it).
fixed-privacy-key.ts writes <home>/.config/codeburn/privacy-key
unconditionally. Under vitest that is the throwaway sandbox
env-isolation.ts mints, and harmless. Imported from anywhere else — tsx,
a stray node -e, a REPL — homedir() is the developer's real home and the
write silently replaces their key, re-keying every resource fingerprint
and orphaning whatever was already synced. privacy-key.ts refuses to
cause that on its own; a test helper must not cause it either.
The sandbox has a recognizable shape (a mkdtemp dir under tmpdir() named
codeburn-test-env-*), so the guard needs no new marker: anything else
throws with the reason.
dedupKey is a CallObservation field: it ships on the envelope. Six
decoders — codebuff, zerostack, pi, omp, grok and lingtai-tui — were
folding the absolute source path (a chat directory, a session file) into
it raw, so a host path rode every payload derived from those sessions.
They now fold `sourceRefFingerprint`, a keyed HMAC-SHA256 under a new
`source` domain. Like every other fingerprint in that module the key is
required and an empty key throws, so a source ref can never degrade to an
unkeyed, dictionary-attackable digest.
That means the CLI bridge has to supply a real key. It passed `''`, which
was correct when minimization happened only on the sync path; it now
threads getHostPrivacyKey() — per-install stable, so dedup keys stay
stable across runs.
The keys change VALUE, so the six carry a `source-ref-fingerprint-v1`
parse version that forces one re-parse and drops the cached raw-path keys
instead of re-ingesting the same records under two shapes. A parse
version cannot see the privacy key change, though, and a lost, rotated,
or ephemeral (unwritable config dir) key would silently produce keys that
never match the cached ones — so computeEnvFingerprint folds a digest of
the key in for exactly those providers.
The five bridge parity goldens used to compute their expected keys with
the same function and key production uses, which pins nothing. They now
re-derive the fingerprint longhand under a pinned test key, assert the key
SHAPE, and assert the raw fixture path appears nowhere in it.
Two pre-existing CLI-suite failures blocked #923's new cli CI job on Linux:
- tests/models.test.ts DeepSeek v4 pricing (x3): loadPricing() fetched the
live LiteLLM table during tests, and live data wins over the bundled
snapshot, so the DeepSeek v4 assertions went red when upstream repriced.
Ports main's fix (4866332b): CODEBURN_PRICING_SNAPSHOT_ONLY skips the fetch
and prices purely off the bundled snapshot; env-isolation.ts sets it for
the whole suite.
- tests/parser-proxy-pricing.test.ts case-insensitive isProxiedPath: the
branch's isProxiedPath/normalizeProxyPath is byte-identical to main's
(case folding is deliberately darwin/win32-only, per the function's own
comment). The test hardcoded the macOS-only expectation, so it fails by
design on Linux CI. Main hit and fixed the same thing (bcf11552); this
ports that fix verbatim: assert the platform-correct behavior instead of
a hardcoded `true`.