Skip to content

ltm: first-live-step NaN in dynamic-index per-element link scores (uninitialized PREVIOUS(index) capture helper) #975

Description

@bpowers

Summary

A PerElement link-score partial that reads the live source through a DYNAMIC index -- e.g. growth[Region] = pop[Region, idx] with idx a time-varying aux -- lowers the frozen occurrence to PREVIOUS(pop[region·r, PREVIOUS(idx)]). The nested PREVIOUS(idx) is rewritten through a synthesized capture-helper aux (the builtins_visitor.rs make_temp_arg machinery), and that helper reads an uninitialized value at the FIRST live step, poisoning that one step's link score to NaN. The observed series is 0, NaN, 0.4974..., ... -- the NaN is isolated to the single step (the score equation recomputes each step), so it degrades one sample rather than the whole series, but it is a spurious NaN in a compiled link score that consumers (relative-score normalization, discovery ranking, FFI surfaces) will ingest.

Where it is pinned

The behavior is deliberately test-pinned as HEAD-preserved: per_element_dynamic_index_scores_preserve_head_lag in src/simlin-engine/src/db/ltm_char_tests.rs asserts results[1] IS NaN, because the NaN is the exact discriminator that the dynamic-index lag is present at all (an un-lagged regression makes the same step finite). The test's comment names this as a pre-existing latent bug tracked separately -- this issue is that tracking. When fixed, that guard must be updated in the same change (the discriminator should become: helper initialized AND lag present).

Fix direction

The synthesized PREVIOUS(idx) capture helper needs a well-defined value at the first live step -- e.g. participate in the initials phase so its first read returns idx's initial value (matching PREVIOUS's documented first-step semantics for direct scalar args, which read the prev_values snapshot seeded from initials), rather than an uninitialized slot.

Components affected

  • src/simlin-engine/src/builtins_visitor.rs -- the PREVIOUS helper-aux synthesis (make_temp_arg / previous_needs_temp_arg) and/or its initials-phase participation
  • src/simlin-engine/src/ltm_augment.rs -- the dynamic-index per-element lowering that emits the nested PREVIOUS(idx) form
  • src/simlin-engine/src/db/ltm_char_tests.rs -- per_element_dynamic_index_scores_preserve_head_lag (the pin to flip)

Discovery context

Found during adversarial review of the LTM transform-first restructuring on branch roundtrips-track-a (part of the #965 typed-IR arc), while fixing the lag/freeze dynamic-index finding. Verified byte-identical between HEAD f057ef38 and the restructured tree (identical series, NaN included) -- pre-existing, NOT a regression of that work.

Tracking

Part of LTM epic #488. Related but distinct:

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