Skip to content

Task 7: geodesic directions and the rotation permutation table - #14

Merged
MrReasonable merged 8 commits into
mainfrom
task-7-geodesic
Jul 30, 2026
Merged

MrReasonable merged 8 commits into
mainfrom
task-7-geodesic

Conversation

@MrReasonable

Copy link
Copy Markdown
Owner

Task 7 turned out not to be the task the plan described.

It was a lift, and the plan's Step 3 was deleted rather than executed

geodesic.rs already existed in borbax-experiments, written months of
plan-time early because the G2 locality measurement could not run without a
rotation table. By the time Task 7 came due it was ahead of the plan's code.

The compiler settles it. Substituting the plan's Step 3 and running the gate's
clippy leg gives 71 errors — 38 indexing_slicing, 9 as_conversions (two
genuine usize → u8 truncations), 9 unwrap_used, 3 disallowed_methods on
f64::total_cmp (§13.4) and 2 on f64::min (§13.1)
— into a crate that has
zero. It also had 11 tests against the shipped 16, and lacked
minus_identity_is_not_in_the_group, the precise hazard CLAUDE.md names.

borbax-experiments now re-exports rather than keeping a copy, so the table G2
measured and the table the binding kernel will search cannot drift apart.

The review found more than the lift did

Ten reviewers: six specialists on the lift, three cross-checking the fixes, two
CodeRabbit CLI passes. The headline was found independently by the two lanes
cross-routed onto each other's domains
.

A canonical ordering is an output that property tests structurally cannot
see.
All 18 original tests — group closure, bijectivity, determinant, the
anti involution, -I absent, matrix agreement, unit norms — are invariant
under relabelling dirs, so re-keying the sort left the entire 325-test
workspace green. The realistic mutation is not sabotage but a tidy-up:
rewriting normalise's scale(a, 1.0/n) as componentwise division is
mathematically identical, better rounded, unflagged by clippy, and is how
borbax-universe's own copy already spells it — and it moves six of the 162
index positions at D=162. the_canonical_direction_ordering_is_pinned is the
fix, and because the three test legs run on three platforms it is a working
§13.4 golden now, rather than at Task 20 where goldens --emit still exits
0 with "No golden artifacts yet".

The rename re-planted the hazard it was introduced to close. contact_perms

  • the permsrotation_perms rename (−13.21% at D=42, bit-identical) updated
    the Task 10 kernel snippet's code and appended the new comment without
    deleting the superseded one — leaving a paragraph that instructs the reader into
    the exact defect. Measured: following the old CLAUDE.md wording against the new
    API gives anti[contact_perms[r][i]], which equals rotation_perms[r][i] in
    100% of entries at all three resolutions. The defensive-looking second
    anti is the improper-coset search.

Verification

  • 330 tests, six gate legs green (fmt, clippy -D warnings, test,
    test --release, cargo doc, xtask).
  • Every guard probed against the mutation it exists to stop, including both
    sides of is_identity's tolerance window.
  • All nine golden constants executed on real x86-64 Linux and reproduced by
    an independent Python implementation sharing no code. musl and gnu opcode
    streams identical at 5234 instructions. Windows inspected — op-for-op
    histogram match, zero FMA-class instructions — so the windows-latest leg is
    what settles it.

Routed forward with discriminators

  • Task 8/9 — §22.8's actual claim (a chiral molecule and its enantiomer are
    different species) has no test anywhere in V0. Every canonicalise test
    asserts rotation-invariance; none asserts mirror-difference.
  • Task 10 — the required probe, restated as a substitution because the
    rename removed the anti[...] its first wording said to delete.
  • Task 20packing.rs's independent oracle disagrees on six of 162
    positions. The sharper question is why a packing error depends on vertex
    ordering at all.

Note on review coverage

CodeRabbit's GitHub app has produced nothing on the last three PRs — measured:
#9 got 24 inline comments, #10 got 10, #11#13 got zero. The repo is private and
the free tier is public-repo only. It is not a required check, so nothing was
gated wrongly. The CLI is authenticated and was run twice here, finding 11 real
issues.

…able

Task 7 is a lift, not a fresh implementation, and the plan's Step 3 code has
been deleted rather than executed. `geodesic.rs` was written in
`borbax-experiments` months of plan-time early because the G2 locality
measurement could not run without a rotation table.

The compiler settles whether that was right. Substituting the plan's own
Step 3 for `geodesic.rs` and running the gate's clippy leg gives 71 errors —
38 indexing_slicing, 9 as_conversions (two genuine usize->u8 truncations),
9 unwrap_used, 3 disallowed_methods on f64::total_cmp (§13.4) and 2 on
f64::min (§13.1), plus needless_range_loop, manual_midpoint and four
missing_docs. The plan's code fails leg 2 of the six-leg gate in 71 places,
five of them determinism violations, into a crate that has zero. It also had
11 tests against the shipped 16, and lacked `rotation_matrices` as real
geometry, the test tying the integer table back to it, a per-element
determinant check, and `minus_identity_is_not_in_the_group` — the exact
hazard CLAUDE.md names.

`experiments` now re-exports rather than keeping a copy. A divergence between
the table G2 measured and the table the binding kernel searches would
invalidate the measurement with nothing failing. That is an argument for one
shared table, not one copy of the construction: `borbax_universe::packing`
holds a third, deliberately, as an independent oracle answerable to nothing
but Euler.

The lift was not verbatim, and that is the part worth reading. Four comments
asserted "this lifts into `borbax-molecule`" as a future event. One listed
four measured claims and told its reader to re-measure on arrival rather than
copy — so they were re-measured, and the third was false: antipodes are exact
negations numerically (`a + b == 0.0` at every index, all three levels) but
not bitwise, because the zero component of each (0, ±1, ±φ) vertex is stored
`+0.0` and negates to `-0.0`. 12, 24 and 48 bit mismatches at D = 12, 42, 162
— exactly the zero-component counts — and zero numeric mismatches. Benign,
since `dist2` sees the numeric equality, and untested either way until now.

Two tests added for gaps the lift created. `is_identity`'s only coverage was
a G2 call site that does not come with the module. And nothing tested the
premise licensing the `total_cmp` exemption — that `dirs` holds no `-0.0` and
no NaN. That second one is the important one: planting a `-0.0` silently
reorders the entire direction set, and 17 of the 18 tests pass straight
through it, because every other property is order-invariant.

Three tests widened from D=42 to all three levels, including the only one
tying the table to real rotations — confining the single geometric check to
one D is how a level-dependent indexing error survives, and this project has
already recorded the inverse mistake.

The test module trades `.unwrap()` for `unwrap_or_else(|| unreachable!(…))`:
`clippy.toml` sets no `allow-unwrap-in-tests`, so the workspace deny reaches
inside `#[cfg(test)]` and a verbatim lift would have broken this crate's
zero-bare-unwrap property.

Every guard was run against the mutation it exists to stop. Reordering
`perms` against the matrices fails exactly one test, the one whose doc claims
to be the only tie to geometry. Building the 60 improper elements fails
seven. `is_identity`'s tolerance was probed on both sides of its window. And
one ulp on a single coordinate fails the antipode assertion, which is three
orders inside the `< 1e-12` it replaced.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
Five specialists plus a CodeRabbit CLI pass. The headline was found twice,
independently, by the two lanes deliberately cross-routed onto each other's
domains — which is the convergence this project treats as worth more than
either lane's confidence.

THE CANONICAL DIRECTION ORDERING WAS AN OUTPUT THAT NOTHING PINNED. Every one
of the 18 tests asserted a property — group closure, bijectivity, det = +1,
the anti involution, -I absent, matrix/table agreement, unit norms — and every
one is invariant under relabelling `dirs` with the matching relabelling of
`perms` and `anti`, which is exactly what a reordered sort produces. Measured:
re-keying the sort (z,y,x) -> (x,y,z) left the whole 325-test workspace green.

The realistic mutation is not a deliberate re-key, it is a tidy-up. Rewriting
`normalise`'s `scale(a, 1.0 / n)` as componentwise division is mathematically
identical, better rounded, unflagged by clippy, and is how
`borbax_universe::packing`'s independent copy already spells it — and it moves
six of the 162 index positions at D=162, because 121 of the 161 adjacent pairs
share a bit-identical `z` and the tie is broken only by rounding. That was
also green across the workspace.

My own probe was the weaker half of the pair and I had stopped at it: it
perturbs a coordinate AFTER the sort, where it breaks antipodal symmetry and
is caught. Upstream of the sort, antipodal symmetry survives and nothing saw
it. `the_canonical_direction_ordering_is_pinned` is the fix, FNV-1a over dirs,
perms and anti at all three levels; the constants were reproduced
independently of the lane that proposed them, and are identical in debug and
release. Because the three `test` legs run on three platforms this is a
working §13.4 golden today rather than at Task 20 — where `goldens --emit`
currently exits 0 with "No golden artifacts yet".

Corroborated by two reviewers each:

- `GeoError::SubdivisionCountMismatch` replaces `UnsupportedResolution` at the
  `verts.len() != D` guard. `D` is already on the ladder when it fires, so the
  old variant printed "D must be 12, 42 or 162" when D was one of those — and
  it is the guard keeping `copy_from_slice` from panicking, so it is the one
  place the message is read.
- `vertex_count` and `level_for` collapsed onto one LADDER table. They were two
  independent encodings of the same ladder, and the deferral I wrote for the
  overflow was wrong twice: the first overflowing level is 31, not 32, and the
  cost to "every call site" was zero — there were none outside its own test.
  At level 32 release wrapping returned 2.
- `Vec3` made public; a `pub fn` signed with a private alias renders as a bare
  `[f64; 3]` beside a linkable `Mat3`.

Claims of mine that were wrong or overstated, withdrawn:

- phi/2 as "a constant of the icosahedral group". Max entrywise deviation is
  not a similarity invariant — conjugating by 0.3 rad gives 0.717 — so it holds
  only in the (0, ±1, ±φ) frame. Replaced with the basis-free bound
  2*sqrt(2)*sin36/3 = 0.554, true in any orientation.
- The portability inventory omitted `f64::midpoint`, the one operation here
  IEEE-754 does not specify. Now named, with the cross-ISA measurement that
  settles it: identical bit dump on aarch64 and x86-64 including
  -C target-cpu=x86-64-v3, and zero FMA-class instructions emitted.
- Two tolerance comments compared the wrong quantity — a linear spacing against
  a squared tolerance, and "headroom over zero", which is not a quantity.
- "ulp at 1.0" used in both directions 300 lines apart.
- That Task 10 needs `is_identity`; its plan section asks for neither it nor
  `rotation_matrices`.
- That a second copy is "the one thing that must never happen"; packing.rs
  holds a third, correctly.
- "Five separate comments" (four) and "the fourth item" (the third), both
  corrected in the amended message of the previous commit.
- experiments/README.md still advertised the file this branch deletes — the
  claims-land-in-more-places class, inside the commit whose message is about it.

Routed forward with discriminators: §22.8's actual claim has no test anywhere
in V0 (Task 8/9); Task 10's required probe, with the measurement showing a
same-index fixture INVERTS rather than merely failing to discriminate; and
packing.rs's six-position divergence against FRONTIER_COEFF (Task 20).

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
Two review findings taken together because both are cheap now and a breaking
change once Task 10 has consumers. Ian's scope call.

FIELDS ARE PRIVATE; `build` IS THE ONLY PRODUCER. Three invariants are
established there and were unenforceable afterwards: `rotation_perms` closed
under composition, `anti` an involution with `dirs[anti[i]] == -dirs[i]`, and
every index `< D`. With the fields public a caller could desynchronise them —
`g.dirs.reverse()` is enough — which reopens at runtime exactly the hazard the
`-0.0` probe found at construction, and every test but the ordering golden
passes through it because the rest are invariant under relabelling.

`perms` BECAME `rotation_perms`, AND `contact_perms = anti . rotation_perms`
IS PRECOMPOSED. Measured -13.21% at D=42 against `anti[perms[r][i]]`, noise
floor 0.24%, five signature-pool sizes from 3 KB to 2.6 MB, two process
launches agreeing to the timer quantum. -2.72% at D=162, where the kernel
stops being L1 load-throughput bound. The transpose layout was measured and
rejected at +0.00%/+0.24%; gather-then-sum at +11.6%/+57.2%.

The rename is the load-bearing half, and the performance lane corrected me on
this after I asserted the opposite in its brief. Precomposing alone does NOT
close the ANTI hazard: a sibling field still called `perms` reads MORE
plausible sitting next to the right one, so adding a table does not remove an
affordance. And `perms` cannot be deleted — Task 9's `canonicalise` rotates a
single signature with no contact involved and needs the raw permutation. Both
tables have real consumers, so the hazard closes only through the names.
`rotation_perms` inside a function called `affinity` is something a reviewer
reads as wrong; `perms` was not.

`contact_perms` deliberately gets no golden constant of its own — that would
be a second encoding of a derived fact, free to drift from the composition it
is supposed to be. Two tests pin it against the tables that do have constants,
and both fail when the `anti` step is dropped (probed):
`contact_perms_is_anti_composed_with_rotation_perms` and
`contact_perms_is_never_the_rotation_table`, the second because the composed
table is the improper coset and must never coincide with the rotation group.

The change is bit-identical by construction — same values, same order, only
the addressing differs — and `the_canonical_direction_ordering_is_pinned`
did not move, which is what says so rather than the sentence above.

Task 9's and Task 10's plan snippets were updated with it: Task 9 to
`rotation_perms(r)`, Task 10's kernel to `contact_perms(r)` with a comment
that the antipode is already applied. Nine call sites across the harnesses.

329 tests, six gate legs green.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
CodeRabbit's second pass on the branch, plus a whole-repo sweep for the old
name. Six findings, four applied, two held.

The rename `perms` -> `rotation_perms` reached every call site — the compiler
saw to that — and left five prose references standing, in exactly the class
this project keeps recording: a correction lands where the compiler looks and
not where the reader does.

- `rotation_matrices`' doc still said "`perms` is what the binding kernel
  needs". It is `contact_perms` now, and saying otherwise in the doc of the
  function that exists to cross-check the tables is the worst place for it.
- The struct doc, the golden test's doc, `is_identity`'s withdrawal note and
  the geometry-tie test's doc all named the old field.
- `experiments/src/signature.rs` carried two comments describing indexing
  through `perms[r]` above code already reading `rotation_perms(r)`.
- The plan's Interfaces block still published `Geodesic::{dirs, perms, anti}`
  as the produced surface, with no mention that the fields are now private.
- `build`'s `# Errors` section never gained `SubdivisionCountMismatch`, added
  in the previous commit two screens above it.

One left deliberately: the sentence explaining that `rotation_perms` inside a
function called `affinity` reads as wrong "where `perms` — the name this field
used to carry — read like the obvious choice" is about the old name on purpose.

Also corrected the plan's "there must be exactly one copy", which as written
forbids `borbax_universe::packing`'s independent oracle. The invariant is one
shared *production* table; a validation implementation answerable to nothing
but Euler is the opposite of a problem.

HELD, not applied. CodeRabbit proposed an index tie-break on the canonical
sort, on the grounds that it would make the order independent of sort
stability. Declined: `slice::sort_by` is stable, the comparator is a total
order, and no tie is reachable in the first place — `intern` dedups at 1e-9
while the nearest neighbours are ~0.28 apart at D=162, measured. The suggestion
adds an allocation and a `Vec<(usize, Vec3)>` round-trip to break ties that
cannot occur, and `the_canonical_direction_ordering_is_pinned` already guards
the thing it is worried about. Its second held finding — documenting the
accessors' panic-on-`r >= 60` — is real and is with a reviewer whose brief
asks for the better answer than a doc note.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
Cross-check round: three specialists pointed at the FIXES rather than the
original code, each routed away from the lane that proposed the thing it was
checking. The two worst findings are both in commit c93644d — the commit whose
whole purpose was closing the ANTI hazard.

THE PLAN'S TASK 10 SNIPPET NOW TAUGHT THE DEFECT. c93644d changed the kernel to
`let perm = g.contact_perms(r)` and `let j = perm[i]`, then APPENDED the new
explanation without deleting the old one. The block read "Indexing `b` by
`perm[i]` directly compares A against B's point inversion" immediately above a
line that now correctly does exactly that. An implementer following the comment
restores an `anti` step, gets `anti[anti[rotation_perms[r][i]]]` — the identity
— and lands the 60-improper-element search that CLAUDE.md's ANTI bullet exists
to prevent, by obeying the plan.

Two casualties of the same rename, both found by the same lane:
- CLAUDE.md and the plan both specified the required Task 10 probe as "delete
  `anti[...]` from `affinity`'s index expression". After the rename there is no
  `anti[...]` there to delete. The mandated probe was un-executable as written;
  it is now a substitution of `rotation_perms(r)` for `contact_perms(r)`.
- CLAUDE.md still said the kernel indexes through `ANTI[PERM[R][i]]`, never
  `PERM[R][i]` directly — the opposite of what the shipped API now asks for.

`contact_perms`' DOCSTRING NAMED THE WRONG POSE. It said "the direction on a
partner rotated by `r`". Measured on a support function at D=42 over all 60x42
pairs: `b[contact_perms(r)[i]]` equals `h_{R_r^T . B}(-u_i)` to 1.8e-15, while
the forward reading is off by 2.26. The partner sits at the TRANSPOSE. Costs
nothing in `affinity`, which maximises over a group closed under inverse; costs
§14.5's renderer everything, since it is handed the winning `r` precisely so it
does not re-derive the pose. A plausible, silent, wrong docking picture in a
project that ships SVG from day one because a shape-based chemistry cannot be
developed blind. The formula on the same line was correct throughout — this is
the class of error no table-versus-table assertion can see, so
`contact_perms_is_the_antipode_of_the_rotated_direction` now pins it against
the matrices directly.

`Rotation` newtype. `rotation_perms(r)` and `contact_perms(r)` took `usize` and
panicked on `r >= 60` — a library panic on an argument the type permits, which
CLAUDE.md treats as a correctness bug and which `clippy::panic` cannot see
because it arrives via indexing. Worse, the module-level
`#[allow(clippy::indexing_slicing)]` justified itself as "indexed by loop bounds
proven against D and N_ROTATIONS", which had silently become false for the two
`pub fn` taking caller input — an allow whose reason is wrong is worse than a
bare one, because the reason is what the next reader trusts. Rejected the two
alternatives on the lane's reasoning: returning the whole array moves the
indexing to Task 10's kernel, which then needs its own allow; `Option<&[u8; D]>`
in a 60-iteration hot loop invites `let ... else { continue }`, silently
dropping a rotation and returning a wrong affinity.

Claim-audit cluster, all verified by grep across source, tests, both plan files,
spec, README and CLAUDE.md:
- `GeoError`'s own doc still said "Both variants"; there are three since a067095
- `build`'s `# Errors` never gained `SubdivisionCountMismatch`
- "re-indexes the whole table" contradicted "six of the 162" in the same file
- "one ulp at 1.0" survived at two sites after the commit that claims to have
  disambiguated it — the recorded failure mode reproducing inside its own repair
- the plan's Step 4 counts were stale within one commit, twice
- `g2.rs`'s user-facing error string told the reader to "check perms"

Also: `dirs()` was the one public signature not using `Vec3`, undercutting the
reason `Vec3` was made public one commit earlier. `#[derive(Clone)]` dropped —
23.5 KB, zero cloners, and it quietly contradicts "built once and passed by
reference"; adding a trait impl later is additive, removing one is breaking.

And `packing.rs`'s discriminator tested the wrong thing. Re-running the probe
with the reciprocal-multiply form would show the six-index difference, which is
already known. The sharper question is why a *packing error* depends on vertex
ordering at all, given the recorded 1.1 band shift when the sort was added.

330 tests, six gate legs green, and the ordering golden did not move.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
`is_identity`'s doc withdrew the "Task 10's binding kernel needs this" claim by
quoting it back and then denying it, which leaves the sentence on the page for
the next reader to half-remember. Rephrased to the timeless form: the binding
kernel does not need it, the placement rests on the harness use.

Measured while I was in there: the non-test half of this file is 1.50 lines of
comment per line of code (the test module is 0.46, which is fine). A reviewer
called the top half a changelog and named ~44 lines of past-tense narration to
cut. I have taken the one clear case and left the rest for Ian, because his
standing preference is generous comments that capture the reasoning and the
same reviewer called several of those passages the best documentation in the
repository. That is a judgement call about register, not a defect.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
…sured

Final cross-check lane, and it supplied the measurement that turns the previous
commit's finding from an argument into a number.

CLAUDE.md's ANTI bullet used to say the kernel must index through
`ANTI[PERM[R][i]]`. `PERM` was renamed under it. Followed literally against the
new API that reads `anti[contact_perms[r][i]]` — and because `anti` is an
involution and `contact_perms = anti ∘ rotation_perms`, that is exactly
`rotation_perms[r][i]`:

    D= 12:  720/720   entries identical
    D= 42: 2520/2520  entries identical
    D=162: 9720/9720  entries identical

100% at every resolution. **The apparently-defensive second `anti` IS the
improper-coset search.** 8a02321 fixed the "reach for rotation_perms" spelling
and missed this one, which is the spelling the stale prose actively produced.
Both are now named, in CLAUDE.md and in the plan's kernel comment.

ROTATION_MATRICES WERE UNPINNED WHILE THE MODULE DOC CLAIMED OTHERWISE. The doc
says the cross-ISA bit dump covered "every direction, every permutation entry,
`anti`, and all 60 matrices". The golden pinned the first three.
`the_permutation_table_agrees_with_the_rotation_matrices` admits 1e-12, so a
change to `frame`'s arithmetic could move matrix bits with the whole suite
green. Now pinned at 0xe3181547c2169bfd, reproduced independently and identical
in debug and release. Probed: a 1e-15 perturbation to `frame` — three orders
inside that 1e-12 — fails the golden alone, 22 of 23 still passing.

A CORRECTION TO c93644d's COMMIT MESSAGE, which I cannot amend without
rewriting three commits of history. It said the precomposition is bit-identical
"and the ordering golden did not move, which is what says so". That is wrong:
the golden does not hash `contact_perms`, so it is evidence the rename and
privatisation did not perturb construction, not evidence about the
precomposition. The actual evidence is recomposing `anti[rotation_perms(r)[i]]`
at the call site and hashing both — identical at all three D on four builds
spanning two ISAs.

Worth recording from the same lane, because it corrects the reasoning rather
than the code: `contact_perms`' own two tests are **blind to compensating
errors by construction** — they assert exactly the invariant a compensating
substitution preserves. Mutating with σ = `anti` leaves
`contact_perms_is_anti_composed_with_rotation_perms`,
`minus_identity_is_not_in_the_group` and `antipode_commutes_with_every_rotation`
all green while 8 of 22 fail. What closes that gap is the golden hashing `anti`
and `rotation_perms` *separately*. Had it hashed only the composition, the hole
would be real. The decision not to give `contact_perms` its own constant was
right, for a different reason than the one I gave.

Portability is now executed rather than argued: all nine constants reproduce on
real x86-64 Linux, and the musl and gnu opcode streams for this module are
identical at 5234 instructions. Windows is inspected not executed — op-for-op
histogram match against linux-gnu, zero FMA-class instructions — so the first
windows-latest run is what settles it.

330 tests, six gate legs green.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 19796c08-00ab-449f-9c1b-20408c71e6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 24955fe and bf670b5.

📒 Files selected for processing (4)
  • CLAUDE.md
  • crates/borbax-molecule/src/geodesic.rs
  • crates/borbax-universe/src/packing.rs
  • experiments/src/g2.rs
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a shared geodesic geometry and rotation framework supporting three molecule resolutions.
    • Added validated direction, rotation, antipode and contact mappings for consistent molecule-signature comparisons.
    • Exposed geometry utilities for use across molecule and measurement tooling.
  • Refactor

    • Consolidated geodesic calculations so experiments and binding comparisons use the same validated tables.
    • Updated molecule signatures and binding orientation matching to use the unified rotation model.
  • Documentation

    • Updated implementation plans and guidance with geodesic, binding, and validation details.
    • Clarified packing comparison behaviour and measurement-table ownership.

Walkthrough

The PR adds a validated geodesic rotation oracle to borbax-molecule, exposes it through the crate APIs, migrates experiment consumers to shared accessor-based tables, and updates signature, binding, planning, and project documentation.

Changes

Geodesic table migration

Layer / File(s) Summary
Geodesic oracle and invariants
crates/borbax-molecule/src/geodesic.rs
Builds canonical geodesic directions and validated rotation, antipode, and contact permutation tables for supported resolutions, with extensive invariant tests.
Public API exposure and experiment wiring
crates/borbax-molecule/src/lib.rs, experiments/Cargo.toml, experiments/src/{lib.rs,signature.rs,g2.rs}, experiments/src/bin/*, experiments/README.md
Exports the shared geodesic API and updates experiment geometry, signature, diagnostics, and documentation to use it.
Signature and binding API alignment
docs/superpowers/plans/2026-07-26-borbax-v0.md, docs/superpowers/plans/2026-07-26-borbax-v0-chemistry.md
Records accessor-based signature canonicalisation, Rotation-based orientation selection, and precomposed contact permutations.
Project status and validation notes
CLAUDE.md, crates/borbax-universe/src/packing.rs
Updates task status and implementation guidance, and documents packing comparison ordering observations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Experiment
  participant Geodesic
  participant RotationTables
  Experiment->>Geodesic: build directions and permutation tables
  Geodesic->>RotationTables: obtain 60 rotation matrices
  RotationTables-->>Geodesic: return rotation mappings
  Geodesic-->>Experiment: expose dirs, rotation_perms, anti, contact_perms
Loading

Possibly related PRs

Poem

A rabbit hops through rotations bright,
Sixty paths arranged just right.
Antipodes pair, tables align,
Shared directions trace a line.
“No stale fields!” the bunny sings,
While signatures grow useful wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: Task 7 geodesic directions and rotation permutation table work.
Description check ✅ Passed The description is clearly related to the geodesic lift and shared implementation changes, so it is sufficiently on-topic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 252-262: Correct the classification in the paragraph beginning
“Two spellings break it and both are silent”: state that using rotation_perms(r)
omits the antipode, while applying anti again to contact_perms(r) yields anti ∘
anti ∘ rotation = rotation; identify both as searching the 60 proper rotations,
and describe the intended anti ∘ rotation mapping as orientation-reversing.

In `@crates/borbax-molecule/src/geodesic.rs`:
- Around line 1155-1201: Parameterize every_rotation_is_a_bijection and
permutations_form_a_group with a const-generic check::<const D: usize>() helper,
and derive the seen-array size and composition range from D instead of
hardcoding 42. Invoke the helper for the existing D=42 coverage while preserving
the current assertions and group checks.
- Around line 15-24: Update the module documentation around f64::midpoint to
describe its overflow check and fallback that halves the inputs, replacing the
inaccurate claim that it is simply (a + b) * 0.5. Keep the portability and
bit-identical deduplication discussion consistent with the corrected behavior.

In `@crates/borbax-universe/src/packing.rs`:
- Around line 446-473: Add the governing numbered PRD § reference to the Rust
comment documenting packing-error invariance under vertex ordering, alongside
the existing Task 7 and Task 20 references. If this invariant is not specified
by the PRD, explicitly mark the note as implementation-only instead.

In `@experiments/src/g2.rs`:
- Around line 319-323: Update the documentation line immediately above the error
attribute on the relevant Geodesic variant to reference rotation_perms instead
of perms, aligning it with the diagnostic message and the current symbol name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ff77ac3a-4a4e-4237-9575-d4f4fd0f6677

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7fbc1 and 24955fe.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • CLAUDE.md
  • crates/borbax-molecule/src/geodesic.rs
  • crates/borbax-molecule/src/lib.rs
  • crates/borbax-universe/src/packing.rs
  • docs/superpowers/plans/2026-07-26-borbax-v0-chemistry.md
  • docs/superpowers/plans/2026-07-26-borbax-v0.md
  • experiments/Cargo.toml
  • experiments/README.md
  • experiments/src/bin/fusion.rs
  • experiments/src/bin/ptable.rs
  • experiments/src/g2.rs
  • experiments/src/geodesic.rs
  • experiments/src/lib.rs
  • experiments/src/signature.rs
💤 Files with no reviewable changes (1)
  • experiments/src/geodesic.rs

Comment thread CLAUDE.md Outdated
Comment thread crates/borbax-molecule/src/geodesic.rs
Comment thread crates/borbax-molecule/src/geodesic.rs
Comment thread crates/borbax-universe/src/packing.rs
Comment thread experiments/src/g2.rs
…t caught it

CodeRabbit reviewed this PR — its first real round since #10 — and its Major
finding is correct on the arithmetic, which makes my sentence wrong as written.

I wrote that both ANTI mistakes "convert the search into the 60 improper
elements". As **index permutations** that is backwards: `anti` is `-I`, so the
correct composition `anti ∘ rotation` has determinant -1 and is the improper
coset, while both mistakes land on the 60 *proper* rotations. As
**transformations of the partner body** it inverts: the correct kernel searches
60 proper poses of B, and dropping the antipode searches B point-inverted.

Both readings are true of different objects, and the bullet asserted the second
while every surrounding sentence was about the first — which is how it managed
to be simultaneously right and backwards. Now says which is which.

The consequence needs neither framing and is provable rather than measured.
Since `mirror(B)[i] = B[anti[i]]`, pairing `a[i]` with
`b[rotation_perms(r)[i]]` is *exactly* the correct kernel evaluated against
`mirror(B)`. The defect does not merely score wrongly — it computes A's
affinity with B's enantiomer, so enantiomers become interchangeable and §22.8's
homochirality is impossible by construction rather than emergent.

Four more, all valid:

- The module doc said `f64::midpoint` "is `(a + b) * 0.5`" while the inline
  comment forty lines down said it is that *guarded*. Both describe the same
  function; only one was precise. Now states both branches and which is
  reachable.
- `every_rotation_is_a_bijection` and `permutations_form_a_group` hardcoded 42
  (`[false; 42]`, `(0..42)`) beside a `geo::<42>()` that already named it.
  §22.2 locks `D` to one of three values, and retargeting either test would
  have produced an out-of-bounds index or a silently truncated composition
  rather than a compile error. Both parameterised; they now run at all three.
- `g2.rs`'s `RotationSearchIsBroken` doc still named `perms` one line above the
  error string where I had already fixed it — the two halves of one diagnostic
  pointing a debugger at different symbols.

And one taken the other way. CodeRabbit asked for a numbered PRD reference on
`packing.rs`'s new block, or an explicit note that it is implementation-only.
The second is the honest branch: the spec says nothing about vertex visitation
order, because "an oracle must not depend on the order it visits its own
inputs" is a property of that probe's validity as ground truth, not an
obligation the simulation carries. Manufacturing a `§` would be worse than
saying there is not one. Written into the comment with the reasoning.

330 tests, six gate legs green.

MrReasonable <4990954+MrReasonable@users.noreply.github.com>
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