Skip to content

test+build+ci: LCP-array coverage, release profile, and a Rust workflow (independent) - #11

Merged
rob-p merged 4 commits into
COMBINE-lab:mainfrom
BenjaminDEMAILLE:feat/foundations
Aug 12, 2026
Merged

test+build+ci: LCP-array coverage, release profile, and a Rust workflow (independent)#11
rob-p merged 4 commits into
COMBINE-lab:mainfrom
BenjaminDEMAILLE:feat/foundations

Conversation

@BenjaminDEMAILLE

Copy link
Copy Markdown
Contributor

Independent of every other open PR. Branches from main, no behaviour change.

This is #3 reduced to the part @rob-p accepted: the test and build scaffolding, without the packed-key work that PR also carried.

The crate had no test covering the LCP array

That is the riskiest gap in this codebase. The public entry points discard the LCP array, but the next merge level consumes it in the three-case decision, so a single wrong entry silently reorders suffixes one level up and the suffix array comes out subtly wrong — permutation intact, order not.

Four tests, checking lcp[0] == 0 and lcp[i] == lcp(text[sa[i-1]..], text[sa[i]..]) against a naive oracle over fixtures, random texts across four alphabet sizes, long runs and periodic text, and finite max_context.

62 tests before, 66 after.

The release profile was not what the benchmarks claimed

bench/README.md stated the published numbers were taken with lto = "fat" and codegen-units = 1, supplied by a parent workspace. That workspace is not in this repository. From the commit that made the crate standalone until now, every build made here used lto = false, codegen-units = 16, so numbers taken in that window are not comparable with numbers taken since. The profile is pinned here and the stale claim corrected.

In a library crate [profile.release] applies only when the crate is the workspace root, so this affects the repo's own tests, examples and benches and is invisible to downstream consumers.

CI

The repository had no Rust CI, only a docs deploy, while carrying 62 tests with nothing running them.

Runs on ubuntu-latest and macos-latest, since the LCP kernel and the pooled bucket path are what diverge per platform (x86_64/AVX2 against aarch64/NEON). Tests run in debug as well as release — that is not ceremony: debug is what exercises the debug_asserts guarding the buffer-length invariants, and on a later branch it caught a u64 shift-by-64 that release masks to zero and silently gets right. Plus fmt, Clippy with warnings denied, an MSRV check against the declared 1.89, and rustdoc with broken links denied.

The workflow triggers on every branch rather than only main. A pull request opened from a fork by a first-time contributor does not run workflows until a maintainer approves them, which is why the API showed no check runs at the tips reviewed in #7; building on push lets the fork produce evidence that can be linked.

Also

Fixes a pre-existing broken intra-doc link in build_ext_mem_for_filter, which pointed at the private FilteredSource and would fail the new rustdoc job.

Review notes from #7, addressed

You asked to correct the stale statement that ruSTAR has not migrated to SegmentedText and to clarify that the verifier is for complete plain SAs — both of those live in the packed-key part of #3 and are not in this PR. verify_sa is not here either; it belongs with the work that needed it.

🤖 Generated with Claude Code

BenjaminDEMAILLE and others added 4 commits August 12, 2026 09:21
Two prerequisites for the performance work that follows, neither of
which changes behaviour.

The crate had no test covering the LCP array. That is the riskiest
possible gap for this algorithm: the public entry points discard the
array, but the *next* merge level consumes it in the three-case
decision, so a single wrong LCP entry silently reorders suffixes one
level up and the SA comes out subtly wrong. Add four tests that check
`lcp[0] == 0` and `lcp[i] == lcp(text[sa[i-1]..], text[sa[i]..])`
against a naive oracle, over fixtures, random texts across four
alphabet sizes, long runs and periodic text, and finite `max_context`.

`bench/README.md` claims the published numbers were taken with fat LTO
and one codegen unit, supplied by a parent workspace. That workspace is
not in this repo, so every build made from it since the crate went
standalone has used `lto = false, codegen-units = 16`. Pin the profile
here. In a library crate `[profile.release]` applies only when this
crate is the workspace root, so it affects this repo's own tests,
examples and benches and is invisible to downstream consumers.

Measured on Apple M4 Max, 12 threads, chr21 FASTA (47.5 MB):
27.8 s -> 24.0 s wall. Neutral on N-free DNA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repository had no Rust CI at all: the only workflow deployed the
docs site, while 73 tests sat in the tree with nothing running them.
That is not a safe baseline for changing the sorting kernel.

Covers both architectures that matter here, since the LCP kernel and the
pooled external-memory bucket path are the parts that diverge per
platform: macOS is aarch64/NEON, Ubuntu is x86_64/AVX2. Runs the tests
in debug as well as release, because debug is what exercises the
`debug_assert`s guarding the unchecked scatter in `radix.rs` and the
buffer-length invariants in the merge kernel. Adds fmt, clippy with
warnings denied, a check against the declared 1.89 MSRV, and rustdoc
with broken links denied.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Public documentation linked to `FilteredSource`, which is private, so
`cargo doc` fails under `RUSTDOCFLAGS=-D warnings`. Pre-existing, but it
blocks the rustdoc job added in the previous commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A pull request opened from a fork by a first-time contributor does not
run workflows until a maintainer approves them, which is why the API
showed no check runs at the tips reviewed in COMBINE-lab#7. Building on push means
the contributor's own fork produces evidence that can be linked from the
PR. Adds `workflow_dispatch` for the same reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rob-p

rob-p commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Accepted and merged as cf71aaa75aff9021e9a228f14d52d9f256315733.

I reviewed this against current main after #9 and kept the PR head in the merge ancestry. The LCP-array oracle tests are valuable coverage of an invariant that the next merge level consumes, the broken rustdoc link fix is correct, and adding Rust CI closes a real project gap.

Validation performed:

  • 66 debug tests: pass
  • 66 release tests: pass
  • doc tests: pass
  • cargo fmt --all --check: pass
  • Clippy, all targets, warnings denied: pass
  • rustdoc, warnings denied: pass
  • GitHub fork-side checks: Ubuntu test, macOS test, MSRV 1.89, fmt/Clippy, and rustdoc all passed

The current GitHub runner-image mapping confirms that macos-latest is Arm64, so the matrix does exercise the NEON path as intended.

I did not run a full annotated-genome performance A/B for this PR because it has no runtime code change in the ruSTAR build. The added source is test-only, documentation-only, or workflow-only. The root [profile.release] change affects caps-sa when it is the workspace root, but Cargo ignores dependency-crate profiles; ruSTAR is the root and already specifies the same lto = "fat" and codegen-units = 1. Consequently the caps-sa code generation used by ruSTAR is unchanged on both sides. This PR is accepted for correctness/build/CI merits, not claimed as an additional ruSTAR runtime speedup.

One local operational note: on this host, running all external-memory tests concurrently can exceed the process file-descriptor limit; serial execution passes. The fork's Ubuntu and macOS jobs passed the default parallel invocation, so this did not block the merge, but bounding pooled files in concurrent tests may be useful future hardening.

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.

2 participants