Skip to content

tui: live message window can re-root mid-session - earlier turns unreachable, no seam summary, no lifecycle event #7142

Open

Description

@vanderberghenry-oss

Landed setting fix — 2026-10-29

Engine main/origin commit 7b54e7f fixes the concrete automatic-off bypass. Preflight/provider overflow recovery now refuses before summary/start/replacement when compaction is disabled, retains original messages and records an actionable typed refusal through the existing audit/error owners. Both overflow paths preserve the recovery failure's actual cause; cancellation takes priority after the awaited recovery. Explicit manual compaction remains available.

Local proof: initial libtest compile and137focused compaction/error/Engine checks passed. After the final model-step cancellation guard, libtest recompiled and69affected Engine/recovery/cancellation checks passed; unchanged pure-check evidence is retained. The renamed existing emergency fixture covers disabled-but-compactable and enabled-but-unnecessary history, zero model calls/attempts, unchanged messages and one typed refusal. Format, boundary, blocking budget569sites169files, dead-code164, changelog/facts and diff checks pass. No new test functions/comments. Exact receipts: artifacts/codex-0103-resume-20261029/compaction-off-landing-verification.json. Source files are released, not held for another build.

Keep this issue and public Engine7142 open. The setting bypass is established and repaired, but it does not explain every reported Windows seam or complete the remaining lifecycle/continuity acceptance. A read-only local Mac journal inspection did not reproduce the reported multi-root shape; that does not prove no context loss or disprove the reporter's sample. No real-provider, hosted CI, Windows/installed/device or release proof is claimed.


Summary

A session's live messages window can be re-rooted partway through a conversation. When
that happens, every journal entry before the new root becomes unreachable to the model
while the on-disk journal keeps it in full. The window stays internally consistent — it
equals its own new branch exactly — so nothing looks truncated from the inside.

Three things are missing at the moment it happens:

  • no summary entry at the seam. The new root is a bare kind: "message" with
    parent_id: null; the entry immediately before it is an ordinary turn on the branch
    that was just abandoned. Real compaction leaves a condensed account of what it replaced.
    This leaves a copy and a hole.
  • no marker in the interface. No banner, no notice, no error.
  • no session-lifecycle event in audit.log. In 8,792 events across five event types,
    every one is tool.approval.* or tool.gate.decision. Nothing announces that the model's
    working context was replaced.

settings.toml has auto_compact = false and auto_compact_threshold_percent = 80.0
throughout. If this is compaction, the setting is not describing what the runtime does.

Environment

  • codewhale --version: 0.10.2 (ff466b2) — verified against the binary
  • OS: Windows 11 (10.0.26200), PowerShell 5.1
  • Model: deepseek-v4-pro (provider deepseek)
  • Install: npm global; ~/.codewhale/{config,settings,permissions}.toml, no [compaction]
    block and no retained_user_message_tokens in config.toml
  • Reproduces on 0.10.1 and 0.10.2

What it looks like

Dump of four consecutive documents from this host. entries = journal.entries;
window = len(messages); root = the journal index the live branch starts at.

created (UTC)        window  entries  root   coverage
2026-10-28T13:08:15     944      986     1      99.9%   <- intact (benign doubled opening)
2026-10-28T15:24:44     632     1639   987      39.8%   <- re-rooted
2026-10-29T07:11:22     403      473    50      89.4%   <- re-rooted
2026-10-29T08:20:46      61      325   259      20.3%   <- re-rooted (this one, mid-report)

The seam itself, from 2026-10-29T07:11:22:

[48] kind=message  role=assistant  parent_id=Y     3970 chars
[49] kind=message  role=user       parent_id=Y     1362 chars
[50] kind=message  role=user       parent_id=NONE  2054 chars   <- window begins here
[51] kind=message  role=assistant  parent_id=Y      360 chars

Prose for scale: the journal for that session holds 473 entries; the model could see 403
of them. The boot turn, including the whole startup sequence, was gone from the window and
present on disk.

Prevalence on this host, and an onset date

87 session documents were parsed structurally. journal + leaf_id on this host arrives
between 11 and 12 Aug 2026 (before that, documents carry artifacts and no journal, so this
signature cannot exist and cannot be tested — that period is untestable, not clean).

period sessions shape
12 Aug – 17 Oct 2026 47 one branch root, window == journal, 100% reachable
18 Oct – 29 Oct 2026 40 two or three roots; 15 re-rooted mid-session

The onset boundary is narrow: …d6832ea3 (17 Oct 18:33:49Z) is clean at 113/113;
…021a7d6a (18 Oct 01:46:51Z) is the first re-root at 962 window / 1045 entries.

Across the 15 mid-session re-roots, the root lands at journal indices 8, 20, 30, 50, 67, 97,
160, 259, 295, 347, 367, 472, 733, 987. Post-re-root coverage ranges from 14.1% to
89.4%; combined unreachable entries 3,909.

In 0 of 87 sessions is the window shorter than its own branch. This is not truncation —
the window is always a complete branch. The branch is simply no longer the session.

Reproduction

No special configuration; it appears on ordinary long sessions. A read-only probe against
any session document:

import json, hashlib

doc = json.load(open(PATH, encoding="utf-8"))
msgs, entries = doc["messages"], doc["journal"]["entries"]

by_id = {e["id"]: i for i, e in enumerate(entries) if e.get("id")}
chain, seen, cur = [], set(), len(entries) - 1
while cur is not None and cur not in seen:
    seen.add(cur); chain.append(cur)
    cur = by_id.get(entries[cur].get("parent_id"))

root = min(chain)
print("window", len(msgs), "branch", len(chain),
      "root", root, "coverage %.1f%%" % (100 * (len(entries) - root) / len(entries)))
  • window == branch and root <= 1 → healthy (a root at 1 is the benign doubled opening;
    do not flag it — see What I got wrong, below).
  • root > 1 → the window re-rooted mid-session. root entries are unreachable.

To inspect a seam, print entries[root-2 : root+2] and check for anything summary-shaped.
On this host there is never one.

How this differs from the nearby open reports

  • #6842 (journal has no bound; compaction retires live messages but keeps superseded
    versions in RAM) names the same underlying machinery and is about memory growth. This
    report is about model visibility: the earlier turns were not merely retained
    redundantly, they became unreachable while the record survived. Different harm, possibly
    the same call site.
  • Automatic Compression Context Exception #7133 (automatic compression at ~40% against an 80% setting) is the closest
    behavioural neighbour and may share a cause — it reports the threshold being wrong,
    where this reports a silent re-root with no summary. Worth triaging together.
  • Conversation history is lost. #7095 (history lost, labelled v0.10.3) is a different fault: there the record does not
    survive because the last save did not flush. Here every byte survives and is correct, and
    only the window is replaced.

What I am asking for

In order of value, and each is independently useful:

  1. Emit a session-lifecycle event. session.opened, session.window.rerooted,
    session.compacted, session.resumed — with both sizes and a reason, into audit.log
    beside the existing tool.* events. This is the smallest change and it converts an
    unexplained amnesia into a dated, attributable event. It is worth having even if the
    re-root turns out to be intended, so please do not let the other two asks gate it.
  2. Honour auto_compact = false, or rename it. A setting that says compaction is off
    while the window is replaced is worse than no setting: it is a claim the operator can
    rely on and cannot verify.
  3. If a window re-root is intended, summarise at the seam. A bounded, visible,
    summarised compaction is a feature. An unannounced re-root that leaves a bare copy of an
    existing message is a data-visibility defect.

What I have not established, and will not guess at

The trigger. Observed re-roots span 78 to 1639 journal entries, 11 to 1437 window
messages, and roughly 1.3 MB to 5 MB of document. That spread is inconsistent with a single
simple threshold, and two documents is not a sample. It could be turn count, cumulative
tokens, document size, wall clock, or a path that only runs on resume.

Whether it is intended. #5262 (closed, shipped v0.9.5) specifies an append-only journal
forest where branching "only moves the leaf — it never rewrites history", so multiple roots
are expected by that design and a re-root is legal in that model. What is not
design-legible is doing it silently, mid-session, with no summary, while
auto_compact = false.

What I got wrong on the way here

Recorded because it is the reason this report is careful, and because a maintainer deserves
to know which tests have already failed.

My first pass used a positional test — does messages[0] start where journal.entries[0]
starts — and produced two confident wrong answers in one morning: first "41 of 93 sessions
lost turns", then, over-correcting, "nothing was lost at all". The positional test fires on
the benign doubled opening, which is why I checked a session minutes old and never restarted
before trusting any number here. The figures above come from a membership test: walk the
branch by parent_id and ask whether the window can reach every entry its own session wrote.

If this report is wrong, it is wrong in the direction of under-claiming: 15 measured re-roots
on one host, with the mechanism named and the unknowns listed.


Reproduction and evidence kept locally. Happy to provide sanitised session documents or run
any probe upstream asks for.

Activity

  1. added
    needs-triageNew external report awaiting maintainer triage; repro, logs and version output help
    on Oct 29, 2026
  2. added this to the v0.10.3 milestone on Oct 29, 2026
  3. added
    bugSomething isn't working
    and removed
    needs-triageNew external report awaiting maintainer triage; repro, logs and version output help
    on Oct 29, 2026
  4. Hmbown commented on Oct 29, 2026

    @Hmbown
    Collaborator

    Thank you for this!!

  5. vanderberghenry-oss commented on Oct 29, 2026

    @vanderberghenry-oss
    Author

    You're very welcome! Codewhale is my lead engineer since May and ships like a king. Never had any issues till this happened in the last couple of weeks. My memory system is comprehensive - so it wasn't a train crash. I love working with codewhale. My compliments

    Have good one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv0.10.3

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions