Design: thread context pins for preserving important context across compaction

Open 💬 5 comments Opened Jun 7, 2026 by pdurlej

Problem

Long-running Codex sessions can lose user-important continuity after /compact or automatic compaction. Several existing reports describe the same underlying need from different angles:

  • #26321 asks for /compact hints so users can say which context should be retained.
  • #25083 asks for "Do Not Compact" areas.
  • #25618 asks for configurable compaction modes with user-selected retention priorities.
  • #23263 describes a PDF-heavy Codex App thread where transient document content overwhelmed the important long-term workflow instructions.
  • #16839 describes compaction-driven re-read loops for context that remains important across compaction cycles.
  • #14347 asks for better cumulative retention across repeated compactions.
  • #5957 describes auto-compaction causing Codex to forget it was mid-task and had already edited files.

These are not all the same feature request, but they point to a common missing primitive: users need a small, explicit, thread-scoped way to mark text that should remain available to future turns even after compaction.

Proposal

Add thread context pins: bounded text snippets attached to a materialized thread and supplied to future turns as user-provided context.

The primitive should be deliberately narrow:

  • persisted per thread, not global;
  • text-only in the first version;
  • bounded by count and bytes;
  • injected as untrusted user-provided context, not as system/developer instructions;
  • not an approval mechanism and not a way to bypass policy;
  • available after compaction because active pins are re-supplied on future turns.

This complements compaction improvements rather than replacing them. /compact hints, do-not-compact blocks, pinned files, and UI affordances could build on this later, but the first mergeable piece can be the storage/API/runtime primitive.

Suggested first PR

Current implementation: compare main...pdurlej:feature/thread-context-pins-refresh-v2.

A first PR can stay server-side and avoid UI scope:

  • add sqlite persistence for thread-scoped context pins;
  • add app-server JSON-RPC CRUD methods;
  • enforce conservative limits and reject oversize input instead of truncating stored text;
  • reject ephemeral threads;
  • inject active pins into turn/start and turn/steer as untrusted additional context;
  • ensure unchanged pins are re-emitted after compaction clears the additional-context baseline;
  • update generated app-server schemas and README docs.

This gives app clients a concrete primitive to experiment with while keeping the initial surface small enough to review.

Implementation-validated constraints

A working server-side implementation and focused tests surfaced several details that are easy to miss in a design-only version:

  • Creating a pin before the first turn requires materializing and flushing the rollout before state reconciliation, so the thread row exists for the pin's foreign key.
  • Strict thread deletion must explicitly remove context pins alongside the other thread-scoped state.
  • Updating a pin must preserve createdAt, because list ordering uses creation time with pinId as a deterministic tie-breaker.
  • Stored text can retain the conservative byte limit, while model injection should use the existing user-context rendering bound (currently roughly 1000 tokens per pin).
  • Pin CRUD must preserve archived thread state.
  • Per-thread exclusive request serialization makes count and aggregate-size validation deterministic within one app-server process.

The focused test surface now covers CRUD, archived-thread preservation, reinjection after compaction, deletion cleanup, count and byte boundaries, update replacement accounting, and pagination order/cursors.

One bounded follow-up remains: repeated resume-with-turn cycles can temporarily accumulate duplicate rendered pin fragments in history until compaction. Avoiding that correctly requires history-level deduplication; simply suppressing persistence or reinjection makes pins disappear after the first turn. That broader history contract does not need to expand the initial storage/API primitive.

Non-goals for the first PR

  • App UI for selecting messages or pinning text.
  • Pinned files or attachments.
  • Compact-summary editing or preview.
  • Treating pins as trusted instructions.
  • Notifications for every pin change.

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗