Skip to content

test(applied): pin MatR::permute_side on both sides and its length guard (#298) - #369

Merged
tsondru merged 1 commit into
mainfrom
audit/G4-T2
Aug 27, 2026
Merged

test(applied): pin MatR::permute_side on both sides and its length guard (#298)#369
tsondru merged 1 commit into
mainfrom
audit/G4-T2

Conversation

@tsondru

@tsondru tsondru commented Aug 27, 2026

Copy link
Copy Markdown
Member

Closes #298. Taskmap G4-T2.

Premise probe (before briefing): the issue's first claim holds, its second does not, and the doc rider is already done.

  • Mismatch → no-op is genuinely dead. Sharper than "no coverage": four pre-existing tests do exercise MatR::permute_side on non-identity morphisms (hand_anchored_permute_side_values, permute_side_composes_the_braidings, permute_side_on_an_identity_matches_the_constructors, permute_side_functor_square_exhaustive_n3_and_n4), but every one is on a square matrix, so rows != cols never occurs and the p.len() != expected guard is never reached with a mismatch. The nearest guard pin, prop.rs:483 permute_side_length_mismatch_leaves_self_unchanged, is on PropExpr, not MatR.
  • "Both silent-Err arms" cannot be reached by any test. The guard makes matmul's only failure condition (self.cols != other.rows) impossible: the codomain side builds perm_mat as p.len() × p.len() == self.cols × self.cols, the domain side builds p_transpose as n × n with n == self.rows. Measured: a copy panicking in both arms ran 146/146 binaries green. Owner call: convert both to .expect naming the invariant rather than leave the silent swallow (MatR::permute_side silently discards matmul errors (latent no-op path) #260's class).
  • The doc-truth rider was already discharged at docs(applied): rustdoc and CHANGELOG to contract statements (#365) #367 (916ed5f) — mat.rs:11-12 reads "The nalgebra bridge specialized to F64Rig is crate::mat_f64, behind the f64-rig feature". It is a code span rather than the intra-doc link the rider sketched, because mat_f64 is #[cfg(feature = "f64-rig")] and the link would not resolve without the feature. Lines 9-11 untouched.

Shipped (production diff 6 lines; tests + CHANGELOG):

  • Both matmul results go through .expect naming the guard checked above, replacing if let Ok arms that discarded the Err.
  • One test pinning all four cells of {of_codomain} × {guard hit, guard missed} on non-square matrices with entries written out: wide 2×3 under rotation_left(3,1) on the codomain → [[3,1,2],[6,4,5]]; tall 3×2 under the same 3-cycle on the domain → [[5,6],[1,2],[3,4]]; and the unchanged matrix where the permutation's length matches the opposite side's arity, so a mismatch is rejected by length alone and never because the permutation was an identity.

Falsification (--no-fail-fast, cp -r copy; impl + two review rounds):

perturbation red observed vs expected
guard deleted new pin alone panic at the codomain .expect, CompositionSizeMismatch { expected: 3, actual: 2 }
expected = self.cols always new pin alone domain case [[1,2],[3,4],[5,6]] vs [[5,6],[1,2],[3,4]]
expected = self.rows always new pin alone codomain case [[1,2,3],[4,5,6]] vs [[3,1,2],[6,4,5]]
guard applied only when of_codomain new pin alone panic at the domain .expect, expected: 3, actual: 2
each expected matrix transposed in the test new pin alone MatR{2,3,…} vs MatR{3,2,…} on each side
domain branch builds P not Pᵀ new pin + 5 pre-existing [[3,4],[5,6],[1,2]] vs [[5,6],[1,2],[3,4]]
codomain branch uses permutation_matrix(&p.inv()) new pin + the same 5 [[2,3,1],[5,6,4]] vs [[3,1,2],[6,4,5]]
restore if let Ok (both arms) none — 146/146 ok the null: the Err arms are dead

Coverage fact: six of the eight perturbations are caught by the new pin alone — no pre-existing test in the workspace detects the guard's existence, either expected selector, or either no-op branch.

Reviews: executing adversarial (0 blocking / 1 important / 3 minor), all applied. The important one is the interesting one: the first draft pinned the domain side on the 2×3 with transposition(2,0,1), an involution, so P == Pᵀ and the β(p)-vs-β(p⁻¹) perturbation left the new test green while reddening five pre-existing ones — sound but narrow. Fixed by moving that pin to a 3×2 fixture with a 3-cycle; the delta review reconstructed the superseded pin, confirmed it stays green under that perturbation while the new one goes red, and returned 0 findings. Minors: four rows()/cols() assertions implied by the struct equality above them (removed), and a stale file header.

Gates: 146 binaries / 2118 / 0 / 24; clippy -D warnings on default, --no-default-features, --all-features; RUSTDOCFLAGS=-D warnings cargo doc; fmt; version-refs, cc-pin/audit-count, rand-dev-only.

🤖 Generated with Claude Code

…ard (#298)

Closes #298.

`permute_side` had no non-square coverage: every pre-existing exercise is on
a square matrix, so `rows != cols` never occurred and the `p.len() != expected`
guard was never reached with a mismatch. Pinned on a 2 x 3 and a 3 x 2
`MatR<F64Rig>` with entries written out — the 3-cycle `rotation_left(3, 1)` on
each side, and the unchanged matrix where the permutation's length matches the
opposite side's arity. Both direction pins use a permutation that differs from
its inverse, so each separates the braiding from its transpose.

Both `matmul` results now go through `.expect` naming the guard, instead of an
`if let Ok` that discarded the `Err`. The arms are unreachable: the guard makes
`matmul`'s only failure condition impossible, so the issue's "reach both Err
arms" cannot be satisfied by any test — a probe panicking in both arms ran
146/146 binaries green. Production diff 6 lines.

Falsified against the perturbations, each red on the new test alone: guard
deleted (panics at the codomain `.expect`, `expected: 3, actual: 2`);
`expected` pinned to `self.cols` (domain case observes the unpermuted
`[[1,2],[3,4],[5,6]]`); `expected` pinned to `self.rows` (codomain case
observes the unpermuted `[[1,2,3],[4,5,6]]`); domain branch building `P`
instead of `P^T` (observes `[[3,4],[5,6],[1,2]]` against `[[5,6],[1,2],[3,4]]`).
Restoring `if let Ok` reddens nothing — the null that confirms the arms are
dead.

The issue's doc-truth rider was already discharged at #367: `mat.rs:11-12`
points at `crate::mat_f64` behind the `f64-rig` feature. It is a code span
rather than the intra-doc link the rider sketched, because the module is
feature-gated and the link would not resolve without it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tsondru
tsondru merged commit 68be9f4 into main Aug 27, 2026
6 checks passed
@tsondru
tsondru deleted the audit/G4-T2 branch August 27, 2026 17: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.

applied: MatR permute_side — mismatch no-op contract and both silent-Err arms dead in tests

1 participant