Cyclic or over-deep $ref graphs stack-overflowed the three standalone
schema walkers (OffsetMap::compute, LayoutBuilder::new,
materialize_aligned) — SIGABRT on probe, parity-preserved from 0.2.0.
- New src/walk_guard.rs: check_ref_graph() — one bounded walk over the
reachable reference graph (depth cap 128 matching the plan compilers,
path-scoped cycle set; diamonds allowed, cycles and 201-def chains
rejected with the plan compilers' error wording)
- All three walkers run the guard at entry, before any recursion;
materialize_aligned's is defense-in-depth (a cyclic doc can no longer
produce an OffsetMap, but mismatched doc/map inputs must still fail
cleanly)
- Behavioral side effect, net-positive: the guard eagerly parses every
reachable def, so an invalid non-root def now surfaces at
LayoutBuilder::new instead of build() — four H3 tests updated to
expect the same Schema error earlier
- Test family: 12 new tests (walk_guard, offset_map, layout_builder,
materialize) covering self/two-def/composite-carrier cycles, deep
chains, and diamond non-rejection; no stack-overflow reproducers
in-tree per the review's Methodology warning
- Stale "walkers have no cycle guard" statements updated in
validation.md, 030 plan, ADR-012, and the engine gate comment
Verified: 501 tests green (423 + 17 + 34 + 15 + 12 + 2 ignored),
clippy -D warnings clean, wasm32 build green, cargo doc zero warnings.