Design: thread context pins for preserving important context across compaction
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
/compacthints 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/startandturn/steeras 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 withpinIdas 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.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗