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
{{ message }}
Repository navigation
After leaving Rust mode on OpenCode 1, the TS historian refuses forever: "gap before message N" on synthetic-only user rows the module skipped #781
Versions: plugin 0.49.22 on OpenCode 1. The code paths cited below are unchanged on main (3a6d3cb90).
What happens: a session ran in Rust mode for weeks. During that time a plugin injected synthetic: true user text messages into it (a cache-warm ping, one text part, about 56 chars). The module's compartments do not cover those rows: each one sits alone between two compartments.
After the project switched back to TS mode, every historian run fails before any model call:
historian failure: source=existing-validation reason="gap before message 3387 (expected 3386)"
It failed 66 times in 8 hours, and the session got no new compartments. The stored compartments had four such one-message gaps (3386, 3770, 4180, 4220). Each gap message was a user row whose only part was a text part with synthetic: true.
Why it never recovers (main):
validateStoredCompartments (compartment-runner-validation.ts:302-321) accepts a gap only if it is listed in nonNarrativeGapRanges.
v2NonNarrativeStoredGapRanges (compartment-runner-incremental.ts:143) returns [] unless the session's rebase generation is v2, so on OpenCode 1 no gap is ever accepted.
isStrictGapHealingMessage (read-session-raw.ts:20) would also reject these rows: a V1 row has no storeType: "synthetic", and a text part with content returns false, even with synthetic: true on it.
The repair and backoff around existingValidationError (compartment-runner-incremental.ts:394-430) apply only when an unresolved compartment is involved. This failure therefore repeats on every trigger and has no repair path.
Workaround (the same shape as the one confirmed on #561): for each gap, set the next compartment's start_message back by one, and its start_message_id to the gap message. The next run published normally.
Suggested fix:
Treat a V1 user row whose parts are all synthetic: true text as gap-healing, as storeType: "synthetic" already is on V2.
Accept such gaps on OpenCode 1 too, not only after a v2 rebase.
Alternatively, heal the gap by extending the next compartment, as the workaround does.
Either way, back off this failure class like the unresolved case, so it doesn't re-run on every trigger.
Related: #561, where a compaction marker on a synthetic user row wedged a session in Rust mode.
Thanks for the precise trace. I confirmed it on current code. Our own sessions have the same shape: one session that ran in Rust mode has 32 one-message gaps between its compartments, and the ones I checked are each a single user row whose only part is a synthetic: true text notice from another plugin. On OpenCode 1, v2NonNarrativeStoredGapRanges returns nothing outside a v2 rebase, so after a switch back to TypeScript mode the historian refuses on every trigger with no repair path, exactly as you describe.
This blocks a switch we're about to make ourselves, so it's being fixed now. The direction:
the TypeScript validator accepts exactly the rows the Rust module skipped, and no wider;
this failure class backs off and is logged once, instead of re-running on every trigger;
a test switches a Rust-mode session with these gaps back to TypeScript and checks that the historian publishes and the cached prompt stays unchanged.
Your workaround is safe to keep using until it ships. We'll post here when it does.
Fixed in 0.49.24. Thanks @iceteaSA for the report and the trace.
The TypeScript historian now skips user messages made only of synthetic parts (such as background-task notices) the same way the Rust historian did. A session that left Rust transform mode no longer refuses summarization forever with "gap before message N", and it logs with backoff instead of on every trigger.
Update the plugin and restart OpenCode to pick it up.
Activity
magic-alfonso commentedon Oct 26, 2026
Thanks for the precise trace. I confirmed it on current code. Our own sessions have the same shape: one session that ran in Rust mode has 32 one-message gaps between its compartments, and the ones I checked are each a single user row whose only part is a
synthetic: truetext notice from another plugin. On OpenCode 1,v2NonNarrativeStoredGapRangesreturns nothing outside a v2 rebase, so after a switch back to TypeScript mode the historian refuses on every trigger with no repair path, exactly as you describe.This blocks a switch we're about to make ourselves, so it's being fixed now. The direction:
Your workaround is safe to keep using until it ships. We'll post here when it does.
magic-alfonso commentedon Oct 27, 2026
Fixed in 0.49.24. Thanks @iceteaSA for the report and the trace.
The TypeScript historian now skips user messages made only of synthetic parts (such as background-task notices) the same way the Rust historian did. A session that left Rust transform mode no longer refuses summarization forever with "gap before message N", and it logs with backoff instead of on every trigger.
Update the plugin and restart OpenCode to pick it up.