Skip to content

refactor(core): simplify system context into a belief model - #34917

Merged
kitlangton merged 9 commits into
v2from
context-checkpoint
Jul 2, 2026
Merged

kitlangton merged 9 commits into
v2from
context-checkpoint

Conversation

@kitlangton

@kitlangton kitlangton commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Audit-driven simplification of the V2 system context subsystem (SystemContext algebra + SessionContextEpoch persistence). The durable design is unchanged — stable baseline in the system prompt, drift narrated as chronological system messages, seq-based folding — but the implementation now follows one invariant and one mental model:

The Applied record is what the model was last told. Reconcile never rewrites the baseline; only completed compaction rebaselines, and move/committed-revert resets the checkpoint.

Changes

Algebra (system-context/index.ts)

  • ReconcileResult narrows to Unchanged | Updated; reconcile can no longer escalate to a full replacement
  • Undecodable stored values are treated as new sources (baseline re-announced as an update) instead of forcing a re-baseline
  • Sources removed without removal text silently retain the model's belief; entries self-clean at the next rebaseline
  • rebaseline (formerly replace) restates unobservable sources from their last-applied values instead of blocking; ReplacementReady/ReplacementBlocked are deleted. Only a session's first initialize still blocks on unavailability
  • Renames: Snapshot → Applied, Generation → Baseline { text, applied }

Checkpoint (session/context-checkpoint.ts, formerly context-epoch.ts)

  • One prepare entry point per turn, called before input promotion; the initialize/prepare two-step and its redundant queries are gone
  • Corrupt stored applied records self-heal (treated as empty → everything re-announces); ContextSnapshotDecodeError is deleted from RunError
  • Context update messages now precede the promoted user message, matching their actual chronology

Registry deleted

  • Builtins and instruction context become ordinary Location-scoped load() services; the runner composes all six producers explicitly in loadSystemContext. Dynamic registration/deregistration had no consumers

Fixes

  • Forks now copy the parent's checkpoint row. Previously copied messages kept parent seqs while the fork initialized a near-zero baseline_seq, so nothing folded: forks got a fresh full baseline plus every stale context-update message, and copied compaction rows triggered a spurious rebaseline
  • Committed-revert checkpoint reset now has test coverage
  • Dead SessionStore.runnerContext / SessionHistory.loadForRunner removed

Feature: delta updates

  • Skill, reference, and MCP guidance render pure additions ("new skills are available…") and pure removals ("no longer available and must not be used: …") as small deltas instead of restating the full list on every change; description/instruction edits still restate the superseding list

Compatibility

No migration and no SDK regen: the SQL table (session_context_epoch), its column names, and the session.next.context.updated wire event are unchanged.

Testing

  • packages/core: full suite green (the 6 Watcher failures reproduce identically on unmodified v2 under load and pass in isolation)
  • bun typecheck clean in core, server, cli, sdk-next

Follow-up

Planned separately:

Reconcile never rewrites the baseline; only rebaseline (compaction) and
initialize (first turn) produce baseline text. The durable Applied record
tracks what the model was last told, per source.

- Incompatible stored values re-announce the source baseline as an update
  instead of forcing a full replacement
- Sources removed without removal text retain the model's belief silently;
  entries self-clean at the next rebaseline
- Rebaseline restates unobservable sources from their last-applied values
  instead of blocking; ReplacementBlocked and ReplacementReady are gone
- Rename Snapshot to Applied and Generation to Baseline {text, applied}
One prepare call per turn, before input promotion: it creates the
checkpoint when missing, rebaselines after completed compaction, and
otherwise narrates drift as a chronological update. The initialize/
prepare two-step and its redundant queries are gone, and context update
messages now precede the promoted user message.

An undecodable stored applied record heals by treating every source as
new instead of failing the drain; ContextSnapshotDecodeError is deleted.

Renames: SessionContextEpoch -> SessionContextCheckpoint. The SQL table
name and wire event type are unchanged.
Builtins and instruction context become ordinary Location-scoped
services exposing load(), the same shape as skill, reference, and MCP
guidance. The runner composes all six producers explicitly in
loadSystemContext, so the full context roster is readable at one call
site. Dynamic registration and scoped deregistration had no consumers.
Copied fork messages keep their parent seqs, but a fresh checkpoint's
baseline_seq starts near zero, so nothing folded: forks received a new
full baseline plus every stale context-update message from the parent,
and copied compaction rows triggered a spurious rebaseline. Inheriting
the parent row keeps the fold horizon consistent with the copied
transcript.

Also asserts the checkpoint reset on committed reverts and deletes the
unused SessionStore.runnerContext / SessionHistory.loadForRunner
surface.
Pure additions announce only the new entries and pure removals name
what is gone, instead of restating the full list on every change.
Description or instruction edits still restate the superseding list.
Skill, reference, and MCP guidance shared the same keyed three-way diff
mechanics; each keeps only its own changed comparator and phrasing.
The required Provider.Request settings field and tsgo's literal
narrowing broke typecheck in llm and core; CI missed it because turbo
replayed cached typecheck results for packages whose own sources did
not change.

- anthropic-messages: lower adaptive thinking display with explicit
  literal branches so tsgo keeps the union type
- runner model: annotate providerOptions with its index-signature
  return type
- tests: construct provider requests with the now-required settings
  field and copy them mutably for plugin drafts
@kitlangton
kitlangton merged commit 4045041 into v2 Jul 2, 2026
4 of 6 checks passed
@kitlangton
kitlangton deleted the context-checkpoint branch July 2, 2026 13:32
jinhuang712 pushed a commit to jinhuang712/opencode that referenced this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant