mcp: batch-first apply_patch guidance - #3
Open
ActArtech wants to merge 1 commit into
Open
Conversation
Tool description, agent guide, from-brief preamble, and README now instruct agents to compose one atomic apply_patch batch per phase instead of looping single-op calls. The tool already validates all ops before applying any; only the guidance was missing. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
ActArtech
pushed a commit
that referenced
this pull request
Sep 6, 2026
…Mesh containers (pascalorg#760) * perf probe: drawComposition() — per-item/per-asset/per-kind draw census Feeds charter backlog #3: projected draws for per-item merge vs per-asset instancing, plus a meshes-by-kind bucket for the non-item side of the budget. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * spike: BatchedMesh through the WebGPU pipeline (?spike=batch) 400-4,900 instances, two geometries, one material, shadows on. Verdict: renders/shadows/post-FX clean; ~0.3us encode per instance (~10x cheaper than a real mesh); per-instance frustum culling active (draw/tri track the camera). WebGPU counts each multi-draw segment in info.render, so the 3a metric is encode/frame-cpu ms, not draw calls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * Item draw batching v1: per-material BatchedMesh with wall-batch semantics (charter 3a) Level-parented items render through one BatchedMesh per (level, material, attribute-signature) — membership follows the dirty signal, lit items release and draw themselves (outline/selection paint untouched), joins wait for a quiet window, isolation stands the whole thing down, and 'thumbnail:before-capture' hands every item its meshes back so exports never see a batch (belt: batches carry pascalExport='strip'). The container is incremental — instance add/delete per membership change, geometry deduped per batch, capacity grown 2x on overflow. An existing batch always accepts a rejoining item; only new batches need MIN_BATCH_ENTRIES. v1 scope: items whose parent is a level; hosted items (wall/ceiling/roof) move on host edits that never dirty the item, so they keep drawing themselves. Interactive/animated/transparent items excluded by the same rule as walls. Rich 2x: 114 of 270 items batched (304 instances, 37 batches), idle frame cpu 10.65 -> ~8.3ms. Full matrix + gates follow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * item batch: prune batches orphaned by level-subtree remounts + probe surface A React remount of a level subtree (thumbnail capture level shuffling, tool-state swings) replaces the registry groups: imperatively-parented batch meshes die with the old group while fresh source clones mount with no layer hold. pruneDetached() spots the orphaned batches by parent identity each frame and re-stales their items. ?perf probe gains stats/wave/census/batchRender hooks for scripted verification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * ?perf: draw row shows batched instance share On WebGPU each batched instance still counts once in drawCalls (the backend loops drawIndexed per visible instance), so a batched scene looked no cheaper by the panel's DRAW number. The row now reads e.g. '581 (272 batched · 2 mesh)' — live per sample, so per-instance frustum culling is visible as the camera moves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * ?perf: batch row shows membership truth, not per-pass culling The first cut read _multiDrawCount, which snapshots whichever camera culled the batch last — shadow vs main vs outline passes made the number flip (206 -> 11 on hover) and read as items dropping out of the batch. The batch system now publishes its membership (items/instances/ containers) to the panel store; the row is stable and only moves when membership actually changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * Node batching 3b: doors, windows and columns join the batch (charter 3a+3b) item-batch generalizes to shared/node-batch: same wall-batch semantics, BatchedMesh containers per (level, material, attribute-signature), now covering items, columns and wall-hosted openings (resolved to the host wall's level). New release triggers: a dirty WALL cascades to its openings (the wall edit moves them without marking them), a host wall that is lit or mid-gesture releases its openings, and a door/window whose animation record appears draws itself for the tween. Hitboxes (material.visible=false) and glass (transparent) never batch. Bake pages (?disable=draw) stand batching down entirely, and the release- everything paths sweep every level subtree for stale 'batched' holds — a system can rebuild a batched node's meshes and orphan the tracked refs, and a stale hold is exactly what the GLB exporter would prune. Rich 2x: 188 nodes / 1,108 instances batched, in-page A/B frame 9.1 -> 7.4ms (-19%); rich 4x: 2,280 instances, 19.3 -> 14.1ms (-27%). Gates: e2e 8/8; bake tri+byte parity batching-on vs off; in-session pixel diff 0.4% confined to seam AA and transparent blend order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * lint: sort node-batch imports/exports, format store Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: id-diff membership + per-batch geometry refcounts (Bugbot findings) A same-size add-and-remove slipped past the count tell and left the removed node's instances drawing as ghosts — membership now diffs ids against last frame's registry. Released geometries drop their packed mapping at refcount zero, so a rejoin re-packs current vertex content (a rebuilt geometry can reuse its uuid) instead of the copy captured at first join; the orphaned range stays until the existing overflow rebuild reclaims it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: content-stamped geometry reuse + container culling off (Bugbot round 2) The refcount fix traded one problem for another: dropping the packed mapping at zero made every hover release/rejoin cycle re-pack the same geometry, inflating used until premature overflow rebuilds. The mapping now survives cycles and carries a content stamp (position version + counts) — reuse is free, and a geometry rebuilt in place under the same uuid re-packs instead of instancing stale vertices. Whole-container frustumCulled goes off: its bounding sphere is computed at first cull, so instances joining farther out later could vanish with the whole batch — per-instance culling already owns visibility. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: hosted child nodes' subtrees stay out of the host's candidate walk (Bugbot round 3) An item can host other items (a shelf's books) whose registered groups mount inside the host's group — the walk packed those meshes as the HOST's instances, freezing the child at join pose with no release of its own. The walk now cuts at every hosted child's registered group; hosted nodes keep drawing themselves (they are outside v1 batch scope by the level-parent rule anyway). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: hidden-wall openings, autoplaying clips, late-copy gathering (Bugbot round 4) - A hidden wall hides its openings through group visibility; batch instances hang off the level root and kept drawing them. Openings of an invisible wall are no longer candidates (the wall's visibility dirty-mark cascades the release). - ItemAnimation autoplays a GLB's first clip even without an interactive effect; such items batched and froze mid-motion. The renderer stamps clip presence on the registry group and candidates exclude it. - Below-threshold candidates were dropped after their wave, so copies placed more than a settle window apart never gathered into a batch. A new id now re-stales every unbatched node, letting the copy that crosses MIN_BATCH_ENTRIES pull earlier ones in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: partial members re-offer leftovers on new placements (Bugbot round 5) + import sort store.has locked a partially-joined node (some meshes under the new-batch threshold) out of every later wave. Partial members are now tracked; a new placement releases them for a full re-collect, so the copy that makes a leftover bucket viable pulls them in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * lint: format node-batch system Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK * node batch: overrides release + deferred-peer gathering (Bugbot round 6) Live overrides now release an already-batched node (a collaborator's remote drag or a programmatic move carries no local selection to tint it) and defer, rather than drop, a stale one — the commit mark re-offers it. Threshold-short candidates land in a leftover set that re-offers as a group when a NEW leftover arrives, so a copy that was deferred (selected, dirty, loading) while its peers' wave ran can still gather them into a batch; a stable leftover set re-offers nothing and small scenes stay quiet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nq2rLE18tFVES2LU6HGokK --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Makes batch-first
apply_patchusage the stated default across the MCP agent-facing guidance (idea P1 fromcontent/ideas/mcp-speed-and-batching.md). The tool already validates every op before applying any (atomic, single undo step) — but nothing told agents to use that batch instead of looping single-op calls, which multiplies the O(N) snapshot/save pipeline per call.Four guidance surfaces updated, no logic changes:
packages/mcp/src/tools/apply-patch.ts— tool description: batch-first is the default, do not loop one-op callspackages/mcp/src/resources/agent-guide.ts— Scene Creation Rules bulletpackages/mcp/src/prompts/from-brief.ts— "One patch per phase" preamblepackages/mcp/README.md— apply_patch table rowHow to test
bunx tsc --noEmit -p packages/mcp(verified green on this branch)Screenshots / screen recording
N/A — guidance-text change only, no runtime behaviour change.
Checklist
bun checkto verify)fix/camera-framing(stacked; will retarget once the base merges)