Skip to content

fix(agents): dedupe transcript rewrite replay#79150

Closed
LambertArm wants to merge 1 commit into
openclaw:mainfrom
LambertArm:fix/transcript-rewrite-replay-dedupe-69208
Closed

fix(agents): dedupe transcript rewrite replay#79150
LambertArm wants to merge 1 commit into
openclaw:mainfrom
LambertArm:fix/transcript-rewrite-replay-dedupe-69208

Conversation

@LambertArm

@LambertArm LambertArm commented May 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Real behavior proof (required for external PRs)

External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only. Screenshots are encouraged even for CLI, console, text, or log changes; terminal screenshots and copied live output count. Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.

  • Behavior or issue addressed: transcript rewrite replay no longer persists exact duplicate user payloads or repeated bootstrap context entries when rewriting a polluted active branch.
  • Real environment tested: local OpenClaw checkout on Linux, Node 24.15.0 via repo pnpm exec tsx, branch based on upstream/main at a1ac559ed7.
  • Exact steps or command run after this patch: ran a local OpenClaw runtime script that creates a real persisted SessionManager JSONL file, appends three identical user payloads plus two duplicate openclaw:bootstrap-context:full custom entries, calls the production rewriteTranscriptEntriesInSessionFile(...) function, reopens the session file, and prints the active branch counts.
  • Evidence after fix (copied terminal output from the local OpenClaw runtime script):
before branch roles=user,user,user,assistant
before user_count=3
before bootstrap_count=2
[agent/embedded] [transcript-rewrite] rewrote 1 entry bytesFreed=0 sessionKey=agent:main:main:heartbeat
rewrite changed=true rewrittenEntries=1
after branch roles=user,assistant
after user_count=1
after bootstrap_count=1
raw_jsonl_lines=10
  • Observed result after fix: after reopening the persisted session file, the active branch contains only one user payload and one bootstrap context entry instead of the three user payloads and two bootstrap entries that existed before rewrite.
  • What was not tested: a full end-to-end forced provider context-overflow run was not triggered locally; the runtime script exercises the same transcript rewrite/file-state path called by tool-result truncation recovery.
  • Before evidence (optional but encouraged): the same script prints before user_count=3 and before bootstrap_count=2 immediately before invoking the rewrite path.

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: rewriteTranscriptEntriesInSessionManager and rewriteTranscriptEntriesInState branched from the first replaced message and then appended every suffix entry without an idempotency invariant.
  • Missing detection / guardrail: there was no regression coverage for rewriting a branch already polluted with exact replayed user messages, repeated bootstrap custom entries, or cloned compactions.
  • Contributing context (if known): overflow recovery uses this rewrite path after building tool-result replacement plans, so a polluted suffix could be faithfully replayed into the new active branch.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/agents/pi-embedded-runner/transcript-rewrite.test.ts
  • Scenario the test should lock in: exact replayed user payloads collapse during in-memory and persisted file rewrites, repeated same text with distinct message timestamps remains intact, and replayed bootstrap/compaction clones collapse.
  • Why this is the smallest reliable guardrail: it exercises the shared transcript rewrite seam directly, including the file-state path used for persisted sessions, without requiring a slow provider overflow setup.
  • Existing test that already covers this (if any): existing tests covered suffix replay and remapping, but not polluted suffix dedupe.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Overflow-recovery transcript rewrites should stop amplifying exact replayed transcript records in session JSONL. Normal repeated user text with distinct message timestamps remains preserved.

Diagram (if applicable)

Before:
[polluted branch] -> [rewrite first replacement] -> [append every suffix entry] -> [duplicates stay active]

After:
[polluted branch] -> [rewrite first replacement] -> [append first replay identity only] -> [active branch is compacted]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: Node 22/24 through repo pnpm wrappers
  • Model/provider: N/A for targeted transcript rewrite regression
  • Integration/channel (if any): Agent transcript persistence / overflow recovery seam
  • Relevant config (redacted): default local test config

Steps

  1. Build an active transcript branch with three exact copies of the same user AgentMessage payload.
  2. Rewrite the first message through rewriteTranscriptEntriesInSessionManager and rewriteTranscriptEntriesInSessionFile.
  3. Re-open/read the active branch and count visible message roles.

Expected

  • The active branch contains one user message followed by the assistant tail.
  • Same user text with a different message timestamp is preserved as a distinct turn.
  • Duplicate bootstrap custom entries and cloned compactions collapse during replay.

Actual

  • Matches expected after this patch.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: local persisted-session runtime script, targeted transcript rewrite regression tests, persisted file-state rewrite path, production core typecheck, test typecheck, formatter, and whitespace check.
  • Edge cases checked: repeated user text with distinct message timestamps is not collapsed.
  • What you did not verify: full live provider overflow recovery with a real model context overflow.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: overly broad dedupe could remove legitimate repeated turns.
    • Mitigation: message replay identity hashes the full AgentMessage payload, so repeated same text with different timestamps remains a distinct turn; a regression test locks this in.
  • Risk: skipped duplicate entries might break later remaps.
    • Mitigation: skipped entry ids map to the first emitted equivalent id, preserving downstream label/compaction remapping.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. size: M labels May 8, 2026
@clawsweeper

clawsweeper Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR adds replay-identity deduplication to transcript rewrite suffix replay, regression tests for duplicate user/bootstrap/compaction entries, and a changelog note.

Reproducibility: yes. The linked issue provides persisted JSONL/log evidence, and current-main source tracing shows overflow recovery reaches unguarded suffix replay; I did not run a live overflow in this read-only review.

Real behavior proof
Sufficient (terminal): The PR body includes copied terminal output from an after-fix local runtime script using a real persisted SessionManager JSONL file and the production rewrite function.

Next step before merge
A narrow automated repair can update the compaction replay identity and add missing regression coverage before maintainer review.

Security
Cleared: The diff changes transcript rewrite logic, tests, and changelog text, and adds no dependency, permission, secret, network, workflow, or command-execution surface.

Review findings

  • [P2] Include the kept boundary in compaction identity — src/agents/pi-embedded-runner/transcript-rewrite.ts:42-46
Review details

Best possible solution:

Land a current-base transcript rewrite idempotency fix that dedupes replay clones while preserving distinct compaction kept-entry boundaries, then close the concrete Track A issue and leave the broader umbrella open.

Do we have a high-confidence way to reproduce the issue?

Yes. The linked issue provides persisted JSONL/log evidence, and current-main source tracing shows overflow recovery reaches unguarded suffix replay; I did not run a live overflow in this read-only review.

Is this the best way to solve the issue?

No, not as written. The central rewrite replay boundary is the right place to fix this, but compaction identity should include the remapped firstKeptEntryId so distinct compaction boundaries are not collapsed.

Full review comments:

  • [P2] Include the kept boundary in compaction identity — src/agents/pi-embedded-runner/transcript-rewrite.ts:42-46
    The new compaction replay key ignores firstKeptEntryId. buildSessionContext() uses the latest compaction's kept-entry boundary to decide which pre-compaction messages remain in context, so two legitimate compactions with identical summaries/details/tokens but different kept boundaries would collapse to the earlier entry during rewrite and lose the newer boundary. Include the remapped kept id in the identity so cloned entries still dedupe without merging distinct compactions.
    Confidence: 0.85

Overall correctness: patch is incorrect
Overall confidence: 0.86

Acceptance criteria:

  • node scripts/run-vitest.mjs src/agents/pi-embedded-runner/transcript-rewrite.test.ts
  • node scripts/crabbox-wrapper.mjs run --label transcript-rewrite-dedupe --shell -- "pnpm check:changed"

What I checked:

  • PR diff adds replay identities: The PR adds computeBranchEntryReplayIdentity and applies emittedReplayIdentities in both rewriteTranscriptEntriesInSessionManager and rewriteTranscriptEntriesInState; the compaction identity hashes tokensBefore, summary, details, and fromHook, but not firstKeptEntryId. (src/agents/pi-embedded-runner/transcript-rewrite.ts:42, ee0a371f0fad)
  • Current main still has unguarded suffix replay: Current main branches from the first matched entry and appends each suffix entry without a replay identity guard, so the underlying bug is not implemented on main. (src/agents/pi-embedded-runner/transcript-rewrite.ts:230, ea16a5e9e10c)
  • Overflow recovery reaches rewrite path: Tool-result truncation calls rewriteTranscriptEntriesInSessionManager for in-memory sessions and rewriteTranscriptEntriesInState for persisted file-state recovery, matching the linked overflow-recovery report. (src/agents/pi-embedded-runner/tool-result-truncation.ts:657, ea16a5e9e10c)
  • Dependency compaction contract: pi-coding-agent v0.74.0 buildSessionContext selects the latest compaction and uses compaction.firstKeptEntryId to decide which pre-compaction entries remain in context. (packages/coding-agent/src/core/session-manager.ts:390, 1eee081e29c1)
  • Linked issue evidence: The closing issue includes persisted JSONL/log evidence for duplicate role=user rows, cloned compactions, and repeated bootstrap custom records; maintainer discussion keeps it as the concrete Track A issue under the duplicate-transcript umbrella.
  • Related PR context: The prior similar PR was closed as superseded by this PR, and the overlapping abandoned-entry cleanup PR remains open but covers a different cleanup invariant rather than this exact replay-identity fix. (579a2172af81)

Likely related people:

  • jalehman: Commit 751d5b7 introduced the context-engine transcript maintenance surface, including transcript rewrite and related maintenance tests. (role: introduced behavior; confidence: high; commits: 751d5b7849ca; files: src/agents/pi-embedded-runner/transcript-rewrite.ts, src/agents/pi-embedded-runner/context-engine-maintenance.ts, src/agents/pi-embedded-runner/tool-result-truncation.ts)
  • steipete: Recent commits maintained transcript file-state rewrites, session write-lock handling, and tool-result overflow recovery on the same path. (role: recent area contributor; confidence: high; commits: f7fe6ad55eb0, f7ed29e11812, a42ee69ad4a7; files: src/agents/pi-embedded-runner/transcript-rewrite.ts, src/agents/pi-embedded-runner/transcript-file-state.ts, src/agents/pi-embedded-runner/tool-result-truncation.ts)
  • Takhoffman: Commit history shows focused aggregate tool-result truncation work in the overflow recovery path that invokes transcript rewrite. (role: adjacent overflow recovery contributor; confidence: medium; commits: 4917009ac7e4; files: src/agents/pi-embedded-runner/tool-result-truncation.ts)
  • sercada: Current-main blame shows the transcript rewrite file was reintroduced in commit 4725233 during recent mainline work, although the behavior predates that commit. (role: recent file contributor; confidence: low; commits: 472523360d88; files: src/agents/pi-embedded-runner/transcript-rewrite.ts)

Remaining risk / open question:

  • The PR head is currently conflicting with current main, so final validation must run after rebase or a replacement branch.
  • This read-only review did not run tests; the verdict is based on source inspection, dependency source, issue evidence, and the contributor's copied runtime proof.

Codex review notes: model gpt-5.5, reasoning high; reviewed against ea16a5e9e10c.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels May 8, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 8, 2026
@LambertArm LambertArm closed this by deleting the head repository May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overflow recovery duplicates role=user messages in session JSONL, amplifying transcript growth

1 participant