## Why
The core migration is trying to make `PermissionProfile` the shape tests
and runtime code reason about, leaving `SandboxPolicy` only where legacy
behavior is explicitly under test. The local
`permission_profile_for_sandbox_policy()` test helpers kept new
permission-profile tests mentally tied to the old sandbox model even
when the equivalent profile is straightforward.
## What Changed
- Removed the `permission_profile_for_sandbox_policy()` helper from the
network proxy spec tests and session tests.
- Replaced legacy conversions for read-only, workspace-write, and
full-access cases with `PermissionProfile::read_only()`,
`PermissionProfile::workspace_write()`, and
`PermissionProfile::Disabled`.
- Constructed the external-sandbox session test's
`PermissionProfile::External` directly, while preserving the legacy
`SandboxPolicy` only where the test still exercises legacy config update
behavior.
## How To Review
This PR is intentionally test-only. Review the two touched files and
check that each replacement preserves the old legacy mapping:
- `SandboxPolicy::new_read_only_policy()` ->
`PermissionProfile::read_only()`
- `SandboxPolicy::new_workspace_write_policy()` ->
`PermissionProfile::workspace_write()`
- `SandboxPolicy::DangerFullAccess` -> `PermissionProfile::Disabled`
- `SandboxPolicy::ExternalSandbox { network_access: Restricted }` ->
`PermissionProfile::External { network: Restricted }`
## Verification
- `cargo test -p codex-core
requirements_allowed_domains_are_a_baseline_for_user_allowlist`
- `cargo test -p codex-core
start_managed_network_proxy_applies_execpolicy_network_rules`
- `cargo test -p codex-core
session_configured_reports_permission_profile_for_external_sandbox`
- `cargo test -p codex-core
managed_network_proxy_decider_survives_full_access_start`
- `just fix -p codex-core`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22795).
* #22891
* __->__ #22795
## Why
The app-server thread lifecycle API should no longer expose the full
`PermissionProfile` value. After the permissions-profile migration,
clients should round-trip only the active profile identity through
`activePermissionProfile` and `permissions` when that identity is known.
The full profile is server-side config. Treating a response-derived
legacy sandbox projection as a new local profile can lose named-profile
restrictions and accidentally widen permissions on the next turn. The
legacy `sandbox` response field remains only as the
compatibility/display fallback.
## What Changed
- Removed `permissionProfile` from `ThreadStartResponse`,
`ThreadResumeResponse`, and `ThreadForkResponse`.
- Stopped populating that field in app-server thread start/resume/fork
responses.
- Updated embedded exec/TUI response mapping to derive display
permission state from local config or the legacy sandbox fallback
instead of a response profile value.
- Added a TUI turn override shape that distinguishes preserving server
permissions, selecting an active profile id, and sending a legacy
sandbox for an explicit local override.
- Preserved remote app-server permissions across turns by sending
`permissions` only when an `activePermissionProfile` id is known, and
otherwise sending no sandbox override unless the user selected a local
override.
- Kept embedded `thread/resume` hydration server-authored when
`activePermissionProfile` is absent, which matches the live-thread
attach path where the server ignores requested overrides.
- Updated the app-server README to remove the obsolete lifecycle
response `permissionProfile` reference. The remaining
`permissionProfile` README references are request-side permission
overrides.
- Regenerated app-server JSON schema and TypeScript fixtures.
- Kept the generated typed response enum exempt from
`large_enum_variant`, matching the existing payload enum exemption after
the lifecycle response variants shrank.
## How To Review
Start with `codex-rs/app-server-protocol/src/protocol/v2/thread.rs` to
confirm the response shape, then check the response construction in
`codex-rs/app-server/src/request_processors`. The generated schema and
TypeScript fixture changes are mechanical follow-through from the
protocol removal.
The TUI behavior is the delicate part: review
`codex-rs/tui/src/app_server_session.rs` for response hydration and
turn-start override projection, then
`codex-rs/tui/src/app/thread_routing.rs` for the decision about whether
the next turn should preserve the server snapshot, send an active
profile id, or send a legacy sandbox for an explicit local override.
## Verification
- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol
thread_lifecycle_responses_default_missing_optional_fields`
- `cargo test -p codex-exec
session_configured_from_thread_response_uses_permission_profile_from_config`
- `cargo test -p codex-tui --lib thread_response`
- `cargo test -p codex-tui turn_permissions_`
- `cargo test -p codex-tui
resume_response_restores_turns_from_thread_items`
- `cargo test -p codex-analytics
track_response_only_enqueues_analytics_relevant_responses`
- `just fix -p codex-analytics`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-tui`
- `just argument-comment-lint`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22792).
* #22795
* __->__ #22792
This adds `apps_mcp_product_sku` as a toplevel config.toml key. We pass
the given value as a header when listing MCPs for the client, allowing
connectors to be filtered per product entry point.
---------
Co-authored-by: Codex <noreply@openai.com>
## Why
Sandbox telemetry tags should be derived from the active permission
profile, not from a legacy `SandboxPolicy`, so the tagging code stays
aligned with the permissions migration and does not preserve a
policy-shaped production helper only for tests.
## What Changed
- Removed the production `sandbox_tag(&SandboxPolicy, ...)` helper.
- Updated sandbox tag tests to construct the relevant
`PermissionProfile` values directly.
- Kept the platform-specific sandbox tag behavior under the existing
`permission_profile_sandbox_tag` path.
## How To Review
The production change is in `codex-rs/core/src/sandbox_tags.rs`. Most of
the diff is test cleanup that replaces legacy policy setup with
permission profiles, so review the expected tag assertions rather than
the old helper mechanics.
## Verification
- `cargo test -p codex-core sandbox_tag`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22791).
* #22795
* #22792
* __->__ #22791
## Why
The permissions instruction builder should consume the new permissions
model directly. Keeping a `SandboxPolicy` conversion helper in this path
encourages new code to route through legacy sandbox policy values even
when the caller already has a `PermissionProfile`.
## What Changed
- Removed `PermissionsInstructions::from_policy`.
- Removed the test that exercised that legacy helper.
- Left the existing profile-based instruction coverage in place.
## How To Review
Review `codex-rs/core/src/context/permissions_instructions.rs` first.
This PR is intentionally narrow: the production behavior should be
unchanged for profile callers, and the deleted surface was only a
convenience adapter from `SandboxPolicy`.
## Verification
- `cargo test -p codex-core builds_permissions_from_profile`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22790).
* #22795
* #22792
* #22791
* __->__ #22790
## What
- Internal Git helper commands now ignore configured hook directories
during repository bookkeeping.
## Why
- These helper flows should stay consistent even when a repository has
hook-directory configuration of its own.
## How
- Pass a command-local `core.hooksPath` override in the shared helper
path and the Git-info helper path.
- Add regressions for the baseline index rewrite flow and the metadata
status flow.
## Validation
- `cargo fmt --manifest-path
/Users/bookholt/code/codex/codex-rs/Cargo.toml --all --check`
- `cargo test --manifest-path
/Users/bookholt/code/codex/codex-rs/Cargo.toml -p codex-git-utils`
- `cargo test --manifest-path
/Users/bookholt/code/codex/codex-rs/Cargo.toml -p codex-core
test_get_has_changes_`
## Why
[#22581](https://github.com/openai/codex/pull/22581) started separating
the chat composer’s responsibilities, but `ChatComposer` still owned the
remaining editable draft state alongside footer/status presentation
state. This follow-up makes those ownership lines explicit so future
composer changes have a smaller blast radius and `BottomPane` does not
need to keep exposing scattered draft getters.
This is just a refactor. No functional or behavioral changes are
intended.
## What changed
- Move the remaining editable composer state into
`bottom_pane/chat_composer/draft_state.rs`.
- Move footer and status-row presentation state into
`bottom_pane/chat_composer/footer_state.rs`.
- Add an internal `ComposerDraftSnapshot` for restore flows, replacing
several ad hoc `BottomPane` pass-through reads.
- Rewire the related history-search and thread-input restore paths to
use the extracted state.
## Verification
- `RUST_MIN_STACK=8388608 cargo test -p codex-tui`
- `cargo insta pending-snapshots`