carlos / platform Public

zdd-6 task 7: fix round 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev d0996d3ceae275deb2d506da22bff9d437c58dd7 parent fb6d815
2 files changed, +27 −0
  • cli.go +1 −0
  • internal/console/apiship_swap_test.go +26 −0
diff --git a/cli.go b/cli.go
index c17a06ac2..bf6ab5830 100644
--- a/cli.go
+++ b/cli.go
@@ -2164,6 +2164,7 @@ func runShip(args []string) {
// the target (release versioning spec, 2026-08-08). Everything else about
// this check is unchanged — an app and at least one file are still the
// minimum a ship can mean.
+ // TODO-COPY zdd.ship.usage-flag
usage := "usage: carlos ship --app <a> [--version <sha>] [--label <one line>] [--notes <text> | --notes-file <file>] [--kind binary|static] [--account <sqid-or-name>] [--account-id <sqid>] [--console <origin>] [--capabilities <file.json>] [--program-change] [--swap hold] <files-or-site-dir>"
if fs.NArg() == 0 {
log.Fatal(usage)
diff --git a/internal/console/apiship_swap_test.go b/internal/console/apiship_swap_test.go
index b5b3e7656..1976dc358 100644
--- a/internal/console/apiship_swap_test.go
+++ b/internal/console/apiship_swap_test.go
@@ -70,3 +70,29 @@ func TestAPIShipWithoutSwapAnswersAsBefore(t *testing.T) {
t.Fatalf("a ship with no demand echoes swap: %v", out)
}
}
+
+func TestAPIShipRefusesAddingTheDemandOnAReship(t *testing.T) {
+ fx := newConsoleFixture(t)
+ liftShipBudget(fx)
+ token := cliToken(t, fx, "paul@keymail.dev", "code-ship-swap-add")
+ u := fx.ts.URL + "/api/cli/apps/" + fx.sqid + "/kass/releases"
+ art := shipPart{name: "linux-arm64", body: randomBytes(t, 64)}
+
+ if code, out := postShip(t, u, token, []shipPart{metaPart(t, "v12", "binary"), art}); code != http.StatusCreated {
+ t.Fatalf("ship = %d %v", code, out)
+ }
+ code, out := postShip(t, u, token, []shipPart{metaPart(t, "v12", "binary"), art})
+ if code != http.StatusOK {
+ t.Fatalf("identical no-demand re-ship = %d %v, want 200", code, out)
+ }
+ if _, present := out["swap"]; present {
+ t.Fatalf("a re-ship with no demand echoes swap: %v", out)
+ }
+ if code, out = postShip(t, u, token, []shipPart{metaPartSwap(t, "v12", "hold"), art}); code != http.StatusConflict {
+ t.Fatalf("re-ship adding the demand = %d %v, want 409", code, out)
+ }
+ m, err := release.GetManifest(context.Background(), fx.rel, fx.sqid, "kass", "v12")
+ if err != nil || m.Swap != "" {
+ t.Fatalf("stored manifest swap = %q, %v, want it unchanged", m.Swap, err)
+ }
+}