Skip to content

fix(transcript): keep derived history record identities unique - #11783

Merged
esengine merged 1 commit into
esengine:main-v2from
aron-intframe:fix/10456-derived-record-identity
Oct 4, 2026
Merged

esengine merged 1 commit into
esengine:main-v2from
aron-intframe:fix/10456-derived-record-identity

Conversation

@aron-intframe

Copy link
Copy Markdown

Summary

stampHistoryRows derives its last-resort record address as m:<messageID>:notice:<i>. Interrupted-attempt records are persisted without a message id, so two of them both derive m::notice:0, NewProjection rejects the whole baseline, and the session cannot be opened at all — including after a restart.

Disambiguated with a used set, the way the sibling repairCheckpointRecordIdentities already does. Canonical m:/tool: addresses are untouched, so genuine duplicates are still rejected.

Issues

Refs #10456 — I could not reproduce that report's exact identity (tool:call_00_…), only the derived m::notice:N collision, so Refs rather than Fixes.

Verification

go test ./internal/transcript/ green; gofmt -l and go vet clean. Two new tests, both built from the production constructor agent.InterruptedStreamRecord:

  • TestHistoryKeepsDerivedRecordIdentitiesUnique — reverting the fix fails it with rows 1 and 3 share record identity "m::notice:0".
  • TestHistoryKeepsCanonicalDuplicatesRejected — inverse guard; tool:reused-call twice must still fail.

Linux, root module only. Two failures elsewhere (control TestServeForkStartsNoBroaderThanInheritedReadOnly, agent TestRepairSessionListingProjectionHealsRecoveryLedgerOnce) also fail on a clean main-v2 with this change stashed.

Documentation impact

Documentation-impact: none - no user-visible behavior change.

Cache impact

Cache-impact: none - display projection only.
Cache-guard: n/a; internal/transcript records never enter provider messages.
System-prompt-review: N/A

Interrupted-attempt records are persisted without a message id, so
stampHistoryRows derived "m::notice:0" for every one of them and
NewProjection rejected the whole baseline, leaving the session
unopenable across restarts. Disambiguate the derived address the same
way repairCheckpointRecordIdentities already does. Canonical duplicates
are untouched and still rejected.

Refs esengine#10456
@github-actions github-actions Bot added the v2 Reasonix 1.x (Go) — main-v2 branch, maintenance / stable label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Thanks, this is a clean fix. I merged it onto current main-v2: the transcript tests pass, reverting the production change fails the new test with the shared m::notice:0 identity you describe, and the inverse test keeping real duplicate canonical identities rejected is the right guard. One small nit: the doc comment on stampHistoryRows is longer than our 5-line declaration limit; keeping just the constraint (only the derived address is disambiguated, canonical addresses still fail the projection) is enough. Otherwise this is good to merge.

@esengine
esengine merged commit 3b4319c into esengine:main-v2 Oct 4, 2026
57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Reasonix 1.x (Go) — main-v2 branch, maintenance / stable

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants