Fix #79: commit manifest last + same-source resume #89

Merged
crueber merged 2 commits from fix/issue-79 into main 2026-09-05 02:30:07 +00:00
Owner

Fixes #79 (failed import wedges target: committed manifest, no access.json, un-actionable 409).

What

runImport PutCreates the in-progress import.json claim (complete:false) BEFORE the manifest CAS, commits the manifest provisionally, converges idempotently (presence-probed packs with .idx ensure, create-only ref converge that aborts loud on divergent refs, tier-2 repack, importer-admin), CAS-completes the sidecar (complete:true), and rolls every failure back to resumable-or-clean. Begin resumes same-source retries (202); 409 names only genuinely foreign manifests (no sidecar), foreign completions, and live foreign claims.

Why not literal manifest-last

The publish path needs the handle Create returns and internal/wal is untouchable (S12/law 8), so the commit stays provisional instead — same guarantee, arbitration unchanged. Documented in 10 Decisions.

Winner semantics

Exactly-one-winner preserved at three CAS points: claim PutCreate (adopt/resume/takeover/409), manifest PutCreate (unchanged name arbitration), completion PutUpdate (one completer, losers adopt same-source).

Tests

New resume_test.go: fault injection at ingest/admin/doc stages (rollback-to-clean or resume-converge, retry succeeds, never caller-unfixable), divergent-ref 409, Begin matrix (resume 202/live-foreign 409/expired-takeover), concurrent same-target one-winner, cross-instance converge, full error-arm units. Coverage 95.9% (>=95% gate), gofmt/vet clean, full suite green incl. -race. Evidence budget holds: 13 control PUTs, 7 GETs, 0 LIST, flat S/M.

Fixes #79 (failed import wedges target: committed manifest, no access.json, un-actionable 409). ## What runImport PutCreates the in-progress import.json claim (complete:false) BEFORE the manifest CAS, commits the manifest provisionally, converges idempotently (presence-probed packs with .idx ensure, create-only ref converge that aborts loud on divergent refs, tier-2 repack, importer-admin), CAS-completes the sidecar (complete:true), and rolls every failure back to resumable-or-clean. Begin resumes same-source retries (202); 409 names only genuinely foreign manifests (no sidecar), foreign completions, and live foreign claims. ## Why not literal manifest-last The publish path needs the handle Create returns and internal/wal is untouchable (S12/law 8), so the commit stays provisional instead — same guarantee, arbitration unchanged. Documented in 10 Decisions. ## Winner semantics Exactly-one-winner preserved at three CAS points: claim PutCreate (adopt/resume/takeover/409), manifest PutCreate (unchanged name arbitration), completion PutUpdate (one completer, losers adopt same-source). ## Tests New resume_test.go: fault injection at ingest/admin/doc stages (rollback-to-clean or resume-converge, retry succeeds, never caller-unfixable), divergent-ref 409, Begin matrix (resume 202/live-foreign 409/expired-takeover), concurrent same-target one-winner, cross-instance converge, full error-arm units. Coverage 95.9% (>=95% gate), gofmt/vet clean, full suite green incl. -race. Evidence budget holds: 13 control PUTs, 7 GETs, 0 LIST, flat S/M.
runImport PutCreates the in-progress import.json claim BEFORE the
manifest CAS, converges idempotently (presence-probed packs, create-only
ref converge, CAS-completed sidecar), and rolls every failure back to
resumable-or-clean: a failed import never wedges its target. Begin
resumes same-source retries (202); 409 names only genuinely foreign
manifests. Manifest Create stays the name-ownership arbitration
(exactly-one-winner at claim/manifest/completion CAS). Doc 10 §2/§4 +
Decisions updated in the same change (law 12).
Drain/cancel landing between claim-resolve and the manifest commit
deleted the claim unconditionally. In a resume run the claim is shared
with the prior attempt; with a surviving manifest the retry would then
409 foreign (the #79 wedge via the drain path). Now only fresh/taken-over
claims (mode == claimFresh) roll back; resume runs keep both commit
points. Regression: TestIssue79DrainRefusalKeepsResumeClaim (fails
pre-fix, passes post-fix). Doc: name pre-fix manifest-without-sidecar
as 409-by-design + broaden pack-dup residual.
Sign in to join this conversation.
No description provided.