Skip to content

Simplification round: collapse five repeated shapes, and fix the two cache leaks one of them bred - #55

Merged
JamesKane merged 6 commits into
mainfrom
refactor/simplification-round-2
Aug 17, 2026
Merged

Simplification round: collapse five repeated shapes, and fix the two cache leaks one of them bred#55
JamesKane merged 6 commits into
mainfrom
refactor/simplification-round-2

Conversation

@JamesKane

Copy link
Copy Markdown
Owner

A reuse-and-concision pass over the workspace, looking for shapes that had been copied rather than
named. Net −1,180 lines across five consolidations, plus one behaviour change that falls out of
the third.

Sequel to the 2026-06 round, which split the two God files. That one moved code between files; this
one removes copies of the same idea.

The behaviour change (fix(store):)

consensus_archaic, consensus_roh, consensus_painting and consensus_archaic_segments were the
same module four times over — same struct, same upsert/get/delete, same test — with a table name and
three column names swapped. ~420 lines to say one thing.

The copies had already cost something. Both places that purge a subject's derived results enumerate
the cache tables by hand, and each had fallen behind the set by a different amount:

  • biosample::clear_data — the "reset this subject's analysis" path — only ever named
    consensus_painting. ROH and both archaic caches survived a clear, still keyed to a consensus
    signature that no longer existed.
  • purge_alignment_derived named three of the four and left the Tier B archaic segments behind,
    keyed to an alignment that had just been deleted.

Both now iterate sig_cache::ALL. That is the fix; the deduplication is what makes the list exist.

The schema is untouched — the columns really are named differently per table for historical
reasons, so each cache carries its own names and get aliases them back to a common shape. Table
names are &'static str constants, never caller input. The tests run over ALL, including one the
four copies could not have written: that writing one cache does not disturb another sharing a column
name (segments belongs to both consensus_painting and consensus_archaic_segments).

The rest (behaviour-preserving)

  • navigator-syncpush_create_inner/push_put/push_delete/pull_list each carried their
    own copy of refresh-on-401 + exponential backoff + offline flag. A change to the retry policy had
    four places to land, on the path where getting it wrong means hammering a PDS or dropping a
    publish. One with_resilience driver; the four methods become one line each.
  • navigator-app — three clients reached the AppView independently and converged on the same two
    request shapes. social_post and exchange_post were byte-for-byte identical (a doc comment even
    said one mirrored the other), social_get/exchange_get_poll differed by a closure, and
    sync.rs/matching.rs open-coded it twice more. New appview.rs owns the URL, both shapes, the
    transport-error mapping (16 sites) and the non-2xx classification.
  • navigator-ui/cli.rs (2,415 → ~2,000) — ~70 four-line match blocks were standing in for ?
    in functions that return an exit code. An ExitCode trait lets a single cli_try! arm cover both
    error types; require_subject takes the twelve copies of the not-found block.
  • navigator-domain::new constructors for the workspace aggregates, and 91 exhaustive
    literals rewritten onto them. Fields that carry intent now lead, defaults sit behind a ...
    Deliberately not applied to the three store row→domain mappers, where the exhaustive literal is
    the safety property.
  • navigator-ui/chrome.rsalignment_has_bam (×5), cancel_button (×4), danger_button (×5).
    The cancel button's non-obvious detail — it disables itself once clicked, because cancellation is
    cooperative — was documented in one copy and absent from the other three.

Left alone on purpose

The 4× gVCF read_line preamble: an iterator version trades a reused line buffer for a per-line
allocation in a read path, and a closure version turns every continue into return Ok(()) through
60–100-line bodies. And the three egui two-column row closures: 26 call sites churned to save 9
lines.

Verification

cargo test --workspace 0 failures
cargo clippy --all-targets --workspace -- -D warnings exit 0
cargo fmt --all --check clean
all six commits cargo check --workspace --all-targets PASS

The history is bisectable — each commit was built individually, not just assumed to compile. The
first sweep caught a real failure (haplogroup.rs spans two themes and used appview_url a commit
before it existed); fixed by reordering the last two commits, with git diff confirming the final
tree stayed byte-identical to the one the test suite ran against.

🤖 Generated with Claude Code

JamesKane and others added 6 commits August 17, 2026 08:24
push_create_inner, push_put, push_delete and pull_list each carried their own copy of the
engine's resilience discipline — refresh-on-401 once, exponential backoff on transient
failures, mark offline, give up past the cap. Twenty-five identical lines, four times,
differing only in which PdsClient method sat in the middle.

That is the kind of duplication that does not stay identical. A change to the backoff, or
to what counts as retryable, had four places to land and no way to notice a missed one —
on the path where getting it wrong means either hammering a PDS or silently dropping a
publish.

`with_resilience` takes the call to retry and owns everything around it; the four methods
become one line each. It re-invokes `op` per attempt against a client rebuilt from the
possibly-rotated session, which is why it is `Fn` and why the record-carrying callers clone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every `navigator <subcommand>` is a function returning the process exit status, so `?` was
unavailable and each fallible step wrote its own four-line `match` to turn an error into a
code. Seventy of them had accumulated in one file. Reading a command meant reading past
them to find the three or four things it actually does.

Two error types reached those matches, and they differ only in whether the message has been
printed: the helpers here print their own and hand back a code, `App` returns an AppError
nobody has shown the user yet. An `ExitCode` trait is that seam, so `cli_try!` needs one arm
and covers both. Sixty-four blocks collapse to one line each.

`require_subject` takes the other twelve: the "no subject with identifier" not-found block,
copied verbatim wherever a `--subject` command started up.

No behaviour change — `report` still exists for the `.map_err(report)?` sites inside the
Result-returning helpers, and now just calls through to the same printing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three small shapes had been copied around the tabs rather than named:

- the "does this alignment have a file we can walk" lookup, five times across two files,
  each spelling out the same find-then-map-then-unwrap_or(false);
- the cancel button, four times, including its non-obvious detail — it disables itself once
  clicked, because cancellation is cooperative and a live button made a working cancel look
  ignored. That reasoning was written out in one copy and absent from the other three;
- the destructive-action button (filled DANGER, white label), five times.

They move to chrome.rs beside `tr`, where the next tab can find them. The analysis modal
keeps its own cancel: it defers the click out of the closure and adds a spinner, so it is a
genuine variant rather than another copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Biosample, SequenceRun and Alignment are mostly Option fields that are None until some later
pass populates them — the struct docs say so outright. Every construction site still had to
write the whole column of `None`s, 91 of them across the workspace, and the two or three
fields that carried the actual intent were buried in the middle of it.

Each type gets a `new` taking exactly the fields with no sensible empty value. Sites that
know more say so with functional-update syntax, which puts the interesting fields first and
the defaults behind a `..`: an alignment with a BAM path now reads as one.

Left alone deliberately: the three row-to-domain mappers in the store. There, every field is
assigned from a column and the exhaustive literal is the point — adding a column should fail
to compile until the mapping handles it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three clients reached the Edge API independently — IBD exchange, social, recruitment — and
each arrived at the same two shapes: a POST whose body carries the device-key signature, and
a replay-guarded signed GET with did/ts/sig on the query. `social_post` and `exchange_post`
were byte-for-byte identical; the doc comment on one even said it mirrored the other, which
is a duplication noticed and then kept. `social_get` and `exchange_get_poll` differed only by
a closure. sync.rs and matching.rs open-coded the same request a fourth and fifth time, and
sixteen sites spelled out the same reqwest-error mapping by hand.

appview.rs is all of it: the URL, the two request shapes, the transport-error mapping, and
the non-2xx classification. `exchange_get_poll` survives as a one-liner because the exchange
endpoints all sign the same canonical poll string — that, and not the HTTP, was its content.

fetch_exchange_key and the ibd/suggestions poll keep their own bodies: one treats 404 as
absence, the other retries a 403 while the AppView verifies a freshly registered device key.
Both now build their URL and map their errors through the shared helpers.

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

consensus_archaic, consensus_roh, consensus_painting and consensus_archaic_segments were the
same module four times over — same struct, same upsert/get/delete, same test — with a table
name and three column names swapped. Roughly 420 lines to say one thing.

The copies had already cost something. Both places that purge a subject's derived results
enumerate the cache tables by hand, and each had fallen behind the set by a different amount:

  - `biosample::clear_data`, the "reset this subject's analysis" path, only ever named
    consensus_painting. ROH and both archaic caches survived a clear, still keyed to a
    consensus signature that no longer existed.
  - `purge_alignment_derived` named three of the four and left the Tier B archaic segments
    behind, keyed to an alignment that had just been deleted.

Both now iterate `sig_cache::ALL`, which is the list. That is the fix; the deduplication is
what makes the list exist.

The schema is untouched — the column names still differ per table for historical reasons, so
each cache carries its own and `get` aliases them back to a common shape. The tests run over
ALL, including one that the four copies could not have written: that writing one cache does
not disturb another sharing a column name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JamesKane
JamesKane merged commit 57280a1 into main Aug 17, 2026
6 of 9 checks passed
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