You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The ceteris-paribus partial builders in src/simlin-engine/src/ltm_augment.rs (build_partial_equation_shaped -> wrap_non_matching_in_previous -> wrap_index_non_matching_in_previous) PREVIOUS-wrap a bare dimension-name identifier sitting inside a subscript index. Originally filed against the live Wildcard reference shape (SUM(matrix[State, *]) becoming SUM(matrix[PREVIOUS(state), *])), the defect is also reachable through a mundane Bare-shape apply-to-all equation with no reducer at all (see "Demonstrated reachable, fully silent path" below), where the dimension name leaks into the co-source other_deps set and the frozen co-source's subscript index gets wrapped.
PREVIOUS of a dimension name is meaningless: the fragment fails compilation and the synthetic link-score variable is silently stubbed -- the #546/#548/#587 hazard class ("LTM fragment compiler can't handle a shape -> synthetic var stubs to 0 -> degraded score masquerades as inactive"). Correction to the original filing: when the doomed expression routes through a synthesized implicit helper, the failure produces no diagnostic at all -- model_ltm_fragment_diagnostics never covers implicit helpers (the #741 class) -- so the original claim that the defect "is surfaced as an Assembly Warning when reached" is wrong for this route.
Verified independently by an implementor (discovery) and an adversarial reviewer (probe) during GH #743 work on branch ltm-fix-batch-2 at commit 7012d9f0:
growth[D1] = matrix[D1, c1] * frac[D1]
A Bare-shape apply-to-all equation with a pinned literal co-source index (c1), no reducer involved. The changed-first partial for the frac -> growth edge freezes the co-source as
PREVIOUS(matrix[PREVIOUS(d1), d2·c1])
-- the iterated-dim named1 is PREVIOUS-wrapped inside the subscript index.
Mechanism: the A2A/scalar link-score generators build their dep sets via identifier_set(ast, &[], None) -- with empty dims (two call sites, ~2694 and ~2980 at 7012d9f0) -- so dimension names leak into other_deps. By contrast, build_arrayed_link_score_equation deliberately strips the source's dimension and element names from the dep set before building partials. Once d1 is in other_deps, the per-index walk in wrap_index_non_matching_in_previous treats it as a causal dependency (the #587 guards only recognize dimension elements, never dimension names) and wraps it.
Observed failure mode: the doomed expression routes through a synthesized implicit helper; the helper's fragment-compile failure is silent (#741 -- model_ltm_fragment_diagnostics never covers implicit helpers), the helper stubs to 0, and the link score reads a constant wrong value -- -40 for the probe constants (= -1/(5·0.005)) -- with zero diagnostics. Silent wrong numbers on a mundane equation shape.
Failure chain (original Wildcard-shape analysis)
wrap_non_matching_in_previous's live-Subscript arm (ltm_augment.rs ~506-551): the reference matches the live Wildcard shape (classify_expr0_subscript_shape returns Wildcard whenever any index is *), so the outer subscript stays unwrapped and each index gets the per-index treatment.
The iterated-dim index State is not a literal element (is_literal_element_index matches dimension elements, never dimension names), so it is routed into wrap_index_non_matching_in_previous (ltm_augment.rs ~788).
Superseded scoping note: this issue originally claimed the defect was latent/unreachable on current paths and surfaced via an Assembly Warning when reached. Both claims were falsified during the GH #743 review rounds (branch ltm-fix-batch-2, commit 7012d9f0): the Bare-shape pinned-index repro above is reachable on a mundane apply-to-all equation and is fully silent. Severity raised from low to medium.
The original Wildcard-path analysis (still accurate for that shape):
A whole-RHS reducer over a MAPPED iterated axis (inflow[State] = SUM(matrix[State, *]) with a positional State -> Region mapping) would route its Wildcard partial through this builder, but the ltm: mapped-dimension sliced reducers are not hoisted into aggregate nodes (compute_read_slice declines a remapped iterated axis) #534 work sidestepped it by minting a synthetic agg for mapped whole-RHS reducers instead (the variable-backed exception in ltm_agg.rs's walk_var_equation) -- precisely because the variable-backed Wildcard partial path cannot remap.
Dynamic-index sliced reducers fail engine compile before LTM runs (per the reviewer's probe).
Counterfactually verified during the #534 review on branch ltm-core-batch (commit 74f5366): removing the #534walk_var_equation gate (making the mapped whole-RHS reducer variable-backed again) reproduces sum(matrix[PREVIOUS(state), *]), the fragment-compile failure, and zero-stubbed loop scores.
Components affected
src/simlin-engine/src/ltm_augment.rs -- wrap_non_matching_in_previous (live-Subscript per-index path, ~528-546), wrap_index_non_matching_in_previous (~788, the guard gap), qualify_element_index (~766), and the A2A/scalar link-score generators' dep-set construction (identifier_set(ast, &[], None) call sites, ~2694 and ~2980), which let dimension names into other_deps in the first place.
Fix shape
Two complementary directions; the same fix covers both the original Wildcard-path shape and the demonstrated Bare-path shape:
Wrapper-side guard (the original proposal): the wrapper must not wrap identifiers that resolve to dimension names in subscript-index position. The subscript-index context is already known during the walk (wrap_index_non_matching_in_previous is only ever called on indices), and the iterated-dim machinery already distinguishes index idents -- cf. is_live_source_iterated_dim_subscript / classify_expr0_subscript_shape's GH ltm-augment: subscripted-A2A-reference link-score partial fails to compile (PREVIOUS arg must be Var) #511 branch, and the iter_ctx / dims_ctx parameters already threaded through the walk. Extend the ~801/~815 guard pair with a third case: an index Var whose canonical name resolves to a project dimension (via dims_ctx, or matching iter_ctx's iterated dims) is a dimension selector, never a causal reference -- leave it verbatim, mirroring the LTM: PREVIOUS-in-subscript arg not reduced to a bare var during helper rewriting -> LTM synthetic link/loop score silently drops to 0 #587 element-name treatment.
Generator-side dep filtering: pass the target's dims to the A2A/scalar generators' identifier_set calls (or strip dimension names from the resulting dep set, the way build_arrayed_link_score_equation already does) so dimension names never land in other_deps at all.
Regression tests should pin the partial text for (a) a live Wildcard reference with an iterated-dim index (e.g. via the #534 counterfactual shape) and (b) the Bare-shape pinned-index repro above (growth[D1] = matrix[D1, c1] * frac[D1], asserting the frac -> growth link score is correct and no PREVIOUS(d1) appears in the partial), so neither mangle can silently return.
Discovery context
Identified during adversarial review of the GH #534 work on branch ltm-core-batch (commit 74f5366); the #534 commit avoids the Wildcard path (synthetic-agg minting for mapped whole-RHS reducers) rather than fixing the builder.
Rescoped 2026-06: during the GH #743 review rounds on branch ltm-fix-batch-2 (commit 7012d9f0), an implementor and an independent adversarial reviewer both demonstrated the Bare-shape pinned-index path above, falsifying the latency and warning claims and raising severity to medium.
Tracking
Part of LTM tracking epic #488 (Augmentation group). Related but distinct:
Summary
The ceteris-paribus partial builders in
src/simlin-engine/src/ltm_augment.rs(build_partial_equation_shaped->wrap_non_matching_in_previous->wrap_index_non_matching_in_previous) PREVIOUS-wrap a bare dimension-name identifier sitting inside a subscript index. Originally filed against the liveWildcardreference shape (SUM(matrix[State, *])becomingSUM(matrix[PREVIOUS(state), *])), the defect is also reachable through a mundane Bare-shape apply-to-all equation with no reducer at all (see "Demonstrated reachable, fully silent path" below), where the dimension name leaks into the co-sourceother_depsset and the frozen co-source's subscript index gets wrapped.PREVIOUSof a dimension name is meaningless: the fragment fails compilation and the synthetic link-score variable is silently stubbed -- the #546/#548/#587 hazard class ("LTM fragment compiler can't handle a shape -> synthetic var stubs to 0 -> degraded score masquerades as inactive"). Correction to the original filing: when the doomed expression routes through a synthesized implicit helper, the failure produces no diagnostic at all --model_ltm_fragment_diagnosticsnever covers implicit helpers (the #741 class) -- so the original claim that the defect "is surfaced as an Assembly Warning when reached" is wrong for this route.Demonstrated reachable, fully silent path (GH #743 review, raises severity to medium)
Verified independently by an implementor (discovery) and an adversarial reviewer (probe) during GH #743 work on branch
ltm-fix-batch-2at commit7012d9f0:A Bare-shape apply-to-all equation with a pinned literal co-source index (
c1), no reducer involved. The changed-first partial for thefrac -> growthedge freezes the co-source as-- the iterated-dim name
d1is PREVIOUS-wrapped inside the subscript index.Mechanism: the A2A/scalar link-score generators build their dep sets via
identifier_set(ast, &[], None)-- with empty dims (two call sites, ~2694 and ~2980 at7012d9f0) -- so dimension names leak intoother_deps. By contrast,build_arrayed_link_score_equationdeliberately strips the source's dimension and element names from the dep set before building partials. Onced1is inother_deps, the per-index walk inwrap_index_non_matching_in_previoustreats it as a causal dependency (the #587 guards only recognize dimension elements, never dimension names) and wraps it.Observed failure mode: the doomed expression routes through a synthesized implicit helper; the helper's fragment-compile failure is silent (#741 --
model_ltm_fragment_diagnosticsnever covers implicit helpers), the helper stubs to 0, and the link score reads a constant wrong value -- -40 for the probe constants (= -1/(5·0.005)) -- with zero diagnostics. Silent wrong numbers on a mundane equation shape.Failure chain (original Wildcard-shape analysis)
wrap_non_matching_in_previous's live-Subscript arm (ltm_augment.rs ~506-551): the reference matches the liveWildcardshape (classify_expr0_subscript_shapereturnsWildcardwhenever any index is*), so the outer subscript stays unwrapped and each index gets the per-index treatment.Stateis not a literal element (is_literal_element_indexmatches dimension elements, never dimension names), so it is routed intowrap_index_non_matching_in_previous(ltm_augment.rs ~788).qualify_element_index(~766) and theis_element_of_any_dimensionfallback (~815) both test whether the index ident names a dimension element. A dimension name is neither, so the index falls through to the recursivewrap_non_matching_in_previouscall, where the bareVar(state)is indistinguishable from a causal dependency reference and gets wrapped:PREVIOUS(state).assemble_modulegracefully drops the fragment, the synthetic var keeps its layout slot and reads constant 0 for the whole run. On the named-synthetic-var routemodel_ltm_fragment_diagnosticsemits aWarning; on the implicit-helper route (the demonstrated Bare-shape path) nothing is emitted (ltm: failed implicit-helper fragment compile emits no diagnostic (model_ltm_fragment_diagnostics skips model_ltm_implicit_var_info) #741); loop scores through the edge silently read garbage.Reachability (severity: medium)
The original Wildcard-path analysis (still accurate for that shape):
inflow[State] = SUM(matrix[State, *])with a positionalState -> Regionmapping) would route itsWildcardpartial through this builder, but the ltm: mapped-dimension sliced reducers are not hoisted into aggregate nodes (compute_read_slice declines a remapped iterated axis) #534 work sidestepped it by minting a synthetic agg for mapped whole-RHS reducers instead (the variable-backed exception inltm_agg.rs'swalk_var_equation) -- precisely because the variable-backedWildcardpartial path cannot remap.$\u{205A}ltm\u{205A}agg\u{205A}{n}nodes (ltm: mapped-dimension sliced reducers are not hoisted into aggregate nodes (compute_read_slice declines a remapped iterated axis) #534), so they never reach this builder.Counterfactually verified during the #534 review on branch
ltm-core-batch(commit 74f5366): removing the #534walk_var_equationgate (making the mapped whole-RHS reducer variable-backed again) reproducessum(matrix[PREVIOUS(state), *]), the fragment-compile failure, and zero-stubbed loop scores.Components affected
src/simlin-engine/src/ltm_augment.rs--wrap_non_matching_in_previous(live-Subscript per-index path, ~528-546),wrap_index_non_matching_in_previous(~788, the guard gap),qualify_element_index(~766), and the A2A/scalar link-score generators' dep-set construction (identifier_set(ast, &[], None)call sites, ~2694 and ~2980), which let dimension names intoother_depsin the first place.Fix shape
Two complementary directions; the same fix covers both the original Wildcard-path shape and the demonstrated Bare-path shape:
wrap_index_non_matching_in_previousis only ever called on indices), and the iterated-dim machinery already distinguishes index idents -- cf.is_live_source_iterated_dim_subscript/classify_expr0_subscript_shape's GH ltm-augment: subscripted-A2A-reference link-score partial fails to compile (PREVIOUS arg must be Var) #511 branch, and theiter_ctx/dims_ctxparameters already threaded through the walk. Extend the ~801/~815 guard pair with a third case: an indexVarwhose canonical name resolves to a project dimension (viadims_ctx, or matchingiter_ctx's iterated dims) is a dimension selector, never a causal reference -- leave it verbatim, mirroring the LTM: PREVIOUS-in-subscript arg not reduced to a bare var during helper rewriting -> LTM synthetic link/loop score silently drops to 0 #587 element-name treatment.identifier_setcalls (or strip dimension names from the resulting dep set, the waybuild_arrayed_link_score_equationalready does) so dimension names never land inother_depsat all.Regression tests should pin the partial text for (a) a live Wildcard reference with an iterated-dim index (e.g. via the #534 counterfactual shape) and (b) the Bare-shape pinned-index repro above (
growth[D1] = matrix[D1, c1] * frac[D1], asserting thefrac -> growthlink score is correct and noPREVIOUS(d1)appears in the partial), so neither mangle can silently return.Discovery context
Identified during adversarial review of the GH #534 work on branch
ltm-core-batch(commit 74f5366); the #534 commit avoids the Wildcard path (synthetic-agg minting for mapped whole-RHS reducers) rather than fixing the builder.Rescoped 2026-06: during the GH #743 review rounds on branch
ltm-fix-batch-2(commit7012d9f0), an implementor and an independent adversarial reviewer both demonstrated the Bare-shape pinned-index path above, falsifying the latency and warning claims and raising severity to medium.Tracking
Part of LTM tracking epic #488 (Augmentation group). Related but distinct:
model_ltm_fragment_diagnosticsskipsmodel_ltm_implicit_var_info); this is why the demonstrated Bare-shape path is fully silent.DynamicIndex(there the wholeSUM(...)guard form gets wrapped and codegen rejects anApp). Different shape, different failure site: this issue wraps an ident inside a subscript index and fails at fragment compile with a silent stub.qualify_element_index+is_element_of_any_dimension) this defect slips past, because a dimension name is not a dimension element.