Skip to content

docs: full verification pass of ltm--loops-that-matter.md against the LTM code (build_partial_equation is gone; recompute passage describes the adapter, not ModuleOverrideCache; module-role taxonomy is prose-only) #1049

Description

@bpowers

Problem

docs/design/ltm--loops-that-matter.md (2,188 lines) is the LTM implementation design doc that docs/README.md, docs/architecture.md, src/simlin-engine/CLAUDE.md and the engine's own auto-switch warning text (db/ltm/mod.rs, "see docs/design/ltm--loops-that-matter.md for the two-tier strategy") all point at. The repo rule is that docs are evergreen and true of the tree.

The documentation chunk of the compiler-unification-v2 branch (PR #1040, commit 78d0195a) rewrote and verified the describer sections against the code: the db/ltm/ module table, the RefShape variants including PerElement, compute_read_slice, node identity as the spelled reducer, BareSpelling, occurrence_realizes_shape, and the residual carve-outs as the code's current declines. The rest of the document -- the two-modes, module-classification, module-boundary / composite, polarity, post-simulation discovery, array, pinned-loop and test-coverage sections -- still describes the machinery as of 2026-05/06 and was NOT verified name-by-name or claim-by-claim against the current tree. That verification is its own chunk, and this issue tracks it.

Line numbers below are as of 78d0195a.

Concrete evidence (spot checks, not a full pass)

Stale name (absent from the tree):

  1. build_partial_equation (lines 368 and 2093). Nothing in src/ is named that. The builders are build_partial_equation_shaped (src/simlin-engine/src/ltm_augment.rs:1051) and build_partial_equation_shaped_with_live_ref (:1102). Line 368 attributes the module-ref-aware normalize_module_ref() behaviour to the old name; line 2093 lists it under Test Coverage.

Names that exist but whose surrounding prose has drifted (this is why a name-existence sweep is not sufficient):

  1. recompute_module_input_edge_series (lines 496, 552, 582). The function exists (src/simlin-engine/src/ltm_finding.rs:2168), but its own rustdoc now calls it "the links-slice ADAPTER kept for call sites (and tests) that still hold a Link sequence ... Production no longer calls this directly for a sequential edge -- see ModuleOverrideCache::series". The engine is recompute_module_input_edge_series_for (:2036), memoized by ModuleOverrideCache (:2235). The doc has zero mentions of ModuleOverrideCache or the _for variant, so "This is recompute_module_input_edge_series in ltm_finding.rs" (line 552) and the subscript-stripping attribution (line 582) describe the adapter, not the production path.

  2. "Module Classification" (lines 426-440) states that "Modules fall into three LTM roles: Infrastructure, DynamicModule, Passthrough" as if it were a code-level taxonomy. DynamicModule occurs in the tree only inside comments (ltm_finding.rs:2049, ltm/graph.rs:462, db/ltm_module_tests.rs:415); Infrastructure and Passthrough occur nowhere in ltm/graph.rs, ltm_finding.rs or db/ltm/mod.rs. Whether the three-role prose still matches how model_ltm_variables / modules_carry_state actually decide is unverified.

Checked and found consistent (so the pass does not have to re-litigate these):

  1. The path0 / path1 worked example (lines 475-477) is illustrative, not an identifier. Its claim -- >= first-index tie-break picks the earlier pathway when magnitudes tie -- matches the discovery recompute's let keep_earlier = slot.abs() >= candidate.abs(); in ltm_finding.rs. The emitted composite equation's textual form was not checked.
  2. All seven file paths named in section headings exist (ltm/graph.rs, ltm/types.rs, ltm/partitions.rs, ltm_finding.rs, ltm_finding_enum.rs, ltm_finding_fallback.rs, tests/integration/simulate_ltm.rs), and FoundLoop, CausalGraph, Link, Loop, CyclePartitions are defined where the headings say.

Changelog-style narration (the evergreen rule in the root CLAUDE.md forbids it; the V10 narration rg used a narrower pattern and did not match these): 10 lines, e.g. line 438 "now fires only for", line 600 "Before this fix, discovery read the composite's offset", line 843 "the exhaustive path now uses the all-slots reading", line 588 "This defense is LIVE, not latent, since GH #716 closed", and PR review-comment IDs (r3353459409, r3353597299, r3353097150, r3353758167) at lines 489, 492, 543, 586. GH issue numbers are fine as pointers; review-comment IDs and "before/now" framing are not.

Bounded sweep result (the floor the pass starts from)

Extracting every backticked snake_case / CamelCase identifier from the doc gives 285 distinct names; 278 resolve via rg -F -w in src/simlin-engine/{src,tests}. Of the 7 that do not, 5 are libsimlin / pysimlin surface names present elsewhere in src/ (get_loops, simlin_analyze_discover_loops, simlin_analyze_get_loop_element_count, SimlinDiscoveredPartition, SimlinModel), 1 is an example variable (x_rate), and 1 is genuinely stale (build_partial_equation, item 1). Items 2 and 3 pass this sweep, which is the point: name existence is necessary, not sufficient.

Measurement (name existence only):

grep -o '`[A-Za-z_][A-Za-z0-9_:]*`' docs/design/ltm--loops-that-matter.md | tr -d '`' \
  | grep -E '_|::|^[A-Z]' | sort -u \
  | while read -r id; do rg -q -F -w "${id##*::}" src/simlin-engine/src/ src/simlin-engine/tests/ || echo "$id"; done

Why it matters

  • The doc is the designated entry point for LTM (four in-repo pointers, including a runtime warning string). A reader who follows item 2 to recompute_module_input_edge_series lands on the adapter and would reasonably conclude production strips subscripts there; the cache is where the production decision lives.
  • scripts/check-docs.py validates path references but deliberately blanks inline code spans, so no gate covers backticked identifiers or backticked .rs names in docs/design/. Drift here is invisible to CI and to review (reviewers check code against the doc's claim, not the claim against the tree).
  • Sibling rule already tracked for CLAUDE.md files: tech-debt when placing a new element, highlight text #61. This issue is the design-doc counterpart, scoped to one file.

Affected

docs/design/ltm--loops-that-matter.md against src/simlin-engine/src/db/ltm/, src/simlin-engine/src/db/ltm_ir.rs, src/simlin-engine/src/ltm_*.rs (ltm_augment*.rs, ltm_finding*.rs, ltm_agg.rs), src/simlin-engine/src/ltm/, src/simlin-engine/src/analysis.rs.

Possible approach

Doc-only chunk, its own PR (no .rs changes expected):

  1. Section by section (Two Modes ~96-158; Cycle Partitions 159-196; Synthetic Variables / Naming 197-283; Link Score Equations 284-418; Module Boundary Handling 419-688; Polarity 689-844; Post-Simulation Discovery 845-1269; Array Support 1270-1843; Pinned Loops 1844-1910; Limitations / Divergences 1911-2079; Test Coverage 2080-2181): for every backticked name run rg -F -w; for every behavioural claim name the function or the test that pins it, or rewrite the claim to what the code does.
  2. Fix items 1-3 above; add ModuleOverrideCache / recompute_module_input_edge_series_for to the discovery-recompute passage; restate the module-role paragraph in terms of the actual decision points (model_ltm_variables, modules_carry_state, find_model_output_ports, the pathway map) rather than a named taxonomy the code does not have.
  3. Rewrite the 10 narration lines as standing constraints (e.g. "the recompute strips subscripts before every name comparison; the exhaustive twin compute_module_link_overrides does the same" instead of "This defense is LIVE ... since GH ltm: scalar module output feeding an arrayed reader emits a single scalar constant-0 link score, dropping all arrayed loops through modules #716 closed"); drop the review-comment IDs.
  4. Re-run the Test Coverage section's test names against #[test] fn definitions (they are in the 278 that resolve today, but check they still test what the bullets say).
  5. Optional prevention, stated as a necessary-condition gate only: extend scripts/check-docs.py (or add a sibling) so every backticked snake_case identifier in docs/design/*.md must resolve with rg -w somewhere in src/. It would have caught item 1 and would not catch items 2-3; say so in the check's docstring.

Discovery context

Out-of-scope discovery of the V10 documentation chunk on the compiler-unification-v2 branch (PR #1040, commit 78d0195a "doc: engine map, ledger and evergreen design docs"). Related tracking: epic #488 (LTM); tech-debt #61 (CLAUDE.md evergreen rule). No open issue covered this (searched "ltm--loops-that-matter.md", "LTM design doc", "docs stale", "loops-that-matter").

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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