Skip to content

Commit f14947d

Browse files
authored
fix(codex): persist automatic compaction history (#110587)
* fix(codex): persist automatic compaction counts * test(codex): stress compaction and restart recovery * test(codex): bind compaction waves to fresh counts * chore: keep Codex release note in PR
1 parent 609e5d2 commit f14947d

9 files changed

Lines changed: 510 additions & 86 deletions

docs/help/testing-live.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,17 @@ Docker notes:
334334
history. Override the bounded counts with
335335
`OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_HISTORY_TURNS` (1-20) and
336336
`OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_RESTARTS` (1-10).
337+
- Optional fan-out stress: set `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE=1`
338+
and `OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_COUNT` (1-12). The harness starts
339+
every child concurrently, waits for every terminal run, and verifies each
340+
unique child reply and native thread identity.
341+
- Optional compaction stress: `OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS=1`
342+
generates bounded native tool output, requires automatic compaction events,
343+
verifies the persisted compaction count and hidden-marker recall, restarts
344+
the Gateway and physical Codex app-server, then repeats the output and
345+
compaction wave. Tune the bounded work with
346+
`OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS_TURNS` (1-8) and
347+
`OPENCLAW_LIVE_CODEX_HARNESS_LARGE_OUTPUT_BYTES` (100000-1000000).
337348
- Optional loop-relay opt-out probe:
338349
`OPENCLAW_LIVE_CODEX_HARNESS_DISABLE_LOOP_RELAY=1`
339350
- The requested thinking preference may map to the nearest effort advertised
@@ -367,6 +378,17 @@ Restart and history stress:
367378

368379
```bash
369380
OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS=1 \
381+
pnpm test:docker:live-codex-harness
382+
```
383+
384+
Fan-out, large-output, compaction, and restart stress:
385+
386+
```bash
387+
OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key \
388+
OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE=1 \
389+
OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_COUNT=8 \
390+
OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS=1 \
391+
OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS=1 \
370392
pnpm test:docker:live-codex-harness
371393
```
372394

extensions/codex/src/app-server/event-projector.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2998,7 +2998,10 @@ describe("CodexAppServerEventProjector", () => {
29982998
expect(JSON.stringify(result.messagesSnapshot[2])).toContain("Codex plan");
29992999
expect(JSON.stringify(result.messagesSnapshot[2])).toContain("next");
30003000
expect(JSON.stringify(result.messagesSnapshot[2])).toContain("[in_progress] patch");
3001-
expect(requireRecord(result.itemLifecycle, "item lifecycle").compactionCount).toBe(1);
3001+
expect(result.compactionCount).toBe(1);
3002+
expect(requireRecord(result.itemLifecycle, "item lifecycle")).not.toHaveProperty(
3003+
"compactionCount",
3004+
);
30023005
expect(onContextCompacted).toHaveBeenCalledOnce();
30033006
});
30043007

extensions/codex/src/app-server/event-projector.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ export class CodexAppServerEventProjector {
434434
successfulCronAdds: toolTelemetry.successfulCronAdds,
435435
cloudCodeAssistFormatError: false,
436436
attemptUsage: projectedUsage,
437+
...(this.completedCompactionCount > 0
438+
? { compactionCount: this.completedCompactionCount }
439+
: {}),
437440
replayMetadata: {
438441
hadPotentialSideEffects,
439442
replaySafe: !hadPotentialSideEffects,
@@ -442,9 +445,6 @@ export class CodexAppServerEventProjector {
442445
startedCount: this.activeItemIds.size + this.completedItemIds.size,
443446
completedCount: this.completedItemIds.size,
444447
activeCount: this.activeItemIds.size,
445-
...(this.completedCompactionCount > 0
446-
? { compactionCount: this.completedCompactionCount }
447-
: {}),
448448
},
449449
yieldDetected: options?.yieldDetected || false,
450450
didSendDeterministicApprovalPrompt:

extensions/codex/src/app-server/run-attempt-start.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,12 @@ export async function startCodexAttemptRuntime(resources: CodexAttemptResources)
159159
state.codexSandboxPolicy = startupResult.sandboxPolicy;
160160
void emitCodexAppServerEvent(params, {
161161
stream: "codex_app_server.lifecycle",
162-
data: { phase: "thread_ready", threadId: state.thread.threadId },
162+
data: {
163+
phase: "thread_ready",
164+
threadId: state.thread.threadId,
165+
action: state.thread.lifecycle.action,
166+
clientId: state.client.getInstanceId(),
167+
},
163168
});
164169
} catch (error) {
165170
activateNativePreToolUseFailureFallback();

scripts/test-live-codex-harness-docker.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,17 @@ echo "==> Chat image probe: ${OPENCLAW_LIVE_CODEX_HARNESS_CHAT_IMAGE_PROBE:-0}"
367367
echo "==> Image probe: ${OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE:-1}"
368368
echo "==> MCP probe: ${OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE:-1}"
369369
echo "==> Subagent probe: ${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE:-1}"
370+
echo "==> Subagent count: ${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_COUNT:-1}"
370371
echo "==> Subagent-only fast path: ${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_ONLY:-auto}"
371372
echo "==> Guardian probe: ${OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE:-1}"
373+
echo "==> Code-mode-only probe: ${OPENCLAW_LIVE_CODEX_HARNESS_CODE_MODE_ONLY:-0}"
374+
echo "==> Loop relay disabled: ${OPENCLAW_LIVE_CODEX_HARNESS_DISABLE_LOOP_RELAY:-0}"
372375
echo "==> Resume stress: ${OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS:-0}"
373376
echo "==> Resume stress history turns: ${OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_HISTORY_TURNS:-4}"
374377
echo "==> Resume stress restarts: ${OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_RESTARTS:-3}"
378+
echo "==> Compaction stress: ${OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS:-0}"
379+
echo "==> Compaction stress turns: ${OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS_TURNS:-4}"
380+
echo "==> Large output bytes: ${OPENCLAW_LIVE_CODEX_HARNESS_LARGE_OUTPUT_BYTES:-300000}"
375381
echo "==> Auth mode: $CODEX_HARNESS_AUTH_MODE"
376382
echo "==> Profile file: $PROFILE_STATUS"
377383
echo "==> CI-safe Codex config: ${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}"
@@ -401,9 +407,14 @@ DOCKER_RUN_ARGS+=(--rm -t \
401407
-e OPENCLAW_LIVE_CODEX_HARNESS_AUTH="$CODEX_HARNESS_AUTH_MODE" \
402408
-e OPENCLAW_LIVE_CODEX_HARNESS=1 \
403409
-e OPENCLAW_LIVE_CODEX_HARNESS_CHAT_IMAGE_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_CHAT_IMAGE_PROBE:-0}" \
410+
-e OPENCLAW_LIVE_CODEX_HARNESS_CODE_MODE_ONLY="${OPENCLAW_LIVE_CODEX_HARNESS_CODE_MODE_ONLY:-0}" \
411+
-e OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS="${OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS:-0}" \
412+
-e OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS_TURNS="${OPENCLAW_LIVE_CODEX_HARNESS_COMPACTION_STRESS_TURNS:-4}" \
404413
-e OPENCLAW_LIVE_CODEX_HARNESS_DEBUG="${OPENCLAW_LIVE_CODEX_HARNESS_DEBUG:-}" \
414+
-e OPENCLAW_LIVE_CODEX_HARNESS_DISABLE_LOOP_RELAY="${OPENCLAW_LIVE_CODEX_HARNESS_DISABLE_LOOP_RELAY:-0}" \
405415
-e OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE:-1}" \
406416
-e OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE:-1}" \
417+
-e OPENCLAW_LIVE_CODEX_HARNESS_LARGE_OUTPUT_BYTES="${OPENCLAW_LIVE_CODEX_HARNESS_LARGE_OUTPUT_BYTES:-300000}" \
407418
-e OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE:-1}" \
408419
-e OPENCLAW_LIVE_CODEX_HARNESS_MODEL="${OPENCLAW_LIVE_CODEX_HARNESS_MODEL:-openai/gpt-5.6-luna}" \
409420
-e OPENCLAW_LIVE_CODEX_HARNESS_TARGETS="${OPENCLAW_LIVE_CODEX_HARNESS_TARGETS:-}" \
@@ -416,6 +427,7 @@ DOCKER_RUN_ARGS+=(--rm -t \
416427
-e OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_RESTARTS="${OPENCLAW_LIVE_CODEX_HARNESS_RESUME_STRESS_RESTARTS:-3}" \
417428
-e OPENCLAW_LIVE_CODEX_HARNESS_SETUP_TIMEOUT_SECONDS="$CODEX_HARNESS_SETUP_TIMEOUT_SECONDS" \
418429
-e OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_ONLY="${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_ONLY:-}" \
430+
-e OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_COUNT="${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_COUNT:-1}" \
419431
-e OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE="${OPENCLAW_LIVE_CODEX_HARNESS_SUBAGENT_PROBE:-1}" \
420432
-e OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG="${OPENCLAW_LIVE_CODEX_HARNESS_USE_CI_SAFE_CODEX_CONFIG:-1}" \
421433
-e OPENCLAW_LIVE_CODEX_CLI_PACKAGE_SPEC="$CODEX_CLI_PACKAGE_SPEC" \

src/gateway/gateway-codex-harness.live-helpers.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
isExpectedYieldedAgentTimeout,
1111
isRetryableCodexHarnessLiveError,
1212
isStrictExpectedCodexModelsCommandText,
13+
shouldUseCodexHarnessSubagentOnlyFastPath,
1314
} from "./gateway-codex-harness.live-helpers.js";
1415

1516
const includesExpectedCodexModelsCommandText = (text: string) =>
@@ -30,6 +31,30 @@ function expectStrictCodexModelsCommandText(text: string): void {
3031
}
3132

3233
describe("gateway codex harness live helpers", () => {
34+
it("keeps combined stress probes out of the subagent-only fast path", () => {
35+
const base = {
36+
chatImageProbe: false,
37+
codeModeOnly: false,
38+
compactionStress: false,
39+
explicitOptOut: false,
40+
guardianProbe: false,
41+
imageProbe: false,
42+
mcpProbe: false,
43+
resumeStress: false,
44+
subagentProbe: true,
45+
};
46+
47+
expect(shouldUseCodexHarnessSubagentOnlyFastPath(base)).toBe(true);
48+
expect(shouldUseCodexHarnessSubagentOnlyFastPath({ ...base, resumeStress: true })).toBe(false);
49+
expect(shouldUseCodexHarnessSubagentOnlyFastPath({ ...base, compactionStress: true })).toBe(
50+
false,
51+
);
52+
expect(shouldUseCodexHarnessSubagentOnlyFastPath({ ...base, codeModeOnly: true })).toBe(false);
53+
expect(shouldUseCodexHarnessSubagentOnlyFastPath({ ...base, explicitOptOut: true })).toBe(
54+
false,
55+
);
56+
});
57+
3358
it("classifies sessions.list timeouts as retryable live Codex errors", () => {
3459
const error = new Error("gateway request timeout for sessions.list");
3560

src/gateway/gateway-codex-harness.live-helpers.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,30 @@ export const EXPECTED_CODEX_MODELS_COMMAND_TEXT = [
8181
"Current OpenClaw session status reports the active model as:",
8282
] as const;
8383

84+
export function shouldUseCodexHarnessSubagentOnlyFastPath(params: {
85+
chatImageProbe: boolean;
86+
codeModeOnly: boolean;
87+
compactionStress: boolean;
88+
explicitOptOut: boolean;
89+
guardianProbe: boolean;
90+
imageProbe: boolean;
91+
mcpProbe: boolean;
92+
resumeStress: boolean;
93+
subagentProbe: boolean;
94+
}): boolean {
95+
return (
96+
params.subagentProbe &&
97+
!params.chatImageProbe &&
98+
!params.codeModeOnly &&
99+
!params.compactionStress &&
100+
!params.guardianProbe &&
101+
!params.imageProbe &&
102+
!params.mcpProbe &&
103+
!params.resumeStress &&
104+
!params.explicitOptOut
105+
);
106+
}
107+
84108
const HEALTHY_CODEX_MODELS_COMMAND_TEXT = [
85109
"Codex models:",
86110
"Available Codex models",

0 commit comments

Comments
 (0)