Summary
LTM discovery mode (the strongest-path heuristic in src/simlin-engine/src/ltm_finding.rs) silently drops every cross-element feedback loop that traverses a hoisted reducer (a synthetic \$⁚ltm⁚agg⁚{n} aggregate node) more than once -- e.g. pop[a] → agg → pop[b] → agg → pop[a]. Discovery returns only the single-petal (same-element-through-agg) loops; the genuinely cross-element ones never appear, with no diagnostic.
The recovery routine that materializes these loops, recover_cross_agg_loops (src/simlin-engine/src/db/ltm/loops.rs, the petal-stitcher; call site ~line 1056), runs only on the exhaustive Johnson path via build_element_level_loops. Discovery has its own per-timestep strongest-path DFS (ltm_finding.rs) and never feeds recover_cross_agg_loops.
Root cause
A cross-agg loop visits the agg node more than once, so it is not an elementary circuit in the element graph. Discovery's DFS maintains a visiting set (ltm_finding.rs:341, checked at :395, inserted at :411) that forbids revisiting any node on the current path -- including the agg node. So these loops are structurally unreachable by the search: the DFS can never construct a path that returns to agg a second time. This is orthogonal to the cap/under-enumeration in the exhaustive recovery (#515) -- in discovery the recovery never runs at all, so even the loops that #515's MAX_AGG_PETALS=8 would have produced are absent.
Empirical repro
A reducer-in-feedback model growth[r] = SUM(pop[*]) * 0.05 over 3 elements:
- Exhaustive mode emits 7 loop scores: 3 single-petal + 3 disjoint-pair + 1 triple.
- Discovery mode returns only the 3 single-petal loops.
All link scores are present and non-zero in both modes (pop[e] → agg = 0.333, agg → growth[e] = 1.0) -- the cross-element loops are not zeroed out, they are simply never reached by the search.
Why it matters (worst exactly where it bites)
The failure case overlaps the auto-flip condition. A reducer-in-feedback model over a large dimension trips MAX_LTM_SCC_NODES and auto-flips from exhaustive Johnson to discovery -- and at that moment loses its cross-element reducer loops, silently. The model author sees a loop list that looks complete but is missing an entire class of loops, with no truncated-style signal. This is a correctness/completeness gap, distinct from the discovery performance issues (#647, #540) and from the recovery cap (#515) and polarity (#516) issues, all of which assume the loops at least get enumerated.
Misleading design doc
docs/design/ltm--loops-that-matter.md, "Discovery Mode" step 4 (lines 1214-1215), claims:
Discovered element-level loops are grouped and classified identically to exhaustive mode via build_element_level_loops; the synthetic agg nodes are [...]
This is misleading: discovery has its own DFS and never feeds recover_cross_agg_loops, so it does not classify identically -- it cannot recover cross-agg loops at all. The doc should be corrected regardless of which code fix is chosen.
Possible approaches
- (a) Post-process discovery results: stitch the discovered single-petal loops through their shared agg nodes by reusing
recover_cross_agg_loops after the per-timestep DFS, so discovery gets the same cross-agg recovery the exhaustive path has.
- (b) Bounded agg-node revisit in the DFS: permit the
visiting guard to allow a bounded number of revisits specifically for agg nodes, so cross-agg loops become reachable by the search itself.
- Either way: fix the design-doc claim, and consider emitting a diagnostic when cross-agg recovery is skipped/unavailable in discovery mode.
Components affected
src/simlin-engine/src/ltm_finding.rs (discovery DFS, the visiting set at :341/:395/:411)
src/simlin-engine/src/db/ltm/loops.rs (recover_cross_agg_loops, exhaustive-only)
docs/design/ltm--loops-that-matter.md (Discovery Mode step 4, lines 1214-1215)
Relationship to existing tracking
Discovery context
Identified during an LTM deep review while cross-checking discovery vs exhaustive loop output on a reducer-in-feedback fixture. Part of LTM tracking epic #488.
Summary
LTM discovery mode (the strongest-path heuristic in
src/simlin-engine/src/ltm_finding.rs) silently drops every cross-element feedback loop that traverses a hoisted reducer (a synthetic\$⁚ltm⁚agg⁚{n}aggregate node) more than once -- e.g.pop[a] → agg → pop[b] → agg → pop[a]. Discovery returns only the single-petal (same-element-through-agg) loops; the genuinely cross-element ones never appear, with no diagnostic.The recovery routine that materializes these loops,
recover_cross_agg_loops(src/simlin-engine/src/db/ltm/loops.rs, the petal-stitcher; call site ~line 1056), runs only on the exhaustive Johnson path viabuild_element_level_loops. Discovery has its own per-timestep strongest-path DFS (ltm_finding.rs) and never feedsrecover_cross_agg_loops.Root cause
A cross-agg loop visits the agg node more than once, so it is not an elementary circuit in the element graph. Discovery's DFS maintains a
visitingset (ltm_finding.rs:341, checked at:395, inserted at:411) that forbids revisiting any node on the current path -- including the agg node. So these loops are structurally unreachable by the search: the DFS can never construct a path that returns toagga second time. This is orthogonal to the cap/under-enumeration in the exhaustive recovery (#515) -- in discovery the recovery never runs at all, so even the loops that #515'sMAX_AGG_PETALS=8would have produced are absent.Empirical repro
A reducer-in-feedback model
growth[r] = SUM(pop[*]) * 0.05over 3 elements:All link scores are present and non-zero in both modes (
pop[e] → agg = 0.333,agg → growth[e] = 1.0) -- the cross-element loops are not zeroed out, they are simply never reached by the search.Why it matters (worst exactly where it bites)
The failure case overlaps the auto-flip condition. A reducer-in-feedback model over a large dimension trips
MAX_LTM_SCC_NODESand auto-flips from exhaustive Johnson to discovery -- and at that moment loses its cross-element reducer loops, silently. The model author sees a loop list that looks complete but is missing an entire class of loops, with notruncated-style signal. This is a correctness/completeness gap, distinct from the discovery performance issues (#647, #540) and from the recovery cap (#515) and polarity (#516) issues, all of which assume the loops at least get enumerated.Misleading design doc
docs/design/ltm--loops-that-matter.md, "Discovery Mode" step 4 (lines 1214-1215), claims:This is misleading: discovery has its own DFS and never feeds
recover_cross_agg_loops, so it does not classify identically -- it cannot recover cross-agg loops at all. The doc should be corrected regardless of which code fix is chosen.Possible approaches
recover_cross_agg_loopsafter the per-timestep DFS, so discovery gets the same cross-agg recovery the exhaustive path has.visitingguard to allow a bounded number of revisits specifically for agg nodes, so cross-agg loops become reachable by the search itself.Components affected
src/simlin-engine/src/ltm_finding.rs(discovery DFS, thevisitingset at :341/:395/:411)src/simlin-engine/src/db/ltm/loops.rs(recover_cross_agg_loops, exhaustive-only)docs/design/ltm--loops-that-matter.md(Discovery Mode step 4, lines 1214-1215)Relationship to existing tracking
MAX_AGG_PETALS=8cap/under-enumeration: assumes recovery runs; this issue is that discovery never runs recovery.Undetermined(agg-hop polarity): about classifying loops that exist; this issue is about their total absence in discovery.Discovery context
Identified during an LTM deep review while cross-checking discovery vs exhaustive loop output on a reducer-in-feedback fixture. Part of LTM tracking epic #488.