Skip to content

engine: defend ltm feeder slot pin and agg source slices - #787

Merged
bpowers merged 1 commit into
mainfrom
ltm-p3-hardening
Jun 12, 2026
Merged

bpowers merged 1 commit into
mainfrom
ltm-p3-hardening

Conversation

@bpowers

@bpowers bpowers commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Follow-up to #784 (supersedes #786, which conflicted because #784 was squash-merged and the old branch carried the pre-merge history). Fixes the two P3 findings #784's automated review surfaced after merge. Both harden the phase's no-silent-fallback discipline, and the first turned out to be a real reachable defect rather than defense-in-depth.

Feeder slot pinning on square-source aggs: a degenerate square-source reducer (x[D1] = 1 + SUM(cube[D1,D1,*] * frac[D1,D1])) mints an agg with result_dims == [D1, D1], and pin_iterated_dim_indices resolved indices by first-match dim name -- every duplicated-dim index pinned to the first slot part, silently freezing the wrong source row on off-diagonal feeder scores (confident garbage, zero diagnostics). It now mirrors resolve_mismatched_index_position's uniqueness defense: an ambiguous index bails to the existing UnfreezablePartial loud-skip channel (per-row warning, no score emitted). RED-verified pre-fix at both fixture and unit level; byte-identical for every non-repeated-dim shape by construction.

agg_sources release-build fallback: the helper carried a debug_assert that every arrayed reducer source has a per-var slice, then silently substituted the canonical slice in release if the invariant ever broke -- which would mislabel a projection feeder as a co-source and corrupt per-(row, slot) derivations. It now returns Option and declines the hoist entirely on a missing slice (the same inert conservative degradation as the dynamic-index carve-outs). The reviewer-suggested empty-slice fallback was rejected because an arrayed source wearing the scalar-feeder encoding would flow into the scalar-agg name grammar -- plausibly wrong rather than inert. The two AST walkers were audited as structurally symmetric, so the path is unreachable today; the defense now matches the asserted invariant.

The remaining square-source hazards (co-source and agg-to-target halves still pin by dim name; phantom off-diagonal link scores reach link-level surfaces unwarned) are pre-existing and tracked as #785/#778, with the loop-score cascade gap as #780. Adversarial review of this commit: APPROVE, with the empirical co-source findings appended to #785.

Two P3 defensive-code findings from PR #784's automated review, both
hardening the phase's no-silent-wrong-numbers invariant.

pin_iterated_dim_indices resolved slot pins first-match-by-name, so a
degenerate square-source agg (iterated slot axes repeating a dim, e.g.
SUM(cube[D1,D1,*] * frac[D1,D1]) inside an A2A-D1 body) pinned every
occurrence of the duplicated dim to the FIRST slot part -- the
off-diagonal slot [r1,r2] froze frac[r1,r1], a silently wrong score.
This shape is reachable (the new ltm_unified_tests fixture demonstrated
the wrong frozen row pre-fix). The pin now mirrors
resolve_mismatched_index_position's uniqueness defense: an index naming
a repeated dim returns None, which
generate_iterated_feeder_to_agg_equation converts into the existing
GH #743 unfreezable error -- the row is loudly skipped via
emit_ltm_partial_equation_warning instead of emitted wrong.

agg_sources' release-build fallback substituted the CANONICAL slice for
an arrayed source missing from per_var, contradicting its own
debug_assert; for a projection feeder (whose slice differs from
canonical by design) that would mislabel the feeder as a co-source and
corrupt the per-(row, slot) scores. The empty-slice alternative was
rejected because an arrayed source with a scalar-shaped slice feeds the
read_slice_rows arity-mismatch fallbacks with mismatched name grammars.
agg_sources now returns None so the hoist is declined and the reference
stays on the conservative Direct path -- the same inert degradation as
the not-statically-describable carve-outs. The invariant is structurally
guaranteed (collect_var_refs and collect_arrayed_source_slices walk the
identical reference surface), so the decline is purely defensive and is
pinned by a direct unit test.
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.87%. Comparing base (3553282) to head (0f09a61).

Files with missing lines Patch % Lines
src/simlin-engine/src/ltm_augment.rs 81.48% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #787   +/-   ##
=======================================
  Coverage   90.86%   90.87%           
=======================================
  Files         224      224           
  Lines      140812   140856   +44     
=======================================
+ Hits       127955   128003   +48     
+ Misses      12857    12853    -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@claude

claude Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review

Reviewed the diff focusing on the two hardening changes:

  1. pin_iterated_dim_indices → Option<Expr0> (ltm_augment.rs:1919-1984): The new unique_dim_position helper correctly distinguishes "not in dims" (Some(None)), "unique match" (Some(Some(pos))), and "ambiguous" (None). The ? propagation through the recursive Subscript/App/Op1/Op2/If arms is sound — collect::<Option<Vec<_>>>() short-circuits on the first None, and ? from inside the map closure returns None from the closure (not the outer function), so a single ambiguous index correctly aborts the whole pin. The caller in generate_iterated_feeder_to_agg_equation (line 1881) converts the None into PartialEquationError::unfreezable, which the existing emit_ltm_partial_equation_warning machinery already handles per the GH ltm: un-hoisted multi-source iterated-dim-feeder reducer emits silent garbage arrayed loop score (combined_read_slice slice-disagreement carve-out) #743 contract.

  2. agg_sources → Option<Vec<AggSource>> (ltm_agg.rs:1027-1054): Replaces the debug_assert + canonical-slice fallback with a clean decline. The is_arrayed predicate matches collect_arrayed_source_slices exactly (same get_dimensions().map(|d| !d.is_empty()) chain), so the invariant should always hold; the None path is defense-in-depth. Both call sites (walk_var_equation line 752, walk_subexpr_for_aggs line 946) correctly use let Some(sources) = ... in let-chains so a None falls through to the conservative Direct path with in_reducer unchanged.

  3. Tests — The three tests (square-source unified integration test, agg_sources_declines_when_arrayed_source_lacks_per_var_slice unit test, and test_generate_iterated_feeder_to_agg_equation_bails_on_duplicate_dims) cover the changes adequately. Test placement and style match existing patterns.

No findings.

Overall correctness verdict

Correct. The fixes are minimal, defensive, and consistent with the codebase's no-silent-fallback discipline. The real reachable defect (square-source feeder slot pin) is addressed by mirroring resolve_mismatched_index_position's uniqueness defense; the agg_sources change replaces a release-build silent fallback with a clean decline that matches the asserted invariant. No bugs detected; existing code and tests should not break.

@bpowers
bpowers merged commit ab76648 into main Jun 12, 2026
15 checks passed
@bpowers
bpowers deleted the ltm-p3-hardening branch June 12, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant