Skip to content

Commit 18dfa72

Browse files
charley-oaicodex
andcommitted
refactor(core): share turn-state fragment registration adapter
Introduce a shared adapter trait used by both developer and contextual-user fragment registries. Both envelope registries now implement the same adapter contract and continue to register fragment types via . Also document why an adapter trait is needed (TurnContextDiffFragment uses static Self-returning constructors) and update contributor docs to point to the shared adapter. Co-authored-by: Codex <noreply@openai.com>
1 parent 6170e60 commit 18dfa72

File tree

5 files changed

+44
-16
lines changed

5 files changed

+44
-16
lines changed

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ In the codex-rs folder where the rust code lives:
3232
- Use `<environment_context>` specifically for environment facts derived from `TurnContext` that may need turn-to-turn diffs (`cwd`, `shell`, optional `current_date`, optional `timezone`, optional network allow/deny domain summaries). Do not put policy text, plugin/skill listings, or other guidance into `<environment_context>`; those should use dedicated fragments.
3333
- Fragments derived from durable/current turn state that should update/reinject via diff across resume/fork/compaction/backtracking should implement `TurnContextDiffFragment` so current-state extraction, diffing, and rendering live together.
3434
- Runtime/session-prefix one-off fragments can implement only `ModelVisibleContextFragment` when they are not turn-state diffs.
35-
- Register new turn-state fragments by type in the fragment registry lists used by both initial-context and settings-update assembly (`...FragmentRegistration::of::<YourType>()` in `context_manager/updates/*_fragments.rs`) so they are discovered by iteration, not ad hoc wiring.
35+
- Register new turn-state fragments by type in the fragment registry lists used by both initial-context and settings-update assembly (`...FragmentRegistration::of::<YourType>()` in `context_manager/updates/*_fragments.rs`). Both envelope registries use the shared `TurnStateFragmentRegistration` adapter trait.
3636
- Do not hand-construct model-visible `ResponseItem::Message` payloads in new code; use fragment conversion and shared envelope builders.
3737
- Do not inject raw strings directly into the initial-context or settings-update builders, and do not call fragment wrapping helpers ad hoc from new code.
3838

codex-rs/core/src/context_manager/updates.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@ pub(crate) enum FragmentBuildPass {
2828
SettingsUpdate,
2929
}
3030

31+
/// Shared adapter for turn-state fragment registries across both envelopes.
32+
///
33+
/// We need this adapter because `TurnContextDiffFragment` relies on static,
34+
/// `Self`-returning constructors (`from_turn_context` and
35+
/// `diff_from_turn_context_item`), which are not directly usable as a uniform
36+
/// registry interface. This trait gives both envelope registries one common
37+
/// "build for pass + context" shape while keeping fragment logic on the
38+
/// fragment types themselves.
39+
pub(crate) trait TurnStateFragmentRegistration {
40+
type Role: ModelVisibleContextRole;
41+
type Fragment: ModelVisibleContextFragment<Role = Self::Role>;
42+
43+
fn build(
44+
&self,
45+
pass: FragmentBuildPass,
46+
previous: Option<&TurnContextItem>,
47+
turn_context: &TurnContext,
48+
params: &TurnContextDiffParams<'_>,
49+
) -> Option<Self::Fragment>;
50+
}
51+
3152
pub(crate) struct TurnStateEnvelopeFragments {
3253
pub(crate) developer: Vec<DeveloperTextFragment>,
3354
pub(crate) contextual_user: Vec<ContextualUserTextFragment>,

codex-rs/core/src/context_manager/updates/contextual_user_update_fragments.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
//! canonical list.
77
88
use super::FragmentBuildPass;
9+
use super::TurnStateFragmentRegistration;
910
use crate::codex::TurnContext;
1011
use crate::environment_context::EnvironmentContext;
1112
use crate::instructions::AgentsMdInstructions;
13+
use crate::model_visible_context::ContextualUserContextRole;
1214
use crate::model_visible_context::ContextualUserTextFragment;
1315
use crate::model_visible_context::TurnContextDiffFragment;
1416
use crate::model_visible_context::TurnContextDiffParams;
@@ -51,23 +53,23 @@ impl ContextualUserFragmentRegistration {
5153
build: build_registered_contextual_user_fragment::<F>,
5254
}
5355
}
56+
}
57+
58+
impl TurnStateFragmentRegistration for ContextualUserFragmentRegistration {
59+
type Role = ContextualUserContextRole;
60+
type Fragment = ContextualUserTextFragment;
5461

5562
fn build(
5663
&self,
5764
pass: FragmentBuildPass,
5865
previous: Option<&TurnContextItem>,
5966
turn_context: &TurnContext,
6067
params: &TurnContextDiffParams<'_>,
61-
) -> Option<ContextualUserTextFragment> {
68+
) -> Option<Self::Fragment> {
6269
(self.build)(pass, previous, turn_context, params)
6370
}
6471
}
6572

66-
// TurnContextDiffFragment uses static constructors returning `Self`, which are
67-
// not object-safe for `dyn` dispatch. This typed registration adapter preserves
68-
// "register a fragment type once" ergonomics while keeping fragment behavior in
69-
// the trait implementations.
70-
7173
/// Canonical registry for turn-state contextual-user fragments.
7274
///
7375
/// To add a new turn-state contextual-user model-visible fragment:
@@ -90,6 +92,8 @@ pub(super) fn build_registered_contextual_user_fragments(
9092
) -> Vec<ContextualUserTextFragment> {
9193
REGISTERED_CONTEXTUAL_USER_FRAGMENT_BUILDERS
9294
.iter()
93-
.filter_map(|registration| registration.build(pass, previous, next, params))
95+
.filter_map(|registration| {
96+
TurnStateFragmentRegistration::build(registration, pass, previous, next, params)
97+
})
9498
.collect()
9599
}

codex-rs/core/src/context_manager/updates/developer_update_fragments.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! switch guidance).
88
99
use super::FragmentBuildPass;
10+
use super::TurnStateFragmentRegistration;
1011
use crate::codex::TurnContext;
1112
use crate::features::Feature;
1213
use crate::model_visible_context::DeveloperContextRole;
@@ -399,14 +400,19 @@ impl DeveloperFragmentRegistration {
399400
build: build_registered_developer_fragment::<F>,
400401
}
401402
}
403+
}
404+
405+
impl TurnStateFragmentRegistration for DeveloperFragmentRegistration {
406+
type Role = DeveloperContextRole;
407+
type Fragment = DeveloperTextFragment;
402408

403409
fn build(
404410
&self,
405411
pass: FragmentBuildPass,
406412
previous: Option<&TurnContextItem>,
407413
turn_context: &TurnContext,
408414
params: &TurnContextDiffParams<'_>,
409-
) -> Option<DeveloperTextFragment> {
415+
) -> Option<Self::Fragment> {
410416
(self.build)(pass, previous, turn_context, params)
411417
}
412418
}
@@ -422,11 +428,6 @@ const REGISTERED_DEVELOPER_FRAGMENT_BUILDERS: &[DeveloperFragmentRegistration] =
422428
DeveloperFragmentRegistration::of::<PersonalityUpdateFragment>(),
423429
];
424430

425-
// TurnContextDiffFragment uses static constructors returning `Self`, which are
426-
// not object-safe for `dyn` dispatch. This typed registration adapter preserves
427-
// "register a fragment type once" ergonomics while keeping fragment behavior in
428-
// the trait implementations.
429-
430431
pub(super) fn build_registered_developer_fragments(
431432
pass: FragmentBuildPass,
432433
previous: Option<&TurnContextItem>,
@@ -435,6 +436,8 @@ pub(super) fn build_registered_developer_fragments(
435436
) -> Vec<DeveloperTextFragment> {
436437
REGISTERED_DEVELOPER_FRAGMENT_BUILDERS
437438
.iter()
438-
.filter_map(|registration| registration.build(pass, previous, next, params))
439+
.filter_map(|registration| {
440+
TurnStateFragmentRegistration::build(registration, pass, previous, next, params)
441+
})
439442
.collect()
440443
}

docs/model-visible-context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ When adding new model-visible context:
2727
3. Set the fragment `type Role` to the correct developer or contextual-user role.
2828
4. If it is a contextual-user fragment, wrap content with shared marker helpers/constants from `model_visible_context`.
2929
5. If the fragment is derived from durable/current turn state and should be diffed/reinjected after history mutations, also implement `TurnContextDiffFragment`.
30-
6. Register the fragment type in the turn-state fragment registries (`...FragmentRegistration::of::<YourType>()` in `context_manager/updates/developer_update_fragments.rs` or `context_manager/updates/contextual_user_update_fragments.rs`) so both initial-context and settings-update paths iterate it.
30+
6. Register the fragment type in the turn-state fragment registries (`...FragmentRegistration::of::<YourType>()` in `context_manager/updates/developer_update_fragments.rs` or `context_manager/updates/contextual_user_update_fragments.rs`). Both registries use the same `TurnStateFragmentRegistration` adapter trait.
3131
7. Push the resulting fragments through the shared envelope builders.
3232

3333
Do not hand-build developer or contextual-user model-visible `ResponseItem`s in new code.

0 commit comments

Comments
 (0)