Skip to content

engine: two divergent circular-dependency cycle-gate code paths (System A salsa vs System B model.rs:442) #568

@bpowers

Description

@bpowers

Summary

The simlin-engine has two independent circular-dependency detection gates that can disagree about whether a model is simulatable. This is an accepted-scope structural divergence (production uses one path; the other is a legacy test-harness-only path), but it is being deliberately widened by the forthcoming C-LEARN B1 fix and must be tracked so a future change either unifies the two gates or explicitly documents/removes the legacy one.

This item was surfaced during the C-LEARN B1 design (docs/clearn-investigation/B1-design.md; reviewer-priors.md §13.3 O1). O1 acceptance was explicitly conditioned on filing this rather than silently dropping the observation.

The two cycle-detection gates

System A -- production (salsa incremental compile)

compile_project_incremental -> assemble_simulation -> assemble_module -> model_dependency_graph_impl.

  • src/simlin-engine/src/db.rs:1134-1531
  • Cycle detection at db.rs:1316
  • Gate / NotSimulatable emission at db.rs:5004-5015

This is the path every production compile takes.

System B -- legacy, test-harness-only

project.rs::from_salsa -> model.rs::ModelStage1, which has its own CircularDependency, its own topo_sort, its own runlists, consumed by compiler/mod.rs / compiler/codegen.rs.

  • Own CircularDependency at src/simlin-engine/src/model.rs:442
  • pub(crate); exercised only by the model.rs / project.rs test harnesses, not by any production caller.

Why it matters / consequence

The forthcoming B1 fix (approach B -- element-level acyclicity refinement for whole-variable SCCs that are acyclic per element) targets System A only. After B1 lands:

  • System A will correctly compile staged subscript-shift recurrences -- e.g. C-LEARN's pevr / Target idiom, and the ref / interleaved / self_recurrence fixtures.
  • System B will still reject the same models as circular (its model.rs:442 CircularDependency is unchanged).

So any code or test that goes through System B will observe divergent simulatability behavior from production for exactly the class of models B1 is meant to unblock.

This divergence is an accepted scope decision:

  • Production is System A; the C-LEARN production path does not exercise System B.
  • Widening B1 to also refine System B's gate risks the 3-minute workspace test budget and is unrelated to the C-LEARN production goal.

It is accepted, but not free: the divergence must remain visible so a future change makes a deliberate choice -- either unify the two gates behind one implementation, or explicitly document System B as deprecated and/or remove it once nothing depends on it.

Components affected

  • src/simlin-engine/src/db.rs (System A: model_dependency_graph_impl, cycle at :1316, gate at :5004-5015)
  • src/simlin-engine/src/model.rs (System B: ModelStage1, CircularDependency at :442, topo_sort, runlists)
  • src/simlin-engine/src/project.rs (from_salsa, the System B entry point)
  • src/simlin-engine/src/compiler/mod.rs, src/simlin-engine/src/compiler/codegen.rs (System B consumers)

Possible approaches for resolution

  1. Unify: have System B (or its test harnesses) delegate cycle detection to the System A model_dependency_graph_impl path, eliminating the second gate entirely.
  2. Deprecate/remove System B: if model.rs::ModelStage1 / compiler::* is genuinely test-harness-only with no production role, migrate its tests onto the salsa path and delete the legacy path (the repo has no external users; only protobuf compatibility is load-bearing).
  3. Document the divergence as an explicit, intentional limitation in src/simlin-engine/CLAUDE.md and at model.rs:442, with a note that System B is not expected to match System A on element-level-acyclic subscript-shift recurrences.

Option 1 or 2 is preferable long-term; option 3 is the minimum to avoid silent surprise.

Priority

Not urgent. Production correctness is unaffected (production = System A; B1 fixes System A). The risk is purely a latent footgun: a future contributor relying on System B's gate, or a test that asserts on it, would see behavior that contradicts production for B1-class models.

References

Discovery context

Identified during the C-LEARN B1 design (branch work for the pevr / Target staged subscript-shift recurrence). The B1 design and its reviewer priors explicitly require the dual-gate divergence to be filed as tracked tech-debt rather than dropped (reviewer-priors §13.3 O1).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions