Repository navigation
fix(server): harden the rollback failure marker - #13911
Conversation
checkpoint.rollback.fail moves out of the public OrchestrationV2Command union into a server-only internal command, so no client can dispatch it through orchestration.dispatchCommand. The thread now records its current rollback request, and a failure from a superseded rollback is rejected instead of overwriting the marker. The worker records a fixed message rather than the lower-level error text, which stays in the server log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This hardening changes both the client/server command boundary and the production rollback state machine, including rejection of forged or superseded failure markers. The scope is focused and tested, but the direct security and integrity implications warrant additional human review. You can add or adjust custom eligibility rules. Learn more. |
b2ed076
into
t3code/codex-turn-mapping
#13887 added a
rollbackFailuremarker so a rewind that fails for good rejects instead of hanging. Macroscope flagged four problems with it after the merge: any client could forge the marker, a late failure from an older rollback could overwrite a newer one, raw lower-level error text reached clients, and one import broke the Effect service convention. This PR fixes all four.What changed
checkpoint.rollback.failwas a member ofOrchestrationV2Command, which is theorchestration.dispatchCommandpayload. That meant any operate-scope client could plant arollbackFailureand make a real rewind reject. It now lives in a server-onlyOrchestrationV2InternalCommand, following the V1InternalOrchestrationCommandsplit.OrchestratorV2.dispatchandThreadManagementService.dispatchacceptOrchestrationV2ServerCommand(client plus internal). The RPC schema still uses the client union, so the socket rejects the command at decode time.thread.rollbackRequestId(same pattern astitleRegeneration.requestId) and clears any old failure in the samethread.metadata-updatedevent.checkpoint.rollback.failis rejected when itsrequestIdis no longer the current rollback. Before this, A's last retry could run after B was accepted, because the outbox claims byavailable_atand doesn't serialize per thread. A would then write a failure on top of B.ROLLBACK_FAILED_MESSAGEinstead ofCause.findErrorOption(cause).message. The full cause is still logged server-side by the worker's existing per-attempt"Orchestration effect execution failed"warning (Cause.pretty, nested causes included).EffectWorker.tsimportsCheckpointRollbackServiceas a namespace.Side effects of the change: a rollback now always emits one
thread.metadata-updatedbeforecheckpoint.rollback-requested, and two existing assertions were updated for that. Four test fakes that record dispatched commands are retyped to the server union, and a dead scenario-testkitcaseis removed.Verification
packages/contracts:vp test run src/rpc.test.ts src/orchestrationV2.test.ts: 32 passed. The new test decodescheckpoint.rollback.failagainst the realdispatchCommandpayload schema and expects a failure. I confirmed it fails against the old contracts.apps/server:vp test run src/orchestration-v2/runtimeLayer.test.ts: 46 passed. That includes the fix(server): a failed rollback shows an error instead of hanging #13887 test and the new "ignores a late failure from a rollback that a newer one superseded": rollback A spends 4 of 5 attempts, B is accepted, A's last attempt fails. The test asserts no marker appears, then that B's own failure is still recorded. With the guard removed this test fails.apps/server: EffectWorker, CheckpointRollbackService, ThreadManagementService, ThreadMessageIntake, linkCreatedPullRequest, pullRequests handlers, and Scheduler integration tests: all passed. ThreadFork integration: 6 passed. Rollback replay fixtures (OrchestratorReplayFixtures.integration.test.ts -t rollback): 5 passed.apps/web:vp test run src/components/ChatView.logic.test.ts -t waitForRevertedMessage(the fix(server): a failed rollback shows an error instead of hanging #13887 web tests): 2 passed.tsc --noEmitclean for contracts, server, client-runtime, web, mobile.vpr knip:checkclean.vp linton touched files: no new warnings (4 existing warnings are on lines this PR doesn't touch).Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code