diff --git a/docs/decisions.md b/docs/decisions.md index 22ffd7f..a4dcd47 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -676,10 +676,12 @@ though `hasRead` states the same stop rule. Valid while the two differ in where ## The four path walks are four loops 2026-09-28, larv-review on systems-architect's finding; approved 2026-09-28 by lilleman. -Goals 2 and 11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` each loop over -`stepInto` and `table.route` themselves, and `descend` builds its probe in place, so the -probe's `drawn` map stays on the stack; its caller always builds that map, so `readRow` -never tests it for nil. One walk calling them through an interface or a type parameter -leaks the walker and the pins it holds, an allocation per reference read. Valid while -Go's escape analysis treats a call through an interface or a type parameter as leaking -its receiver. +Re-tested 2026-09-29 by collapsing them into one loop switching on a mode field. +Goals 2 and 11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` stay four loops. +One loop fails `perf_test.go`: the check's leaves, the probe's selectors and the errors +naming a level leak the walker's contents, and Go tracks a struct's fields as one, so +the draw's pins, arm and hold leak with them. Held by pointer, a repeat of a reference +path rose from 66 to 106 allocations; copied into the walker by value, +`BenchmarkHeld` rose from 5 to 9. A call through an interface or a type parameter leaks +the walker the same way. Valid while Go's escape analysis tracks a struct's fields as +one and treats such a call as leaking its receiver. diff --git a/todo.md b/todo.md index 1a414fe..c91878e 100644 --- a/todo.md +++ b/todo.md @@ -31,13 +31,6 @@ on 2026-09-29 a third time (Navigation 7.0, Locality 5.6, Shape 6.3, Self-suffic This round, in order: -- Collapse the four path walks into one loop switching on a plain mode field, check, - cover, probe or draw, with no call through an interface or a type parameter, and run - it against `perf_test.go`'s allocation gate. It holds: replace the decision "The four - path walks are four loops" with one recording a single loop, and land the collapse; - all nine seats named the four loops among the hardest. It fails: keep the loops, and - rewrite that decision's rationale to match `descend` and `pathProbe.readRow` today, - where `readRow` makes `drawn` on first use. - Key the survey's visits and reads by a pin set's rows spelled as a string, and delete `pinsID`, `pinsLink` and `readSurvey.pinIDs`: five seats stalled on the interning. Start `replayPairs`' pin set as a clone of the earlier read's pins, deleting the