MEM-027: patch_walk unbounded BASS carving exhausts the arena (sniff/PATCH.c)

Recursive patch_walk a_carves ~29MB per directory level (3×4MB trees + 16MB blob + entry arrays) and recurses into every subdir with no depth bound; the plain recursive call keeps each parent's carve live for the whole subtree, so a tree ~34 levels deep overflows the 1GB ABC_BASS. The leaf loop also issues (void)fetch_blob / (void)GRAFMerge3Bytes / (void)GRAFMergeWtFileTunable as plain calls, so any callee BASS scratch is never rewound per iteration and accumulates with file count. a_carve returns BNOROOM (graceful, no OOB), so this is resource exhaustion that fails deep/large trees unpredictably. The goal is to bound depth and reclaim per-leaf scratch.

Issues

Per-level carve held across recursion; per-leaf scratch never rewound.

Blockers

None.

WIP

Re-verified 2026-07-07: patch_walk was split into a scratch-owning wrapper (sniff/PATCH.c:959-978) + patch_walk_inner (:450), but the recursion at :563 is still a plain C call into the wrapper, so each level re-carves 3×4MB trees + 16MB blob + 3× entry arrays (:969-975, PATCH_TREE_BUF/PATCH_BLOB_BUF at :51-52) held live for the whole subtree; still no depth cap. The :966 "single allocation reused" comment holds per level, not across levels.

TODOs

- sniff/PATCH.c:563 — recurse through try() (or an explicit bounded work-stack) so each subtree's ~28MB carve rewinds on child return, and add a recursion-depth bound with a clear error. - sniff/PATCH.c:697,790,797,831,856,926 — leaf (void)fetch_blob/(void)GRAFMerge3Bytes/(void)GRAFMergeWtFileTunable are still plain calls; wrap in call()/try() or bracket the leaf-loop body with a BASS mark/rewind. - Repro FIRST (CLAUDE.md §17): drive PATCHApply against a deeply nested tree (and a wide many-file tree); assert no BNOROOM at sane depth.

Blockers and bummers

None.