Skip to content

fix!: bump seqpro to 0.22.0 for per-variant allele indexing (#330) - #331

Merged
d-laub merged 5 commits into
mainfrom
worktree-deps+seqpro-0.22
Jul 29, 2026
Merged

fix!: bump seqpro to 0.22.0 for per-variant allele indexing (#330)#331
d-laub merged 5 commits into
mainfrom
worktree-deps+seqpro-0.22

Conversation

@d-laub

@d-laub d-laub commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Closes #330. Upstream fix: ML4GLand/SeqPro#71, released in seqpro 0.22.0.

What was wrong

RaggedVariants.alt[i] / ref[i] returned one concatenated bytes per haplotype while start / ilen returned per-variant values, even though all four fields share one offsets object. zip(rv.start[0][h], rv.alt[0][h]) was silently wrong — and invisible for SNP-only data, because 1-byte alleles make the counts coincide. It only misbehaved once an indel entered the window.

Root cause was upstream: seqpro's Ragged.__getitem__ mapped a string-under-axis group straight to a byte range and discarded the interior str_offsets.

Changes

  • pyproject.toml: seqpro>=0.20seqpro>=0.22
  • pixi.toml: py310 pin ==0.21.1==0.22.0
  • pixi.lock regenerated
  • New regression test using a multi-byte allele — the existing suite asserts via to_ak().to_list(), which is structure-preserving and was blind to this
  • SKILL.md + docs/source/dataset.md document per-variant allele access and the seqpro>=0.22 requirement

Cargo.toml is untouched: the 0.22.0 diff changed no Rust, so seqpro-core needs no bump.

Raising the floor also forecloses a latent skew — py311/312/313 declare no seqpro pin and inherit the floor, so now that genoray's cap is lifted a lock refresh under >=0.20 would float them to 0.22 while py310 stayed pinned at 0.21.

Also raises the genoray floor to >=3.3.1

Every genoray up to 3.3.0 pinned seqpro>=0.21.1,<0.22, which made seqpro>=0.22 unsolvable. That cap was lifted to <0.23 in d-laub/genoray#142 and released as genoray 3.3.1, so pixi.lock here moves genoray 3.0.0 → 3.3.1.

genoray>=3.0.0,<4 was not wrong — because seqpro>=0.22 is a direct top-level constraint, pip/uv/pixi all reject the older genoray releases themselves rather than resolving to something broken. But they report it as a transitive seqpro conflict, which points away from the actual requirement. So the floor is now declared directly, in both pyproject.toml and pixi.toml (the py310 feature mirrors the pyproject floor).

Resolution is unchanged: the lock already selected genoray 3.3.1, and the only lock delta is the recorded requires-dist line. This changes the error message users get, not what they install.

Breaking

Indexing an allele field by integer now yields per-variant alleles instead of one concatenated bytes. Callers wanting the old value can use b"".join(...). major_version_zero = true, so this cuts 0.40.2 → 0.41.0.

Verification

Full suite on a dedicated SLURM allocation (32 CPUs), against the real solved environment (seqpro 0.22.0 + genoray 3.3.1), no deselections:

1110 passed, 56 skipped, 4 xfailed in 365.79s (0:06:05)

1110 + 56 + 4 = 1170 — the full collected count. Also green: cargo test --release (138 passed), pyrefly (0 errors), ruff check / ruff format --check, the api.md-vs-__all__ gate, a clean sphinx-build, and cz check over the branch (so the BREAKING CHANGE: footer is machine-readable and the bump lands as a minor, not a patch).

The regression test was confirmed RED on seqpro 0.21.1 before the bump (list(rv.alt[0][0]) yielded [65, 71, 71] — ints from iterating a bytes blob — instead of [b"A", b"GG"]) and GREEN after.

An audit of the six string-Ragged consumers found that all of them only construct string Raggeds or read fields by name / _rl.str_offsets / .to_chars(), none of which changed in 0.22.0. grep -rnE "\.(alt|ref)\[[0-9a-z_]" python/genvarloader/ returns zero hits, so nothing in the package ever depended on the old concatenating behavior.

Not included

Targets main, not streaming — the defect is in the shared RaggedVariants read path the written Dataset returns too. streaming inherits the fix on its next routine main merge.

🤖 Generated with Claude Code

d-laub and others added 5 commits July 27, 2026 18:16
Design for bumping seqpro 0.21.1 -> 0.22.0, which fixes the
string-under-axis Ragged indexing bug (ML4GLand/SeqPro#71) that made
RaggedVariants.alt[i]/ref[i] return one concatenated bytes per
haplotype while start/ilen returned per-variant values.

Off main, not streaming: the defect is in the shared RaggedVariants
read path that the written Dataset returns too.

Covers the pin changes (pyproject floor >=0.22, pixi py310 ==0.22.0,
Cargo.toml unchanged since 0.22.0 touched no Rust), whole-tree
verification plus a targeted audit of the six modules touching
string-Ragged internals, a multi-byte-allele regression test to close
the coverage gap that hid the bug, docs updates, and a breaking
fix!: framing so commitizen cuts 0.41.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four tasks off the approved spec: (1) regression test written against
the still-installed seqpro 0.21.1 so it is SEEN failing, then the pin
bump turns it green in the same commit; (2) whole-tree pytest + cargo
verification plus an audit of the six modules touching string-Ragged
internals; (3) SKILL.md + dataset.md docs; (4) push and draft PR.

Tasks 2 and 3 are independent and may run in parallel.

Test code and the expected 0.21.1 failure mode are taken from seqpro
0.22.0's own #71 regression tests rather than guessed: peeling a group
returns a Ragged where len() works and iteration yields bytes, and the
multidim test uses the same (b, p, ~v) chain. On 0.21.1 the same
expression returns concatenated bytes, so iteration yields ints.

Records that the worktree already carries a working dev env at
seqpro 0.21.1 with a built extension -- verified, not assumed -- which
the red step depends on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Documents that rv.alt[b][h] / rv.ref[b][h] yield one allele per
variant, aligned with rv.start / rv.ilen, and that this requires
seqpro >= 0.22. Adds a Common gotchas entry noting the old
concatenating behavior was invisible for SNP-only data.

Additive only -- no existing prose demonstrated integer allele
indexing, so nothing was false before this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RaggedVariants.alt[i]/ref[i] returned one concatenated bytes per
haplotype while start/ilen returned per-variant values, because
seqpro's Ragged.__getitem__ mapped a string-under-axis group straight
to a byte range and dropped the interior str_offsets. zip(start, alt)
was silently wrong -- and invisible for SNP-only data, since 1-byte
alleles make the counts coincide.

Fixed upstream in seqpro 0.22.0 (ML4GLand/SeqPro#71): integer indexing
on a string-under-axis Ragged now returns a Ragged of strings.

Bumps the pyproject floor to >=0.22 and the pixi py310 pin to ==0.22.0.
Cargo.toml is untouched -- the 0.22.0 diff changed no Rust. Raising the
floor also forecloses a latent skew: py311-313 declare no seqpro pin
and inherit the floor, so once genoray's cap is lifted a lock refresh
under >=0.20 would float them to 0.22 while py310 stayed pinned at 0.21.

Adds the regression test whose absence let this ship: the existing
suite asserts via to_ak().to_list(), which is structure-preserving and
blind to the bug. The new test uses a multi-byte allele, without which
it would pass on the buggy version too.

BREAKING CHANGE: indexing a RaggedVariants allele field by integer
(rv.alt[b][h], rv.ref[b][h]) now yields per-variant alleles instead of
one concatenated bytes object. Callers relying on the concatenated
value can recover it with b"".join(...). Requires seqpro>=0.22.

Closes #330

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every genoray up to 3.3.0 pins seqpro>=0.21.1,<0.22, so none of them can
satisfy the seqpro>=0.22 floor this branch introduces. The cap was lifted
to <0.23 in d-laub/genoray#142 and released as genoray 3.3.1.

The previous >=3.0.0,<4 range was not wrong -- because seqpro>=0.22 is a
direct top-level constraint, pip/uv/pixi all reject the older genoray
releases on their own rather than resolving to something broken. But they
report it as a transitive seqpro conflict, which points away from the
actual requirement. Declaring the real floor turns that into a direct,
readable constraint failure.

Resolution is unchanged: pixi.lock already selected genoray 3.3.1, and
the only lock delta is the recorded requires-dist line. pixi.toml is
bumped alongside pyproject.toml so the py310 feature keeps mirroring the
pyproject floor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@d-laub
d-laub marked this pull request as ready for review July 29, 2026 23:30
@d-laub
d-laub merged commit f490d50 into main Jul 29, 2026
8 checks passed
@d-laub
d-laub deleted the worktree-deps+seqpro-0.22 branch July 29, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant