You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Forking an OpenCode session leaves the parent's compaction marker rows in the fork. The fork then injects a fresh marker behind the orphan, OpenCode's filterCompacted stops at the orphan, and the transform enters degraded mode permanently. Context usage stops shrinking.
What happened?
Session: long-running OpenCode session forked via /fork. After the fork, context usage climbed past 96% of the model window and stayed there. The magic-context log shows degraded mode on every transform pass, zero drops queued, and an emergency drop reclaiming 649 of 104,534 tokens.
Two compaction markers exist in the fork's opencode.db:
Orphan, copied from the parent by the fork. Part prt_fb0ce9a48001wzzdZ5Oyw1kVwr on message msg_fb0ce9a47001Iwu6O1agRSyFBh, boundary at position 7787 of 8185 messages (July 29).
Fresh, injected by the marker manager after the fork. Part prt_ec5fc3dbc001tzNHOw4Gl9ouuf on message msg_fb0cab924001eGVSidtNIHnFst, persisted boundaryOrdinal 1186 (June 14).
filterCompacted (message-v2.ts:521) breaks iteration at the newest marker, so the transform receives only messages after position 7787. The injection logic looks for its boundary message msg_fb0cab953001SLJx4ZG6LA7jiO (June 14, ordinal 1186). It is not in the visible window. inject-compartments.ts logs compartment injection entering degraded mode: boundary ... not in visible messages, skips the splice, and queues zero drops. Context usage stays near the ceiling, the model provider starts throwing stream errors ("socket closed unexpectedly", "Service is too busy") at the ceiling, and the emergency recovery keeps spawning historian runs that fail.
Why the existing safety net does not catch it
compaction-marker-manager.ts documents orphaned rows as harmless because "the next historian publication moves the boundary forward and the new injection replaces the orphans by moving filterCompacted past them." That assumption breaks for forks: the fork has no persisted marker state, so the historian restarts from message 1 and the marker manager injects at the first compartment boundary it publishes, which is far behind the orphan. At roughly 100-150 messages per publication, catching up ~6,600 messages to pass the orphan takes dozens of publications. Until then, degraded mode repeats on every transform pass with no recovery path.
The startup consistency check (compaction-marker-manager.ts:534+) only verifies that the persisted marker's rows exist in opencode.db. It never scans for other compaction parts in the session, so the orphan is invisible to it.
Related context
PARITY.md gap Per model execute threshold not respected #25: "OpenCode /fork does not yet [inherit session state]. OpenCode re-mints message ids during /fork, making entry-id-keyed migration unsafe there. OpenCode fork inheritance therefore needs a separate future design based on a stable cross-fork identity."
Issue If I fork a session, the context will reach over 100% #83 (closed as unsupported): "If I fork a session, the context will reach over 100%". This report covers the same root cause with a more precise failure mode: the orphan marker does not just fail to reduce context, it actively overrides the injected marker and disables drops until the historian catches up.
Expected behavior
After a fork, the new session should not carry marker rows that outrank its own injected marker. Either the marker manager should re-anchor to the newest marker already present in opencode.db instead of injecting behind it, or the consistency check should scan opencode.db for compaction parts not referenced by persisted state and reconcile them.
Short description
Forking an OpenCode session leaves the parent's compaction marker rows in the fork. The fork then injects a fresh marker behind the orphan, OpenCode's
filterCompactedstops at the orphan, and the transform enters degraded mode permanently. Context usage stops shrinking.What happened?
Session: long-running OpenCode session forked via
/fork. After the fork, context usage climbed past 96% of the model window and stayed there. The magic-context log shows degraded mode on every transform pass, zero drops queued, and an emergency drop reclaiming 649 of 104,534 tokens.Two compaction markers exist in the fork's opencode.db:
prt_fb0ce9a48001wzzdZ5Oyw1kVwron messagemsg_fb0ce9a47001Iwu6O1agRSyFBh, boundary at position 7787 of 8185 messages (July 29).prt_ec5fc3dbc001tzNHOw4Gl9ouufon messagemsg_fb0cab924001eGVSidtNIHnFst, persisted boundaryOrdinal 1186 (June 14).filterCompacted(message-v2.ts:521) breaks iteration at the newest marker, so the transform receives only messages after position 7787. The injection logic looks for its boundary messagemsg_fb0cab953001SLJx4ZG6LA7jiO(June 14, ordinal 1186). It is not in the visible window. inject-compartments.ts logscompartment injection entering degraded mode: boundary ... not in visible messages, skips the splice, and queues zero drops. Context usage stays near the ceiling, the model provider starts throwing stream errors ("socket closed unexpectedly", "Service is too busy") at the ceiling, and the emergency recovery keeps spawning historian runs that fail.Why the existing safety net does not catch it
compaction-marker-manager.ts documents orphaned rows as harmless because "the next historian publication moves the boundary forward and the new injection replaces the orphans by moving filterCompacted past them." That assumption breaks for forks: the fork has no persisted marker state, so the historian restarts from message 1 and the marker manager injects at the first compartment boundary it publishes, which is far behind the orphan. At roughly 100-150 messages per publication, catching up ~6,600 messages to pass the orphan takes dozens of publications. Until then, degraded mode repeats on every transform pass with no recovery path.
The startup consistency check (compaction-marker-manager.ts:534+) only verifies that the persisted marker's rows exist in opencode.db. It never scans for other compaction parts in the session, so the orphan is invisible to it.
Related context
/forkdoes not yet [inherit session state]. OpenCode re-mints message ids during/fork, making entry-id-keyed migration unsafe there. OpenCode fork inheritance therefore needs a separate future design based on a stable cross-fork identity."Expected behavior
After a fork, the new session should not carry marker rows that outrank its own injected marker. Either the marker manager should re-anchor to the newest marker already present in opencode.db instead of injecting behind it, or the consistency check should scan opencode.db for compaction parts not referenced by persisted state and reconcile them.
Environment