Merge branch 'codex/work-folders-staging-hardening-refresh' into codex/work-folders-remote-recovery-refresh

* codex/work-folders-staging-hardening-refresh:
  fix(runner): preserve exact Claude model selectors during ACP admission
  test(runner): serve goal reads while holding passive completion notices
  fix(ci): validate shared Cloud image contexts throughout the stack
This commit is contained in:
Dotta committed 2026-09-11 17:02:30 -05:00
commit 3aebfbaa14
3 files changed
+77 -51

No files matched your search

@@ -1619,56 +1619,71 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}))?;
}
if emit_post_completion_passive_statuses {
if let Some(gate) = post_completion_notification_gate.as_ref() {
let deadline = std::time::Instant::now() + Duration::from_secs(5);
while !gate.is_file() {
if std::time::Instant::now() >= deadline {
return Err(
"post-completion notification gate timed out".into()
);
let gate = post_completion_notification_gate.clone();
let thread_id = state.thread_id.clone();
let tail_turn_id = provider_turn_id.clone();
// Goal reconciliation reads must remain responsive while
// the test holds back the post-terminal passive notices.
thread::spawn(move || {
let result = (|| -> io::Result<()> {
if let Some(gate) = gate.as_ref() {
let deadline =
std::time::Instant::now() + Duration::from_secs(5);
while !gate.is_file() {
if std::time::Instant::now() >= deadline {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"post-completion notification gate timed out",
));
}
thread::sleep(Duration::from_millis(1));
}
}
thread::sleep(Duration::from_millis(1));
for notification in [
json!({
"method": "deprecationNotice",
"params": {"summary": "A provider setting is deprecated", "details": null}
}),
json!({
"method": "remoteControl/status/changed",
"params": {"status": "disabled", "environmentId": null}
}),
json!({
"method": "mcpServer/startupStatus/updated",
"params": {"name": "codex_apps", "status": "ready", "error": null}
}),
json!({
"method": "account/rateLimits/updated",
"params": {"rateLimits": {}}
}),
json!({
"method": "rawResponseItem/completed",
"params": {"threadId": thread_id, "turnId": tail_turn_id, "item": {"id": "raw-tail", "type": "reasoning"}}
}),
json!({
"method": "rawResponse/completed",
"params": {"threadId": thread_id, "turnId": tail_turn_id, "response": {"id": "response-tail"}}
}),
json!({
"method": "thread/goal/updated",
"params": {"threadId": thread_id, "goal": "finish the turn"}
}),
json!({
"method": "thread/goal/cleared",
"params": {"threadId": thread_id}
}),
] {
send(notification)?;
}
if let Some(gate) = gate.as_ref() {
fs::write(gate.with_extension("emitted"), b"emitted")?;
}
Ok(())
})();
if let Err(error) = result {
eprintln!("post-completion passive tail failed: {error}");
}
}
for notification in [
json!({
"method": "deprecationNotice",
"params": {"summary": "A provider setting is deprecated", "details": null}
}),
json!({
"method": "remoteControl/status/changed",
"params": {"status": "disabled", "environmentId": null}
}),
json!({
"method": "mcpServer/startupStatus/updated",
"params": {"name": "codex_apps", "status": "ready", "error": null}
}),
json!({
"method": "account/rateLimits/updated",
"params": {"rateLimits": {}}
}),
json!({
"method": "rawResponseItem/completed",
"params": {"threadId": state.thread_id, "turnId": provider_turn_id, "item": {"id": "raw-tail", "type": "reasoning"}}
}),
json!({
"method": "rawResponse/completed",
"params": {"threadId": state.thread_id, "turnId": provider_turn_id, "response": {"id": "response-tail"}}
}),
json!({
"method": "thread/goal/updated",
"params": {"threadId": state.thread_id, "goal": "finish the turn"}
}),
json!({
"method": "thread/goal/cleared",
"params": {"threadId": state.thread_id}
}),
] {
send(notification)?;
}
if let Some(gate) = post_completion_notification_gate.as_ref() {
fs::write(gate.with_extension("emitted"), b"emitted")?;
}
});
}
if emit_post_completion_foreign_turn {
let gate = post_completion_notification_gate.clone();
@@ -101,7 +101,10 @@ describe("Codex ACPX runtime adapter", () => {
});
});
it.each([["claude" as const, "claude-sonnet-5", "claude-sonnet-5"]])(
it.each([
["claude" as const, "claude-sonnet-5", "claude-sonnet-5"],
["claude" as const, "custom-claude-model", "custom-claude-model"],
])(
"opens the qualified %s session through the verified lease",
async (agent, model, providerModel) => {
const runtime = fakeRuntime();
@@ -109,7 +112,7 @@ describe("Codex ACPX runtime adapter", () => {
const options = openOptions(command);
let runtimeOptions: AcpRuntimeOptions | undefined;
options.profile = resolveQualifiedAcpxProfile(agent, model);
options.launchEnvironment = { PATH: "/verified/bin" };
options.launchEnvironment = { PATH: "/verified/bin", ANTHROPIC_CUSTOM_MODEL_OPTION: "stale-model" };
await openCodexAcpxRuntime(options, {
createRegistry: ({ overrides }) => {
@@ -128,6 +131,7 @@ describe("Codex ACPX runtime adapter", () => {
expect(runtimeOptions?.spawnEnvironment?.()).toEqual({
PATH: "/verified/bin",
PAPERCLIP_ACPX_ISOLATED_CONTEXT: "1",
ANTHROPIC_CUSTOM_MODEL_OPTION: providerModel,
});
expect(runtime.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
@@ -317,7 +317,14 @@ export async function openQualifiedAcpxRuntime(
spawnEnvironment: () => ({
...definedEnvironment(options.launchEnvironment),
...(options.profile.agent === "claude"
? { PAPERCLIP_ACPX_ISOLATED_CONTEXT: "1" }
? {
PAPERCLIP_ACPX_ISOLATED_CONTEXT: "1",
// Claude ACP otherwise maps concrete IDs back to rolling picker
// aliases (e.g. claude-sonnet-5 -> sonnet). Advertise the exact
// host-requested ID so model verification stays exact on resume
// and before the first billable prompt.
ANTHROPIC_CUSTOM_MODEL_OPTION: options.profile.reportedModelId,
}
: {}),
}),
spawnCwd: options.cwd,