zdd-4 task 6: the ensureStaged wrapper never restages (incomplete pin set)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+26
−6
internal/release/adopt.go+5 −6internal/release/stage_reuse_test.go+21 −0
diff --git a/internal/release/adopt.go b/internal/release/adopt.go| index ee19ae754..7117204f9 100644 |
| --- a/internal/release/adopt.go |
| +++ b/internal/release/adopt.go |
| @@ -1702,13 +1702,12 @@ type stageCheck struct { |
| // is used as staged, because a box cannot compare what it cannot read, |
| // and the agent runs any attempt onto it in hold (CanonicalDemand). |
| // |
| -// ensureStaged is ensureStagedChecked with the pin set this package can read |
| -// with no registry in hand: the live links. The tests that stage one version |
| -// directly call it. |
| +// ensureStaged is ensureStagedChecked with no reference check. It has no |
| +// registry in hand, so it cannot know what refers to a staged copy and never |
| +// restages one: a copy that differs from its release is refused, and a full |
| +// pass restages it. |
| func ensureStaged(ctx context.Context, s Store, optDir, acct, app, ver, host string) (*Manifest, error) { |
| - return ensureStagedChecked(ctx, s, optDir, acct, app, ver, host, stageCheck{ |
| - refs: func() (StagedPaths, error) { return BoxPins(optDir, nil) }, |
| - }) |
| + return ensureStagedChecked(ctx, s, optDir, acct, app, ver, host, stageCheck{}) |
| } |
| // ensureStagedChecked is ensureStaged with the reuse check's references |
diff --git a/internal/release/stage_reuse_test.go b/internal/release/stage_reuse_test.go| index 6316dd364..47493e848 100644 |
| --- a/internal/release/stage_reuse_test.go |
| +++ b/internal/release/stage_reuse_test.go |
| @@ -273,3 +273,24 @@ func TestUnreadableManifestKeepsTodaysAnswerForAStagedCopy(t *testing.T) { |
| t.Fatalf("pass claimed a mismatch it could not establish:\n%s", out.String()) |
| } |
| } |
| + |
| +// The held stage has no registry rows, generations file or pin source in hand, |
| +// so it cannot know what refers to a staged copy. A copy that differs from its |
| +// release is refused and left as it was; only a full pass restages it. |
| +func TestStageHeldNeverRestagesADifferingCopy(t *testing.T) { |
| + ctx, s, opt := heldFixture(t) |
| + if err := StageArtifact(opt, "bab", "v1", "console", []byte("old v1")); err != nil { |
| + t.Fatal(err) |
| + } |
| + m, err := StageHeld(ctx, s, opt, heldRoute(), "v1") |
| + if err == nil || m != nil { |
| + t.Fatalf("StageHeld = %+v, %v; want a refusal", m, err) |
| + } |
| + if !strings.Contains(err.Error(), "what refers to it is unknown") { |
| + t.Fatalf("error = %q, want it to say what refers to the copy is unknown", err) |
| + } |
| + b, rerr := os.ReadFile(VersionPath(opt, "bab", "v1", "console")) |
| + if rerr != nil || string(b) != "old v1" { |
| + t.Fatalf("staged v1 = %q, %v; want it untouched", b, rerr) |
| + } |
| +} |