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
LTM occurrence identity is a SiteId: a path of u16 components. The FIRST component is the slot index -- for an Ast::Arrayed target, its per-element equation slots numbered in canonical element-key-sorted order (the optional default equation is the slot after the last element).
The builtin-arity axis of that path was hardened on branch roundtrips-track-a (commits b0a2a6f0, d5eed63b): db::ltm_ir::site_child_index returns Option<u16> and declines out-of-range indices, a reserved UNADDRESSABLE_CHILD (= u16::MAX, src/simlin-engine/src/db/ltm_ir.rs:615) sentinel is never emitted by the producer, and ltm_augment::child_path maps an over-arity child to that sentinel with a CHECKED conversion -- so a lookup at or below an unaddressable child provably misses instead of aliasing an earlier sibling. That design exists precisely because a wrapped index re-uses another occurrence's SiteId, and the ceteris-paribus wrap then holds or freezes the WRONG reference and emits a plausible but wrong link score (silent corruption).
The slot axis did not get the same treatment. These casts are unchecked (line numbers verified against roundtrips-track-a @ 86df68bf):
Consumer side, src/simlin-engine/src/ltm_augment.rs, in build_arrayed_link_score_equation (declared at line 3575):
Producer side mirrors it, src/simlin-engine/src/db/ltm_ir.rs, in the Ast::Arrayed arm of collect_all_reference_sites_and_occurrences:
line 501 -- acc.path.push(slot as u16); (per-element slots)
line 517 -- acc.path.push(elem_keys.len() as u16); (default slot)
For an arrayed target with more than 65,535 element equations, slot as u16 wraps and two distinct slots collapse onto the same SiteId prefix -- the same aliasing class the builtin-arity axis now refuses.
Note that #978's framing is inaccurate on this point: it states "Every AST-shaped child count fits a u16 by construction, EXCEPT builtin arity." The arrayed slot count is the counterexample. The other unchecked as u16 pushes in walk_all_in_expr really are bounded by construction and are NOT part of this issue: ltm_ir.rs:809 (i as u16 over subscript indices, bounded by the reference's declared subscript arity) and ltm_ir.rs:976 (child as u16 over the fixed three Expr2::If children).
Why it matters
The consequence is silent. A wrapped slot index yields a wrong-but-plausible link score, not a crash, a panic, or a diagnostic. There is no loud failure mode to notice in the field.
src/simlin-engine/src/ltm_augment.rs (consumer: build_arrayed_link_score_equation, plus the checked child_path precedent)
src/simlin-engine/src/ltm_augment_occurrence.rs -- on roundtrips-track-a the occurrence-lookup helpers (SlotOccurrences, OccurrenceLookup) were extracted here, so the checked-conversion precedent (child_path, child_is_addressable) lives in ltm_augment.rs while the slot-keyed lookup types live in the new file.
Severity / reachability
Latent, not reachable in practice today. Per #977, LTM link-score generation is quadratic in an arrayed target's element count -- (N+1)^2 generated equation arms per edge, measured at ~2.3s of generation for N=400 -- so a 65,536-element arrayed target is many orders of magnitude past where LTM generation completes at all. This is filed for the silent-consequence and reader-trap reasons above, not because anyone can trip it.
Suggested direction
Either:
Apply the same treatment as the arity axis -- a checked conversion plus the reserved-sentinel / decline path; or
If a slot bound is easier to establish than a per-child one, reject an arrayed target with more than u16::MAX element equations at the LTM front door.
#978 tracks a related simplification (rejecting over-arity builtins at the front door so the sentinel machinery can be deleted entirely). If #978 is taken, the slot axis should be folded into the same front-door check rather than growing a second, parallel mechanism. Whichever path is chosen, #978's "EXCEPT builtin arity" claim should be corrected in the same change.
Commits b0a2a6f0 ("engine: make LTM arm parse failures loud, index occurrences by slot") and d5eed63b ("engine: complete the LTM over-arity SiteId guard on both sides") -- the arity-axis precedent
How it was discovered
Noticed while reviewing the over-arity SiteId guard work on branch roundtrips-track-a: the guard covers the builtin-arity axis of the path but not the slot axis. Out of scope for that branch; filed so the known aliasing cast is not documented solely in a chat report.
LTM occurrence identity is a
SiteId: a path ofu16components. The FIRST component is the slot index -- for anAst::Arrayedtarget, its per-element equation slots numbered in canonical element-key-sorted order (the optional default equation is the slot after the last element).The builtin-arity axis of that path was hardened on branch
roundtrips-track-a(commitsb0a2a6f0,d5eed63b):db::ltm_ir::site_child_indexreturnsOption<u16>and declines out-of-range indices, a reservedUNADDRESSABLE_CHILD(= u16::MAX,src/simlin-engine/src/db/ltm_ir.rs:615) sentinel is never emitted by the producer, andltm_augment::child_pathmaps an over-arity child to that sentinel with a CHECKED conversion -- so a lookup at or below an unaddressable child provably misses instead of aliasing an earlier sibling. That design exists precisely because a wrapped index re-uses another occurrence'sSiteId, and the ceteris-paribus wrap then holds or freezes the WRONG reference and emits a plausible but wrong link score (silent corruption).The slot axis did not get the same treatment. These casts are unchecked (line numbers verified against
roundtrips-track-a@86df68bf):Consumer side,
src/simlin-engine/src/ltm_augment.rs, inbuild_arrayed_link_score_equation(declared at line 3575):Producer side mirrors it,
src/simlin-engine/src/db/ltm_ir.rs, in theAst::Arrayedarm ofcollect_all_reference_sites_and_occurrences:acc.path.push(slot as u16);(per-element slots)acc.path.push(elem_keys.len() as u16);(default slot)For an arrayed target with more than 65,535 element equations,
slot as u16wraps and two distinct slots collapse onto the sameSiteIdprefix -- the same aliasing class the builtin-arity axis now refuses.Note that #978's framing is inaccurate on this point: it states "Every AST-shaped child count fits a
u16by construction, EXCEPT builtin arity." The arrayed slot count is the counterexample. The other uncheckedas u16pushes inwalk_all_in_exprreally are bounded by construction and are NOT part of this issue:ltm_ir.rs:809(i as u16over subscript indices, bounded by the reference's declared subscript arity) andltm_ir.rs:976(child as u16over the fixed threeExpr2::Ifchildren).Why it matters
SiteIdpath is now explicitly guarded with a reserved sentinel and rustdoc explaining the aliasing hazard; the sibling axis is a bareas. Someone comparing the two could reasonably assume both were handled -- and ltm: consider rejecting over-arity builtins at the LTM front door to delete the SiteId unaddressable-child machinery #978's body currently encourages that assumption.Components affected
src/simlin-engine/src/db/ltm_ir.rs(producer: occurrenceSiteIdconstruction)src/simlin-engine/src/ltm_augment.rs(consumer:build_arrayed_link_score_equation, plus the checkedchild_pathprecedent)src/simlin-engine/src/ltm_augment_occurrence.rs-- onroundtrips-track-athe occurrence-lookup helpers (SlotOccurrences,OccurrenceLookup) were extracted here, so the checked-conversion precedent (child_path,child_is_addressable) lives inltm_augment.rswhile the slot-keyed lookup types live in the new file.Severity / reachability
Latent, not reachable in practice today. Per #977, LTM link-score generation is quadratic in an arrayed target's element count -- (N+1)^2 generated equation arms per edge, measured at ~2.3s of generation for N=400 -- so a 65,536-element arrayed target is many orders of magnitude past where LTM generation completes at all. This is filed for the silent-consequence and reader-trap reasons above, not because anyone can trip it.
Suggested direction
Either:
u16::MAXelement equations at the LTM front door.#978 tracks a related simplification (rejecting over-arity builtins at the front door so the sentinel machinery can be deleted entirely). If #978 is taken, the slot axis should be folded into the same front-door check rather than growing a second, parallel mechanism. Whichever path is chosen, #978's "EXCEPT builtin arity" claim should be corrected in the same change.
Cross-references
b0a2a6f0("engine: make LTM arm parse failures loud, index occurrences by slot") andd5eed63b("engine: complete the LTM over-arity SiteId guard on both sides") -- the arity-axis precedentHow it was discovered
Noticed while reviewing the over-arity
SiteIdguard work on branchroundtrips-track-a: the guard covers the builtin-arity axis of the path but not the slot axis. Out of scope for that branch; filed so the known aliasing cast is not documented solely in a chat report.