Repository navigation
perf(server): ACP item ordinals no longer copy a session-wide map per item - #13868
Conversation
… item The ACP V2 adapter (Grok, Devin, Antigravity and registry agents) kept every item ordinal of the session in one Map that it copied on each new item and never pruned, so a long session paid O(items) memory and O(items^2) copying. Root item ordinals are only looked up by the turn that allocated them, so that map now lives on the turn. Subagent child items used the same map with ordinals from the subagent's own counter; they now keep them on the subagent, which already carries over into later turns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a localized ACP adapter optimization that bounds ordinal-map lifetime and removes quadratic map copying while preserving per-turn and per-subagent ordinal allocation. It introduces no new capability, schema, product-default, security, billing, or static-analysis changes. You can add or adjust custom eligibility rules. Learn more. |
| return updated; | ||
| const resolveItemOrdinal = (context: ActiveAcpTurn, nativeItemId: string) => | ||
| Effect.sync(() => { | ||
| const existing = context.itemOrdinals.get(nativeItemId); |
There was a problem hiding this comment.
Scoping ordinals to each turn changes the result when a native item ID recurs in a later turn: it now receives that turn's ordinal instead of the earlier turn's. Could you add a focused adapter regression test covering reused IDs across turns and stable ordinals on repeated updates? This backend behavior change needs test coverage.
Posted via Macroscope — Effect Service Conventions
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. |
52d84bd
into
t3code/codex-turn-mapping
… item (pingdotgg#13868) (cherry picked from commit 52d84bd) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The ACP V2 adapter (used by Grok, Devin, Antigravity and the registry agents) has the same item-ordinal problem as Claude (#13864) and Cursor (#13866). Every item ordinal of the session sat in one
Mapthat was copied on each new item and never pruned. Only a rollback's runtime replacement cleared it. A long session paid O(items) memory and O(items²) copying.What changed
ActiveAcpTurn.itemOrdinals). It is mutated in place and goes away when the turn settles. The per-turn counter map is gone, because the map's size is the counter.nextChildOrdinal. They now keep that cache on the subagent (childItemOrdinals). The subagent record already carries over into later turns (carryoverSubagents), so a child update that arrives after the root turn settled still finds its ordinal.Why per-turn is safe for root items
Every
resolveItemOrdinalcall passes the active turn. A subagent keeps its ownturnItemOrdinalon its record (existing?.turnItemOrdinal ?? resolveItemOrdinal(...)), including across carryover. Before the fix I instrumentedresolveItemOrdinalto flag any hit on an ordinal a different turn had allocated, then ran all replay suites plus the ACP, registry and Grok adapter tests: 260 ACP allocations, 3 cross-turn hits. All 3 came from the mock ACP agent inAcpAdapterV2.test.ts("waits for immediate allow and deny permission responses…"), which hardcodestoolCallId: "tool-call-1"on every prompt. A real agent does not reuse a tool call id across prompts. Even for that mock, the only effect of the fix is that turn 2's tool gets a turn-2 ordinal instead of reusing turn 1's. The test does not assert ordinals, and it passes unchanged.Benchmark
This is the same resolver shape as Claude and Cursor. A scratch, uncommitted script ran it before and after, in 10 turns, resolving each item twice:
A 50k-entry map retains about 1.8 MiB for the life of the session. After the fix, root-item memory is bounded by the largest single turn, and child-item memory by the subagent's lifetime.
Verification
vp test runwithAcpAdapterV2.test.ts,AcpRegistryAdapterV2.test.ts,GrokAdapterV2.test.ts,AntigravityAdapterV2.test.ts,OrchestratorReplayFixtures.integration.test.ts(which includes the Grok and registry replay fixtures: background bash, background subagent, monitor, subagent lineage) andOrchestratorReplayRecovery.integration.test.ts: 6 files, 244 tests pass. The replay fixtures are unchanged.vp exec tsc --noEmit -p .inapps/server: no errors.vp linton the touched file: only the existing unusedNodePathimport warning.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code