Skip to content

ltm: model_detected_loops has no stateless gate -- stateless-but-cyclic shapes report unscoreable loops and silently drop pins #772

@bpowers

Description

@bpowers

Problem

model_detected_loops (src/simlin-engine/src/db/analysis.rs, ~line 2082; post-#746 it reuses the scored loop builder) has no stateless gate. For stateless-but-cyclic model shapes it still REPORTS enumerated loops, while the scored surface (model_ltm_variables, src/simlin-engine/src/db/ltm/mod.rs) bails via its stateless early return and emits zero LTM variables -- so the reported loops are detected-but-never-scorable. And because the bail happens before pin processing, a SetLoopName pin over one of those (reported!) cycles is silently dropped with no diagnostic -- the #770 silence class, except here the user can see the loop in the detected surface, making the silence more surprising.

#749 fixed the parent-level-PREVIOUS instance of the surface disagreement. The remaining stateless-but-cyclic shapes post-#749:

(a) Instantaneous cycles through stockless PASSTHROUGH modules. The parent-level cycle is broken at the module boundary, so it is not a compile-time CircularDependency -- but there is no state anywhere in the model.

(b) MODULE-INTERNAL-PREVIOUS cycles. Deliberately uncounted by model_is_stateless so the detected and scored surfaces agree on what counts as state (see the sibling issue on module-internal PREVIOUS, filed in the same #749/#750 batch).

For both shapes: model_ltm_variables returns zero vars with no invalid-pin warnings, while model_detected_loops reports the enumerated loops.

Note this also narrows #770's mitigating-scope claim that "a stateless model with a CYCLE is a loud compile-time CircularDependency anyway": shapes (a) and (b) are stateless, cyclic, and compile cleanly, so the silent-pin-drop case is not limited to acyclic models.

Why it matters

Components affected

  • src/simlin-engine/src/db/analysis.rs (model_detected_loops, no stateless early-out)
  • src/simlin-engine/src/db/ltm/mod.rs (model_ltm_variables stateless gate / model_is_stateless)
  • src/simlin-engine/src/db/ltm/pinned.rs (model_pinned_loops, unreachable on this path)

Possible approaches

  1. Give model_detected_loops the same model_is_stateless gate, returning pins-only or empty, mirroring the scored surface's semantics (the two surfaces then agree that stateless models have no scoreable feedback); or
  2. Keep the detected output but emit the ltm: pin on a genuinely stateless model bypasses validation -- typo'd pin gets no invalid-pin warning #770-style "pins ignored: model has no state" Warning from a surface that CAN accumulate diagnostics, so the asymmetry is at least loud.

Either way the resolution should be coordinated with #770 (same warning text/mechanism) so stateless pin handling is uniform.

Cross-references

How discovered

Identified as a residual during GH #749/#750 work on branch ltm-fix-batch-2 (commits 69e84d8e/03a9ec1f), 2026-06.

Absorbed #770

#770 (pin on a genuinely stateless model bypasses validation -- typo'd pin gets no invalid-pin warning) was closed as consolidated into this issue: both need the same warning mechanism on the same gate, and this issue's fix (a stateless gate or warning shared across the detected/pin surfaces) delivers #770's typo'd-pin warning as a side effect. The adjacent cleanup from #770's body also rides here: compile_ltm_implicit_var_fragment's dead _dep_graph parameter (db/ltm/compile.rs ~1683), which both callers compute and pass for nothing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ltmLoops that Matter (LTM) analysis subsystem

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions