## Why
`apply_patch` maintained separate batch and streaming parsers for the
same patch grammar. That duplicated the parsing rules and allowed final
execution to disagree with the live streamed preview.
## What changed
- Make `StreamingPatchParser` the single owner of hunk and environment
ID parsing.
- Keep heredoc and outer patch-boundary normalization in the existing
`parse_patch` wrapper, preserving its public API.
- Reject non-whitespace content after `*** End Patch` and preserve
separator handling after `*** End of File`.
- Reject duplicate environment ID preambles explicitly.
- Remove the duplicate batch hunk parser and its implementation-specific
tests.
The change removes 201 net lines while retaining focused coverage for
the unified parser's boundary behavior.
## Validation
- `just test -p codex-apply-patch`
- Compared a 24-hour corpus of 2,788,059 observed `apply_patch` payloads
against the previous batch parser. All 2,779,502 accepted payloads
produced identical hunks, canonical patch text, and environment IDs; the
remaining 8,557 payloads were rejected by both parsers, with zero
acceptance or payload mismatches.
## Summary
- add multi-environment apply_patch routing for both freeform and
function-call tool flows
- parse and reconcile the optional environment selector in the main
apply_patch parser, then verify against the selected environment in the
handler
- carry environment_id through runtime and approval surfaces so
remote-targeted patches stay explicit end to end
## Testing
- just fmt
- remote exec-server e2e: `cargo test -p codex-core --test all
apply_patch_multi_environment_uses_remote_executor -- --nocapture` on
dev via `scripts/test-remote-env.sh`
---------
Co-authored-by: Codex <noreply@openai.com>