Design: thread context pins for preserving important context across compaction

Resolved 💬 6 comments Opened Jun 7, 2026 by pdurlej Closed Aug 8, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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 ↗

6 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #26321
  • #25618

Powered by Codex Action

pdurlej · 2 months ago

Implementation branch / compare link:

https://github.com/openai/codex/compare/main...pdurlej:feature/thread-context-pins

This branch implements the server-side first slice described above: sqlite persistence, app-server CRUD methods, bounded text-only pins, untrusted additional-context injection for future turns, compaction re-emission behavior, generated schemas, and README docs.

I attempted to open it as a draft PR from the fork, but GitHub rejected CreatePullRequest for the current account/integration. Leaving the compare link here so the implementation can still be inspected alongside the design discussion.

pdurlej · 2 months ago

Thanks. I agree these are closely related, but I don't think this is a direct duplicate.

#26321 proposes a /compact hint for one compaction pass.
#25618 proposes configurable /compact modes.

This issue is trying to define a smaller underlying primitive: bounded thread-scoped text pins that are persisted and re-supplied as untrusted user context on future turns, including after compaction.

That primitive could support the workflows described in #26321 and #25618, but it does not replace those feature requests. UI affordances like /compact hints, retention modes, or "do not compact" blocks could be layered on top later.

pdurlej · 2 months ago

Refreshed implementation branch:

https://github.com/openai/codex/compare/main...pdurlej:feature/thread-context-pins-refresh

I rebased the implementation onto current main (0ffcefaf3d) via a fresh branch, resolved the app-server turn_processor.rs conflict, and re-ran the focused checks:

  • cargo fmt
  • cargo test -p codex-app-server-protocol client_request_serialization_scope_covers_keyed_families
  • cargo test -p codex-app-server-protocol schema_fixtures
  • cargo test -p codex-state create_list_update_and_delete_thread_context_pin
  • cargo test -p codex-core replace_history_with_reference_context_item_reinjects_additional_context_after_compaction
  • cargo test -p codex-app-server thread_context_pins
  • cargo check -p codex-app-server
  • git diff --check origin/main...HEAD

I also tried opening this as a draft PR from the fork again, but GitHub still rejects the request with CreatePullRequest permissions for this account/integration. Leaving the refreshed compare link here so the current branch can still be reviewed against latest main.

pdurlej · 2 months ago

Quick update: I refreshed the implementation branch onto current main (b668abb488).

Conflicts resolved during the refresh:

  • regenerated app-server protocol JSON/TS schemas after current upstream request/API additions;
  • kept both upstream thread/delete wiring and the new thread/contextPins/* processor wiring;
  • adjusted message_processor state DB ownership after newer upstream app-server processors also needed the state DB handle;
  • kept newer core session test imports plus the context-pin reinjection test;
  • kept newer app-server test modules plus the context pins test module;
  • renumbered the context pins migration from 0036 to 0040, after upstream's current migration sequence through 0039_threads_recency_at.

Validation:

  • codex-app-server-protocol tests pass, including generated schema fixture checks.
  • codex-state tests pass, including the context pins runtime test and upstream migration compatibility tests.
  • Targeted core compaction reinjection test passes.
  • Targeted app-server context pins tests pass.

I still think this primitive is useful beyond the initial "remember this after compaction" case. As goal/loop-style long-running workflows become more important, users and agents need a bounded, explicit way to control durable context without rewriting conversation history. Thread context pins give the app/client a small API surface that could support UI affordances or higher-level retention policies later.

Related community requests this builds on: #26321 and #25618. Their duplicate chains also point at related context-retention/compaction requests such as #25083, #25394, #24779, and #25563.

pdurlej · 20 days ago

Closing this as not planned. The implementation branch remains available as a reference.