Skip to content

[codex] migrate environment context to model world state - #29249

Merged
sayan-oai merged 14 commits into
mainfrom
pakrym/full-ci-model-world-state
Jun 22, 2026
Merged

[codex] migrate environment context to model world state#29249
sayan-oai merged 14 commits into
mainfrom
pakrym/full-ci-model-world-state

Conversation

@pakrym-oai

@pakrym-oai pakrym-oai commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Why

Environment context is model-visible state, but it is currently assembled from transient turn values and diffed through environment-specific paths. That makes initial injection, turn-to-turn updates, and changes that happen within a turn use different baselines.

This PR introduces the smallest useful model world-state slice: environments only, with one in-memory baseline and one renderer for full state and diffs.

What changed

  • Add a typed WorldState container whose sections render fragments relative to an optional previous value. Full rendering uses the same diff path with no previous state.
  • Replace the parallel EnvironmentContext representation with an EnvironmentsState section keyed by environment ID and rendered in deterministic order.
  • Preserve the legacy single-environment output while supporting multiple environments, starting environments, unavailable tombstones, and changes to persisted turn-context values.
  • Store the latest complete WorldState on ContextManager and use it for both turn-boundary and mid-turn environment diffs.
  • Build initial and post-compaction context from the same world-state builder, then retain the rendered state as the next baseline.
  • Seed the in-memory baseline from the latest TurnContextItem when resuming an existing rollout; the world state itself is not serialized.
  • Keep non-world settings updates on their existing path and merge rendered world-state fragments at the session consumer.

Known limitation

A legacy TurnContextItem only reconstructs the primary environment as local; it cannot faithfully recover a remote-primary environment ID after resume. Live state uses the exact environment IDs once a complete baseline is established.

Test plan

  • just test -p codex-core world_state
  • just test -p codex-core record_context_updates
  • just test -p codex-core deferred_executor_
  • just test -p codex-core build_initial_context
  • just test -p codex-core rollout_reconstruction
  • just test -p codex-core process_compacted_history_reinjects_full_initial_context

@pakrym-oai pakrym-oai changed the title [codex] add model world state foundation [codex] migrate environment context to model world state Jun 20, 2026
@pakrym-oai
pakrym-oai marked this pull request as ready for review June 20, 2026 19:22
@pakrym-oai
pakrym-oai requested a review from a team as a code owner June 20, 2026 19:22

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2058d91cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/core/src/context/world_state/environment.rs Outdated
Comment thread codex-rs/core/src/context/world_state/environment.rs Outdated
Comment thread codex-rs/core/src/context/world_state/environment.rs Outdated
Comment thread codex-rs/core/src/context/world_state/environment.rs Outdated
Comment thread codex-rs/core/src/context/world_state/environment.rs Outdated
Comment thread codex-rs/core/src/context/world_state/environment.rs
.values()
.all(|update| matches!(update, EnvironmentUpdate::Current(_)));
(!updates.is_empty() || turn_context_values_changed).then(|| {
ContextualUserFragment::into(RenderedEnvironments {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t distinguish unchanged, changed, and removed value s
An environment-only update re-renders the full date/network/fs state, while a Some -> None transition merely omits the element and never prompt the model to clear

/// A snapshot of the model-visible world with one section per concrete type.
#[derive(Default)]
pub(crate) struct WorldState {
sections: IndexMap<TypeId, Box<dyn ErasedWorldStateSection>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The design talks about a stable structured snapshots and merge patches that can be replayed after resume/ fork/rollback. This is not possible yet but this is planned right? I'm a bit scared of this TypeId that is not serialized anywhere now tbh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is planned, yes, didn't want to cram everything into one PR.

// inputs or add explicit replay events so fork/resume can diff everything
// deterministically.
let contextual_user_message = build_environment_update_item(previous, next, shell);
let world_state_updates = previous

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only evaluates world state through turn-boundary settings updates
We need this a higher grannularity no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we'll do the injection on per-step basis.

Comment thread codex-rs/core/src/session/mod.rs Outdated
.await;
contextual_user_sections.push(
crate::context::EnvironmentContext::from_turn_context(turn_context, shell.as_ref())
crate::context::EnvironmentsState::from_turn_context(turn_context)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can initial and post-compaction context render through the same WorldStates builder used for diffs?

Comment thread codex-rs/core/src/context/world_state/mod.rs Outdated
pakrym-oai added a commit that referenced this pull request Jun 22, 2026
pakrym-oai added a commit that referenced this pull request Jun 22, 2026
@pakrym-oai
pakrym-oai force-pushed the pakrym/full-ci-model-world-state branch from 123ad8a to 92cad21 Compare June 22, 2026 17:09
@pakrym-oai
pakrym-oai force-pushed the pakrym/full-ci-model-world-state branch from 92cad21 to f9b60c8 Compare June 22, 2026 17:57
@pakrym-oai

Copy link
Copy Markdown
Contributor Author

@codex review this

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9dd9bc50c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +105 to +115
let mut updates = self
.environments
.iter()
.filter(|(id, environment)| {
previous
.environments
.get(*id)
.is_none_or(|previous| !environment.has_same_diff_value(previous))
})
.map(|(id, environment)| (id.clone(), EnvironmentUpdate::Current(environment.clone())))
.collect::<BTreeMap<_, _>>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include the legacy environment when adding a second one

When a session initially renders a single environment, the model sees the legacy <cwd>/<shell> form with no environment id. If a later turn or deferred-executor snapshot adds another environment while the original environment is unchanged, this filter emits only the new/changed environment, so the model enters a multi-environment state without ever learning the id for the pre-existing environment and cannot reliably target it in environment-scoped tool calls. Include the unchanged existing environment when diffing from a legacy-single render into a multi-environment render.

AGENTS.md reference: AGENTS.md:L92-L101

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followup

Comment on lines +131 to +134
current_date: self.current_date.clone(),
timezone: self.timezone.clone(),
network: self.network.clone(),
filesystem: self.filesystem.clone(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render only changed global context in env diffs

For an environment-only diff, such as a DeferredExecutor environment moving from starting to ready, turn_context_values_changed is false but the emitted fragment still carries the current date, timezone, network, and full filesystem profile. That re-appends unchanged static context every time an environment changes; with many workspace roots or permission entries this can add a large repeated prompt fragment and cause avoidable cache misses, so these fields should only render on full state or when they changed.

AGENTS.md reference: AGENTS.md:L96-L100

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now.

Comment thread codex-rs/core/src/context/world_state/environment.rs
Comment on lines +2893 to +2894
let world_state_baseline = if reference_context_item.is_some() {
Some(self.build_world_state(turn_context).await)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reuse the rendered world state for compaction baselines

When mid-turn compaction injects full initial context, the replacement items were rendered earlier by build_initial_context, but this recomputes the baseline from a fresh snapshot. If a DeferredExecutor environment changes from starting to ready in that window, the stored baseline can say the model already saw the ready state even though the compacted history still contains the starting state, so the subsequent environment diff is suppressed. Pass through the same WorldState that rendered the inserted context instead of rebuilding it here.

AGENTS.md reference: AGENTS.md:L92-L101

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more generically, compaction uses the potentially stale environment snapshot from TurnContext. seems like we need to recapture an environment snapshot so we inject the right stuff postcompaction.

follow up?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, followup. We need to have compaction use step context.

}
}

impl ContextualUserFragment for EnvironmentsState {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does EnvironmentsState also impl the fragment? it seems like we render via RenderedEnvironments?

@sayan-oai
sayan-oai merged commit 3b32d86 into main Jun 22, 2026
64 of 78 checks passed
@sayan-oai
sayan-oai deleted the pakrym/full-ci-model-world-state branch June 22, 2026 23:07
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants