Skip to content

release #5582: keep fork children out of compression stitch - #5625

Merged
nesquena-hermes merged 2 commits into
masterfrom
release/stage-5582
Jul 5, 2026
Merged

nesquena-hermes merged 2 commits into
masterfrom
release/stage-5582

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Release: #5582 — keep fork children out of compression stitch (#5571)

Contributor @rodboev; gate-pass. Rebuilt on current master, fresh-gated.

The bug (#5571)

_webui_sidecar_lineage_messages_for_display stitches a session's pre-compression snapshot parents for full-transcript display. A cleared/compressed fork child could resurrect its original non-fork parent's transcript once that parent was later compressed.

The fix

When the root session is itself a fork (session_source=='fork'), the parent-walk now allows fork-sourced snapshot parents but stops at the first non-fork ancestor. So a compressed fork continuation still shows its own pre-compression turns, while an ordinary fork child stays isolated from the original parent. Additive guard (one extra break condition); non-fork lineage display is byte-identical.

Gate (green)

  • Codex (data-display scrutiny): SAFE TO SHIP — non-fork stitch unchanged (new condition never fires when root_is_fork is false); no legitimate fork-sourced turns dropped; the legacy missing-session_source edge falls back to prior behavior (not a regression); no seen-set/loop issue. Display-only path, no writes.
  • Full suite: 12110 passed. Own tests: 18/18 (incl. the original fork-with-later-snapshot regression + a new multiply-compressed-fork regression).

Attribution: original author @rodboev (Co-authored-by preserved).

Closes #5582
Closes #5571

_webui_sidecar_lineage_messages_for_display stitches a session's pre-compression
snapshot parents for full-transcript display. A cleared fork child could resurrect
its ORIGINAL (non-fork) parent's transcript when that parent was later compressed.
Now, when the root session is itself a fork (session_source=='fork'), the stitch
walk allows fork-sourced snapshot parents but stops at the first non-fork
ancestor — so a compressed fork continuation still shows its own pre-compression
turns, while an ordinary fork child stays isolated from the original parent.
Additive guard (one extra break condition); non-fork lineage display unchanged.

Contributor stage; gate-pass. Co-authored-by: rodboev <rodboev@users.noreply.github.com>
@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This release PR patches a display bug where clearing and viewing a forked conversation could resurrect transcript messages from the original (non-fork) conversation once that original was later compressed. The fix adds a two-line guard to _webui_sidecar_lineage_messages_for_display that, when the root session is itself a fork, stops the snapshot parent-walk at the first non-fork ancestor.

  • api/routes.py: Captures root_is_fork from the root session's session_source before the parent-walk loop, then breaks if a fork root reaches a non-fork snapshot parent — leaving non-fork lineage logic byte-identical.
  • tests/test_session_lineage_full_transcript.py: Adds four tests: the core fork-isolation case, a multiply-compressed-fork chain that should still stitch its own fork-sourced snapshots, and two regression tests that confirm the pre-existing isolation guards in _merged_webui_lineage_messages_for_display are undisturbed.

Confidence Score: 5/5

Safe to merge — display-only path, no writes, non-fork transcripts are completely unaffected, and the new guard is thoroughly exercised by 18 dedicated tests.

The change is a minimal additive guard (two new lines + one break) in a read-only display helper. The root_is_fork flag is false for all non-fork sessions, so the new code path is entirely inert for the common case. The multiply-compressed-fork test confirms legitimate fork-sourced snapshot chains still stitch correctly, and the isolation test confirms the original bug scenario is blocked. All 12,110 suite tests pass.

No files require special attention.

Important Files Changed

Filename Overview
api/routes.py Two lines + one break added to _webui_sidecar_lineage_messages_for_display; non-fork path is entirely unchanged, and the guard is safely gated behind root_is_fork.
tests/test_session_lineage_full_transcript.py Four new tests added: two cover the fixed function directly (fork-with-snapshot-parent isolation, multiply-compressed-fork stitching), and two are regression guards for the already-correct _merged_webui_lineage_messages_for_display isolation path.
CHANGELOG.md Release changelog entry prepended; accurate description of the fix and attribution.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Start: root session] --> B[Read session_source\nroot_is_fork = source == 'fork']
    B --> C{Loop: next parent_id}
    C -->|no parent_id / seen / unsafe| DONE[Return merged segments]
    C --> D[Session.load parent_id]
    D -->|not found or\nnot pre_compression_snapshot| DONE
    D --> E{root_is_fork AND\nparent_source != 'fork'?}
    E -->|Yes| DONE
    E -->|No| F{segments empty AND\nmessages already have prefix?}
    F -->|Yes| EARLY[Return session_messages as-is]
    F -->|No| G[Append parent to segments\ncurrent = parent]
    G --> C
    DONE --> H{segments empty?}
    H -->|Yes| I[Return session.messages]
    H -->|No| J[Reverse-merge all segments\n+ append session messages]
    J --> K[Return stitched transcript]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Start: root session] --> B[Read session_source\nroot_is_fork = source == 'fork']
    B --> C{Loop: next parent_id}
    C -->|no parent_id / seen / unsafe| DONE[Return merged segments]
    C --> D[Session.load parent_id]
    D -->|not found or\nnot pre_compression_snapshot| DONE
    D --> E{root_is_fork AND\nparent_source != 'fork'?}
    E -->|Yes| DONE
    E -->|No| F{segments empty AND\nmessages already have prefix?}
    F -->|Yes| EARLY[Return session_messages as-is]
    F -->|No| G[Append parent to segments\ncurrent = parent]
    G --> C
    DONE --> H{segments empty?}
    H -->|Yes| I[Return session.messages]
    H -->|No| J[Reverse-merge all segments\n+ append session messages]
    J --> K[Return stitched transcript]
Loading

Reviews (1): Last reviewed commit: "release #5582: keep fork children out of..." | Re-trigger Greptile

@nesquena-hermes
nesquena-hermes merged commit c922f17 into master Jul 5, 2026
18 checks passed
@nesquena-hermes
nesquena-hermes deleted the release/stage-5582 branch July 5, 2026 08:20
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.

Compression stitch should skip fork children (session_source==fork) to avoid post-clear resurrection corner

1 participant