Skip to content

fix(coord): the backlog number floor ignored every ref but origin/main - #88

Merged
wshallwshall merged 1 commit into
mainfrom
ledger-floor-refs
Jul 31, 2026
Merged

fix(coord): the backlog number floor ignored every ref but origin/main#88
wshallwshall merged 1 commit into
mainfrom
ledger-floor-refs

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

The bug

Get-Floor's own header comment in scripts/coord/alloc.ps1 says:

# FLOOR = max over (origin/main) U (every local + remote ref) U (existing allocations).

docs/LEDGER-GATE.md:56-58 repeats it. The adr branch does that. The backlog branch did not — it read origin/main, HEAD, and the working file, and nothing else.

That is not cosmetic. docs/BACKLOG.md here is a published baseline of a larger internal ledger — the file says so itself (the #185 banner, and the #313 reference). Numbers above the baseline live on refs this branch does not carry. Invisible to the floor, they were handed out as free.

On 2026-07-30 the allocator issued #240#247 over numbers that ADR 0115 and seven amended ADRs already cite as ASVS work packages (WP #243WP #246). Nobody noticed until a session cross-read the citations while filing unrelated items. Taking #244 would have made ADR 0014's WP #244 silently resolve to a Steps-view item.

Measured: old floor returns 251, corrected floor returns 314.

The fix

Sweep every local and remote ref, as promised — batched, not looped. ~550 refs share ~190 distinct BACKLOG.md blobs, and a git show per ref costs ~34s on Windows because each is its own process. Resolving all specs through one git cat-file --batch-check and streaming distinct blobs through one git cat-file --batch gives the same answer in ~3s, inside the "about a second, once per ADR" the docstring budgets.

Verified end to end, not just by inspection:

alloc.ps1 -Kind backlog   →   ALLOCATED BACKLOG #315   (3295ms)

Before this change that call returns #252 — straight back into the internal namespace.

Caveat, stated plainly: the floor is now as complete as the local ref set; a clone without those refs computes a lower floor. That is exactly the property the adr path has always had, and a lower floor was never the safe direction.

The erratum

docs/BACKLOG.md gains a preamble note recording what tooling cannot repair:

Written as a ### heading with no leading digit so it matches neither backlog_status_check.py's ^## (\d+)\. (which would demand a status banner) nor the allocator's own floor regex (which would silently move the floor).

Deliberately NOT done — both are owner calls

⚠️ Related hazard, not addressed here

ledger_check.py guards its only backlog rule with if not self.ci, so check_backlog() can never fail in CI. The CI backstop advertised in LEDGER-GATE.md:99-103 does not exist for BACKLOG numbers — a --no-verify commit squatting cited numbers would land green. Flagging rather than changing enforcement in a fix PR.

Verification

python scripts/docs/backlog_status_check.py   → OK — 241 items (unchanged; erratum is not parsed as an item)
python scripts/security/scan_forbidden.py     → exit 0
python scripts/hooks/ledger_check.py          → no blocks
alloc.ps1 parses clean; -List unaffected (230ms)

🤖 Generated with Claude Code

@wshallwshall
wshallwshall enabled auto-merge (squash) July 31, 2026 00:09
Get-Floor's own header comment says "FLOOR = max over (origin/main) U
(every local + remote ref) U (existing allocations)", and LEDGER-GATE.md
repeats it. The adr branch does that. The backlog branch did not -- it
read origin/main, HEAD and the working file, and nothing else.

That is not cosmetic. docs/BACKLOG.md in this repo is a published
baseline of a larger internal ledger, and numbers above the baseline live
on refs this branch does not carry. Invisible to the floor, they were
handed out as free: on 2026-07-30 the allocator issued #240-#247 over
numbers ADR 0115 and seven amended ADRs already cite as ASVS work
packages, and nobody noticed until a session cross-read the citations.
Measured here: the old floor returns 251, the corrected one 314.

Batched rather than looped. ~550 refs share ~190 distinct BACKLOG.md
blobs; a `git show` per ref costs ~34s on Windows because each is its own
process. Resolving all specs through one `git cat-file --batch-check` and
streaming the distinct blobs through one `git cat-file --batch` gives the
same answer in ~3s, which is the "about a second, once per ADR" the
docstring budgets for. Verified end to end: an allocation after this
change lands at #315, where before it would have taken #252 and walked
straight back into the internal namespace.

Caveat worth stating: the floor is now as complete as the local ref set.
A clone without those refs computes a lower floor -- but that is exactly
the property the adr path has always had, and a lower floor was never the
safe direction.

The erratum records what cannot be repaired by tooling: #242-#246 in the
ADRs are provenance into the internal ledger, not pointers into this
file; the numbers here above #231 are a second independent sequence;
#240-#247 and #315 are permanent holes. Renumbering would rewrite
ratified ADRs and republishing would cross SECURITY-DOCS-POLICY, so both
are left as owner calls rather than taken.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@wshallwshall
wshallwshall merged commit 4ea0957 into main Jul 31, 2026
32 checks passed
@wshallwshall
wshallwshall deleted the ledger-floor-refs branch July 31, 2026 00:31
wshallwshall added a commit that referenced this pull request Jul 31, 2026
Owner ruled 2026-07-30 that helper descent is too risky. ADR 0076
Amendment B goes PROPOSED -> DECLINED, and Phase D is not to be built.

Recorded in four places, because a decline that only exists where the
decline was argued is a decline nobody finds. The load-bearing one is
ADR 0089 itself: a session planning the next lens phase reads 0089's
phase list, not an amendment to a different ADR, and 0089's own text
sells Phase D as "the largest structural lever" against "265 delegating
call-sites". That paragraph is left intact with the ruling above it,
plus the correction that 265 is a statement-scan count and not a yield
estimate -- descent makes nothing editable by itself.

Decline-by-design, not deferral. The specification stays so the reasoning
is auditable and so the next reader who thinks helper delegation is the
biggest remaining win finds the work already done. Reopening takes a new
amendment, and must first answer the aliasing question (two call sites of
one helper produce identical child spans; expect_src matches both, so an
edit to one silently rewrites the other) and produce a real per-helper
measurement rather than the 218/522 heuristic superset.

Promotes the alternative from precondition to recommendation: teaching
Phase A the ast.Assign-to-ast.Subscript form (msg["X"] = v) widens what
is editable without touching the row shape, and is on no phase list.

Also fixes a stale cross-reference this session introduced: #239's banner
cited "#240 (comment-only rows)" from before that item was renumbered to
#248. #240 is now a permanent hole, so the citation pointed at nothing --
the exact failure the #88 erratum describes. The same banner's
"~70% of the opaque mass" argument is corrected: with Phase D declined it
rests on comment-only rows alone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
Owner ruled 2026-07-30 that helper descent is too risky. ADR 0076
Amendment B goes PROPOSED -> DECLINED, and Phase D is not to be built.

Recorded in four places, because a decline that only exists where the
decline was argued is a decline nobody finds. The load-bearing one is
ADR 0089 itself: a session planning the next lens phase reads 0089's
phase list, not an amendment to a different ADR, and 0089's own text
sells Phase D as "the largest structural lever" against "265 delegating
call-sites". That paragraph is left intact with the ruling above it,
plus the correction that 265 is a statement-scan count and not a yield
estimate -- descent makes nothing editable by itself.

Decline-by-design, not deferral. The specification stays so the reasoning
is auditable and so the next reader who thinks helper delegation is the
biggest remaining win finds the work already done. Reopening takes a new
amendment, and must first answer the aliasing question (two call sites of
one helper produce identical child spans; expect_src matches both, so an
edit to one silently rewrites the other) and produce a real per-helper
measurement rather than the 218/522 heuristic superset.

Promotes the alternative from precondition to recommendation: teaching
Phase A the ast.Assign-to-ast.Subscript form (msg["X"] = v) widens what
is editable without touching the row shape, and is on no phase list.

Also fixes a stale cross-reference this session introduced: #239's banner
cited "#240 (comment-only rows)" from before that item was renumbered to
#248. #240 is now a permanent hole, so the citation pointed at nothing --
the exact failure the #88 erratum describes. The same banner's
"~70% of the opaque mass" argument is corrected: with Phase D declined it
rests on comment-only rows alone.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

Correction to this PR's description — I overstated the CI gap

This PR's body says:

ledger_check.py guards its only backlog rule with if not self.ci, so check_backlog() can never fail in CI. The CI backstop advertised in LEDGER-GATE.md:99-103 does not exist for BACKLOG numbers — a --no-verify commit squatting cited numbers would land green.

The first sentence is accurate. The second is wrong, and the framing "defect" is wrong. Correcting it here because acting on it would break CI for everyone.

if not self.ci is correct, not an oversight. The rule it guards is ownership — "was this number allocated to this worktree" — and owns() compares against the recorded worktree path. CI checks out to a runner path that matches no allocation, so running this rule in CI would fail every PR that adds a backlog number. Do not remove that guard.

A CI backstop does exist — it just lives in another script. The failure that actually matters (a number reused against the published file) surfaces as a duplicate ## N. heading after merge, and scripts/docs/backlog_status_check.py:120 reports duplicates as errors. tests/test_backlog_status_check.py::test_the_real_backlog_satisfies_the_invariant runs that against the real docs/BACKLOG.md in the normal pytest matrix. That is the gate that reddened main in c786577e, so it is demonstrably live.

What genuinely has no CI coverage — and neither case is fixable in CI:

  1. Squatting a number allocated to another worktree but not yet filed (no heading exists anywhere). Allocation state is local; CI cannot see it.
  2. Squatting a number used only on unpublished refs — the case this PR fixes in alloc.ps1. CI does not fetch those refs.

So the asymmetry with the ADR path is real (ADRs get a CI-safe reuse check and a local ownership check; backlog gets the ownership check plus duplicate detection elsewhere), but it is not an unguarded hole, and there is no change to make here.

The alloc.ps1 fix in this PR stands unaffected — it addresses case (2) at allocation time, which is the only place it can be addressed.

wshallwshall added a commit that referenced this pull request Jul 31, 2026
BACKLOG #252 was allocated BEFORE #88 fixed alloc.ps1's floor, which read only
origin/main + HEAD despite a docstring promising every local and remote ref.
docs/BACKLOG.md is a published baseline of a larger internal ledger, so numbers
above #231 exist on refs this branch does not carry — and 252 was one of them:

  refs/remotes/vaultall/claude/throughput-thursday-4debf6
    ## 252. Per-row ingress-stage raw checksum (integrity anchor)

So the DICOM peer-control item was a duplicate of an unrelated integrity-anchor
item. Re-allocated with the fixed floor, which returns 316 — confirming the
jump the fix predicts. Verified 316 is free on every vault ref before taking it.

Renumbered in the entry, the gate comment, the refusal message, the test
docstring, docs/CONNECTIONS.md, the docs/SECURITY.md decision row and the
CHANGELOG. The claim moved with it: 316 taken, 252 released.

Deliberately a targeted replacement of "BACKLOG #252" and the "## 252." heading
rather than a global 252 -> 316 sweep. CHANGELOG.md mentions cp1252 twice, and a
naive replace turns it into cp1316 — a silent corruption in a file nobody
re-reads. Both occurrences verified intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
…#252) (#94)

* fix(dicom): an AE-title list is not a peer control on its own (BACKLOG #252)

The fail-closed peer-control gate refuses a non-loopback C-STORE SCP that has
no peer control, and it accepted any ONE of three: calling_ae_allowlist,
source_ip_allowlist, or mTLS. It COUNTED them rather than weighing them.

A Calling AE Title is a string the caller asserts about ITSELF in the
association request — no key, no signature, nothing to verify — and AE Titles
are published in conformance statements and visible in any capture. So an SCP
whose only peer control was an AE-title list was reachable by anyone who could
route to it and knew one string, while passing a check named "fail-closed peer
controls". Server TLS does not close it either: without tls_ca_file there is no
client certificate, so the cleartext bind guard (confidentiality) and this gate
(authentication) are orthogonal.

Option (a) of the three recorded on #252, chosen by the owner: PAIR, do not
remove. Off-loopback the gate now requires source_ip_allowlist or mTLS.
calling_ae_allowlist is KEPT and still enforced at association time — it
catches a misrouted sender and pins intent, which is real value — it simply
cannot stand alone.

Measured, off-loopback: AE-title-only STARTS -> REFUSED; AE-title PAIRED with
an IP allowlist starts; IP-only and mTLS-only unchanged; every loopback bind
unchanged (the common dev/single-box case does not regress). Full unfiltered
suite: 9518 passed, 0 failed.

The refusal detects the unpaired case specifically and explains why an AE Title
cannot be verified, and tells the operator to KEEP it and add a control —
otherwise the message reads as "but I already set an allowlist" and the obvious
wrong move is to delete the AE list.

BREAKING for a site whose non-loopback SCP relies on AE-title-alone. The fix is
one line (source_ip_allowlist on the inbound(...) call, which for a DICOM SCP
is the only authoring surface) and the refusal names it. Recorded in CHANGELOG.

A note on the citation. The code and both docs credited this rule to
"ADR 0025 §9". ADR 0025 has no section 9 — its decision sections run 1-8, and
the "§9" in that file refers to CLAUDE.md §9 — and calling_ae_allowlist appears
nowhere in it. The normative statement lives in docs/CONNECTIONS.md and the
docs/SECURITY.md decision table, which is what this commit amends. No ADR
amendment is possible because there is no ADR text to amend; the new code and
tests cite BACKLOG #252, which is real, rather than repeating a pointer that
sends a reader somewhere they cannot go.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ledger): renumber this item 252 -> 316, it collided (BACKLOG #316)

BACKLOG #252 was allocated BEFORE #88 fixed alloc.ps1's floor, which read only
origin/main + HEAD despite a docstring promising every local and remote ref.
docs/BACKLOG.md is a published baseline of a larger internal ledger, so numbers
above #231 exist on refs this branch does not carry — and 252 was one of them:

  refs/remotes/vaultall/claude/throughput-thursday-4debf6
    ## 252. Per-row ingress-stage raw checksum (integrity anchor)

So the DICOM peer-control item was a duplicate of an unrelated integrity-anchor
item. Re-allocated with the fixed floor, which returns 316 — confirming the
jump the fix predicts. Verified 316 is free on every vault ref before taking it.

Renumbered in the entry, the gate comment, the refusal message, the test
docstring, docs/CONNECTIONS.md, the docs/SECURITY.md decision row and the
CHANGELOG. The claim moved with it: 316 taken, 252 released.

Deliberately a targeted replacement of "BACKLOG #252" and the "## 252." heading
rather than a global 252 -> 316 sweep. CHANGELOG.md mentions cp1252 twice, and a
naive replace turns it into cp1316 — a silent corruption in a file nobody
re-reads. Both occurrences verified intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Jul 31, 2026
PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
wshallwshall added a commit that referenced this pull request Aug 3, 2026
#101)

PR #88 made the backlog floor read every ref, which is correct but leaves
the fix dependent on those refs continuing to exist. Measured on the
maintainer clone: the floor is 314 counting all refs and 252 counting
only refs/remotes/origin plus local heads. The 62-number difference lives
on remote-tracking refs for a remote that `git remote -v` no longer
lists, so a routine cleanup would revert the floor to its pre-fix value
and the allocator would resume issuing numbers that are already in use --
no error, no signal, the same bug again.

Every term in the floor is derived from something removable, so derive
one that is not: persist the highest floor ever computed at
alloc/<kind>/.floor-highwater and never go below it. A computed floor
under the mark prints a loud NOTE instead of quietly handing out a used
number. The mark only rises.

It is a backstop, not a substitute: the ratchet keeps the number space
correct, but if those refs go the history behind them is still gone.

Also documents a distinction a peer session flagged after running a prune
and worrying: `git fetch origin --prune` is SAFE -- it touches only
refs/remotes/origin/*, and it is what you should run before allocating.
The dangerous operations are removing a non-origin remote, deleting its
refs, or a gc/reflog expire that drops unreachable objects. An
undifferentiated "do not prune" warning is worse than none, because it
scares people off the fetch the allocation flow depends on.

Verified: allocation writes the mark (316) and lands #317; the ratchet
branch exercised in isolation over six cases -- collapse (252 vs mark
314) holds at 314 and warns, a rising floor advances the mark, first run
with no mark, an equal mark, and empty/non-numeric mark files, which must
neither crash nor zero the floor. The collapse case was tested against a
throwaway watermark rather than by deleting real refs.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.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