Skip to content

Parity ledger fails on main, and --refresh-derived cannot repair it #2211

Closed

Description

@ryanbr

What

Tools/parity_ledger.py fails on main, and the sanctioned repair path cannot run because it validates against the same state that is broken.

FAIL compact baseline drift in test-only-callsite|Packages/WhoopStore

Why the repair path is blocked

--refresh-derived is the reviewed refresh. It writes candidate snapshots, then validates them with parity_ratchet.py against --base, which defaults durably to origin/main. The ratchet refuses:

FAIL derived refresh rejected; snapshots restored
ERROR: Tools/parity_twin_map.json: base authority cannot be reproduced with the current derivation; migration required

That check is:

base_sets = parity_ledger.semantic_authority(base_root)   # base_root = the BASE ref's tree
base_manifest = parity_ledger.authority_manifest(base_sets)
if old_map["authority"] != base_manifest: ... migration required

So it compares the map committed at the base against a fresh derivation of that same base's source, and they disagree. main is internally inconsistent: its checked-in authority no longer reproduces from its own tree.

The deadlock is that --refresh-derived cannot produce the commit that would fix it, because its validation runs against the stale base rather than against the candidate. The other door is shut deliberately:

FAIL --bootstrap-map is for initial authority creation only; use the reviewed refresh workflow for existing authority

Which is correct. Those guards exist so an authority file cannot be rewritten without review, and I am not going to route around them.

What the drift actually is

Fourteen new test-only-callsite findings, all from the same place:

Packages/WhoopStore/Sources/WhoopStore/LiftLogStore.swift  (13)
Packages/WhoopStore/Sources/WhoopStore/LiftMuscle.swift     (1)

e.g. WhoopStore.liftExercises/1 has 3 test callsite(s) and no production callsite.

This is expected and temporary rather than a defect. #2098 landed the lift-log storage deliberately without its consumer, and #2099 is the app that calls this API. Every one of those becomes a production callsite when #2099 lands. Recording it as permanent debt would be recording something we already know is about to stop being true.

Options, none of which I want to pick alone

  1. Wait for Lift Log: an on-device gym log book, advanced by a double-tap on the strap #2099, then refresh once. The debt disappears on its own, but main stays red until then and every PR carries a failing check that is not theirs.
  2. Teach --refresh-derived to validate the candidate rather than the base when the base is provably unreproducible. That is the real tooling gap and it is not a five-minute change.
  3. A reviewed one-off migration commit regenerating the authority, which is what the error's own wording ("migration required") implies exists but which I could not find a supported route to.

The scheduled staleness check from #2142 will presumably start reporting this too, if it is not already.

Not caused by, and does not block

The iOS Coach change in #2207. It touches one SwiftUI view, the ledger is not in its check roster, and this failure reproduces on pristine main with that branch nowhere in sight.

Activity

  1. bhelm commented on Sep 14, 2026

    @bhelm

    Addressed by #2212.

    The PR adds a guarded stale-base repair path for derived parity snapshots and narrows Parity Governance CI so unrelated tests under Tools/tests no longer trigger it. The repair remains fail-closed for branch-added debt, changed dispositions, and partial metadata updates.

    All checks on #2212 are green.

  2. ryanbr commented on Sep 15, 2026

    @ryanbr
    OwnerAuthor

    Repaired by @bhelm in #2212, merged as 8039ad1. Main's ledger is green again, verified in a pristine clone: OK no NEW parity ledger findings (309 baselined).

    The deadlock was real and the fix is the door that was missing rather than a way around the guards. --repair-stale-base regenerates metadata only when the current tree's governed state is byte-identical to the stale base: semantic authority, finding identities, counters and typed dispositions all equal, with the authority and baseline both exactly derived. Six conditions, and failing any one of them refuses.

    Two things make that a guard rather than a gesture, and both are worth recording here because they are what I would have got wrong improvising it:

    The flag is absent from every workflow. Had the routine gate passed it, every PR would take the repair path and the conditions would never bite.

    The acceptance suite tests the refusals. Branch-added debt, disposition changes, a non-exact map, a non-exact baseline, and the flag requiring the guarded refresh. A guard proven only on its success case is not proven.

    On the fourteen lift-log entries the baseline now carries, which is what I was reluctant to accept when I filed this: that reluctance was wrong. When #2099 lands they become production callsites, the findings go, and the ledger reports debt decreased with cleanup optional, exactly as it already does for StrandAnalytics. Recording them is reversible. Main staying red until an unrelated PR merged was not reversible in the same way, and it would have put a failing check on every contributor's PR in the meantime.

    Wrapping this up. The scheduled staleness check from #2142 now has something it can point people at when it fires.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions