Skip to content

fix: separate lineage hash domains and preserve result semantics - #941

Merged
rynfar merged 1 commit into
mainfrom
codex/fix-lineage-hash-domains
Sep 4, 2026
Merged

fix: separate lineage hash domains and preserve result semantics#941
rynfar merged 1 commit into
mainfrom
codex/fix-lineage-hash-domains

Conversation

@rynfar

@rynfar rynfar commented Sep 4, 2026

Copy link
Copy Markdown
Owner

A changed tool result could retain the same lineage hash as the cached history: changing is_error from false to true resumed the old SDK session and still answered SUCCEEDED. Text could also impersonate tool blocks or inject message boundaries, and per-block proofs did not include the message role.

Use structured, domain-separated encodings for aggregate, message and block hashes. Preserve roles, boundaries, tool identities/arguments and result error status. Canonicalize JSON keys while retaining plain-string/text-block equivalence and ignoring opaque thinking/cache hints. The existing adapter/diagnostic renderer remains unchanged and no longer acts as a lineage proof.

Legacy hashes trigger complete replay through the validated #940 path, then subsequent turns on upgraded proxies resume normally. Alternating between old/new proxy versions can repeat this cost until all participating proxies are upgraded. SDK transcript files are never rewritten for migration.

Validation:

  • Thirteen pure cases cover type/boundary/role confusion, errors, key ordering, media/cache hints, legitimate append, and legacy hashes. Initial 11-case baseline: 9 fail; two further controls added.
  • All 10 new HTTP cases fail on main and pass after, including complete revised input delivery and one-time legacy migration followed by resume.
  • Real client tool loop: main ignores the changed error flag and answers SUCCEEDED; the fix delivers the revised flag and answers FAILED. Legacy mapping replay preserves call arguments/result data and then resumes. Haiku and Sonnet × streaming/non-streaming all pass, with immutable source histories inspected using supported SDK APIs.
  • Full npm test: 3,350 pass, one existing skip, zero failures. Typecheck/build pass.
  • All four real E41 sequential/parallel × streaming/non-streaming controls pass. Both real undo-gap modes pass ordinary rollback, revised intermediate history and missing-checkpoint cases. Exact-head CI passes: Linux test/build, Windows smoke, container smoke and container build.

Fixes #887. Related to #872; this does not adopt its proposed arbitrary block-drop continuation.

@rynfar
rynfar marked this pull request as ready for review September 4, 2026 20:56
@rynfar
rynfar merged commit 5ba7c2b into main Sep 4, 2026
5 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.

Per-block lineage hashes are not type-separated: a text block can hash identically to a tool_result block

1 participant