Repository navigation
test(capture-kit): pin the ax1 digest wire format the daemon persists - #2941
Conversation
`computeAxDigest` returns a ~50-byte string that is persisted as post-action evidence and compared across daemon restarts (#1047), yet nothing in the suite pinned the bytes. Every test but two compared one invocation against another, so a change to the construction moved both sides and the file stayed green. Proven by mutant: deleting the NUL separators from `hashNode` — which makes the tuple ('ab', '', '') hash the same bytes as ('a', 'b', ''), i.e. two different trees reporting identical evidence — leaves `ax-digest.test.ts` at 9 passed. It is the only cross-field collision guard in the function and no test could see it go. Three additions, each with a named mutant: - field separation: ('ab','','') vs ('a','b','') vs ('a','','b') must differ. Separator removal now fails 2 tests. - fixed width: `ax1:` + exactly 16 hex, replacing the `startsWith('ax1:')` probe, which a widened `slice()` or a dropped prefix would survive. - a golden for a fixed 3-node set, so XOR width, separator placement, and the count folding are all contract. Narrowing `PER_NODE_HASH_BYTES` to 8 fails only this one. Test-only: no production line changes.
Size Report
Startup median (7 runs, lower is better):
|
|
Reviewed at c982439: this is a test-only golden table pinning the ax1 digest wire format, and it's ready for human review. All 17 checks pass, and the diff only touches packages/capture-kit/src/snapshot/tests/ax-digest.test.ts, so no production route is exercised by this change. Not blocking: the golden's comment and the PR title describe the digest as "persisted ... across daemon restarts," but every comparison found (https://github.com/callstack/agent-device/blob/c982439/src/commands/interaction/runtime/post-action-surface.ts#L66, https://github.com/callstack/agent-device/blob/c982439/src/commands/interaction/runtime/interaction-ios-tap-outcome.ts#L221-L223) computes both sides within one command execution rather than reading anything back after a restart, so it would be worth rewording that comment and the title to the field's real contract — a client-visible value in --verify evidence where a byte change is a user-visible output change — but that can wait for a follow-up. |
|
Summary
computeAxDigestreturns a ~50-byte string that is persisted as post-action evidence and comparedacross daemon restarts (#1047). Nothing in the suite pinned the bytes it returns: 7 of its 9 tests
compared one invocation against another, so any change to the construction moved both sides of the
comparison together.
I verified that rather than assuming it. Mutants I planted and ran:
hashNode('ab','','')hashes the same bytes as('a','b','')— two different trees reporting identical evidence9 passed🚫PER_NODE_HASH_BYTES16 → 8Three additions, each tied to one of those mutants:
('ab','',''),('a','b','')and('a','','b')must all differ. This isthe only cross-field collision guard in the function and it was invisible to the suite.
/^ax1:[0-9a-f]{16}$/, replacing thestartsWith('ax1:')probe, which a widenedslice()or a dropped version prefix would survive. Strengthened in place rather than added next toits own weaker copy.
node-count folding all wire contract instead of implementation detail.
Test-only; no production line changes. The
ax1:prefix test was rewritten, not duplicated, so thefile does not grow a second assertion for the same claim.
Validation
Tested at
c982439f8.npx vitest run --project unit-core packages/capture-kit/src/snapshot/__tests__/ax-digest.test.ts→11 passed (was 9)
git statusclean of production changes before committingpnpm check:affected --run— all runnable checks passedoxlint . --deny-warnings, repo-wideoxfmt,tsc -b packages/capture-kitExpected interaction: the golden is a deliberate wire pin. Changing the digest algorithm in the
future means updating this one literal, which is the point — the change is then visible in the diff
instead of silently invalidating evidence an earlier daemon wrote.
Note for reviewers: #2933 also touches this file. The hunks are ~55 lines apart (it deletes the
self-comparison test at the top; this edits the prefix test and appends), so they merge cleanly in
either order.