AI3D-388 · branch t3code/implement-fence-detection
· 2026-09-08 · 628 tests green · channel still default-off · off-state
byte-identical to main (verified this pass)
Where we are. 312 candidates over the 25 segments that
produce any → 29 accepted. Against your own verdicts: 7 of 7
fences kept, both crop edges rejected, the hedge rejected. Against the earlier
agent labels: 14 of 14 fences kept. Recall on the training split was already
settled two passes ago; this pass was about precision and about the class
boundary you drew.
What is left is 19 candidates nobody has judged. They are
all on one page — the judging
page — with their renders and metrics. A line per id turns into a label
file, and the thresholds follow from it.
What worked
1 · Your verdicts, applied: 13 for 13 on the objects you judged
110/5 ACCEPTED - the fence in grass. Crest 0.56 over all cells, 0.80 about its own line093/3 ACCEPTED - crest 0.31, kept by the narrow-sheet exemption (width 0.44 m, sheet 0.94)028/8 REJECTED [vegetation] - the crop edge, sheet 0.17
Every object you called a fence is accepted; both objects you called not-a-fence are rejected; the hedge is rejected. The two exceptions are the ones you said either way was fine (127/3, 049/3) and the guardrail 015/2, which is below.
2 · Two bodies the first cut of this pass would have accepted
039/6 REJECTED [vegetation] - 0.91 m thick, 88% green. Pass 8 accepted it077/2 REJECTED [vegetation] - 56 m panel array, 0.79 m thick, sheet 0.26. Pass 8 accepted it
Both were found by the code review, not by me. Two separate mistakes let them in.
The sloped crest reading had inverted physics. The top profile is a height above ground, so a fence that merely follows the terrain is already flat by the plain reading — what the sloped reading actually admits is a top whose height above ground ramps. And capping the grade at 8 % let the acceptance window grow with length: over 30 m a line may wander 2.4 m, nine times the flat reading's own tolerance, which is enough for a six-bin fit to find a hedge top. The cap is now on the end-to-end drift (0.50 m at any length).
The thin-sheet exemption was an OR, and each half fails on a real object. The fitted width alone exempted your crop edge 028/8 — the ridge fit trims it to 0.36 m. The sheet share alone exempted these two: a body seen through its own occlusion puts a quarter of its upper mass in one column. It is now an AND, and every fence you named clears both readings.
What did not work
3 · The third echo does not separate the classes
299 candidates. Fences span the whole third-echo range; the sheet share is what splits them028/7 ACCEPTED - the crop segment again, sheet 0.37, third echo 0.23. Needs your verdict
The sign-detection repo separates foliage from hard objects on the share of pulses that split three ways or more — trees ~0.10 against 0.01–0.02 for sign plates — so I added that plane to the fence grid. On fences it does not hold: they run from 0.00 (the wooden post-and-rail 048/8) to 0.57 (103/6), straight through the crop edges at 0.43. That repo warns of exactly this in its own research notes — a mesh fence legitimately multi-echoes, because the pulse goes through the mesh. The metric ships measured and disarmed; sheet_frac_upper is doing the separation instead, and the dashed line is where it now sits.
Post cadence is still degenerate (~2.0 m for everything) and still reads no gate.
4 · The one false positive I cannot fix from this channel
015/2 ACCEPTED - you called it a guardrail. Opaque, 1.25 m, 43.8 m
On every measurement this channel takes, 015/2 and your wooden post-and-rail fence 048/8 are the same object: both opaque (6 % and 12 % multi-return), both flat-topped, both about the same height (1.25 m against 1.45 m), both thin. Any rule that deletes one deletes the other. The clean fix is arbitration — the guardrail channel claiming it — and you also said that segment's guardrail detections are themselves false positives, so there is nothing to arbitrate against yet. Logged, not tuned around.
A candidate id is an ordinal of one run, so a label file keyed by id
compares the wrong objects the moment a run adds or drops a candidate — which
is what made the last report attribute losses to the wrong fences. Labels now
transfer between runs by place (segment, side, offset, station
overlap), labelled objects match first, and stale keys are reported apart from
real recall losses. With the honest join, the pass-6 numbers above are
themselves a correction of what the last report claimed.
Next
Your 19 verdicts — that is the whole of the next pass. Two
of them (028/7, 092/9) sit in the same crop field you have already ruled on
once, and one (039/11) is accepted only because its profile is too short to
measure, which is the null-safe behaviour but worth a look.
Widen the corridor past 45 m. The train recall audit found
one likely fence at ~46 m, just outside. The seed rule already stops far-only
clutter from being proposed, so this is cheap to test.
Hedges as their own class. You said you would not be
against it. Today they are silent rejects; emitting them would make the
vegetation rule's decisions auditable instead of invisible.
Cross-segment stitching stays the one architectural gap: a
fence crossing a segment boundary is fitted twice and measured short.