crucible depot: Increment 1 core (status/push/verify/get/pull + local/cdn/bunny backends) (#30)
test / test (push) Successful in 1m31s
build-binaries / build-binaries (push) Successful in 2m27s
build-image / publish (push) Successful in 42s

Implements Increment 1 of `docs/superpowers/specs/2026-07-04-crucible-depot-core-design.md`: a new stdlib-only `internal/depot` package wired into the dispatcher.

- `crucible depot status|push|verify|get|pull` with backends `local`/`cdn`/`bunny`; exit contract `0` clean / `1` drift / `2` unconfirmed-only / `64` usage / `70` internal.
- Presence is always probed against the real target (IPv4 1-byte range GET; HEAD is banned with a regression-tripwire test). `unconfirmed` is a distinct state, never collapsed into `missing`.
- No prompting anywhere: missing `BUNNY_STORAGE_*` env fails closed (read path included), enforced by a no-stdin test.
- Uploads: probe-then-PUT with `Checksum: <UPPER-sha>`; read/write key split (`BUNNY_STORAGE_READ_PASSWORD` falls back to `BUNNY_STORAGE_PASSWORD`).
- Field-driven fix included: per-probe transient retry (curl `--retry 2` equivalent) — without it a real 1490-blob CDN sweep reported 1222 false-unconfirmed; with it, 1490/1490 present in 74s, exit 0.
- Registry: depot `Wired: true`, joins the interactive menu; stale "(SeaweedFS)" wording removed.

Tests: unit + httptest fake-Bunny (probe sequence, Checksum header, key split, 428 throttling → exit 2) + local→bunny integration (drift → push → clean → idempotent no-second-PUT; incremental pull). `make check` green.

**Merge ordering:** this merges FIRST; the companion `sow-assets-manifest#crucible-depot-cutover` PR needs its flake input bumped to include this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #30
Reviewed-by: xtul <mpiasecki720@protonmail.com>
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit was merged in pull request #30.
This commit is contained in:
2026-07-04 23:30:54 +00:00
committed by archvillainette
parent 448ebc74d4
commit 9357b30994
19 changed files with 3176 additions and 32 deletions
+29 -5
View File
@@ -150,7 +150,7 @@ func TestBuilderHelpIsOK(t *testing.T) {
func TestCanonicalCommandSurface(t *testing.T) {
want := map[string][]string{
"depot": nil,
"depot": {"status", "push", "verify", "get", "pull"},
"hak": {"build", "manifest"},
"module": {"build", "extract", "validate", "compare", "manifest"},
"topdata": {"validate", "build", "package", "compare", "convert"},
@@ -172,7 +172,11 @@ func TestRegistryCommandNamesAndAliasesAreUnambiguous(t *testing.T) {
for _, builder := range Registry {
seen := map[string]bool{}
for _, command := range builder.Commands {
if command.Name == "" || command.Summary == "" || command.AppCommand == "" || command.Usage == "" {
// depot parses its own subcommands and bypasses AppCommand routing
// entirely (see the depot special-case in runBuilder), so its
// Commands carry no AppCommand.
requireAppCommand := builder.Name != "depot"
if command.Name == "" || command.Summary == "" || command.Usage == "" || (requireAppCommand && command.AppCommand == "") {
t.Errorf("%s has incomplete command metadata: %#v", builder.Name, command)
}
if seen[command.Name] {
@@ -289,15 +293,35 @@ func TestMenuItemsSkipsUnwiredIncludesWired(t *testing.T) {
t.Fatal("expected menu items")
}
sawModuleBuild := false
sawDepotStatus := false
for _, it := range items {
if it.Args[0] == "depot" {
t.Errorf("unwired builder %q must not appear in the menu", it.Args[0])
}
if len(it.Args) == 2 && it.Args[0] == "module" && it.Args[1] == "build" {
sawModuleBuild = true
}
if len(it.Args) == 2 && it.Args[0] == "depot" && it.Args[1] == "status" {
sawDepotStatus = true
}
}
if !sawModuleBuild {
t.Error("expected 'module build' in the menu")
}
if !sawDepotStatus {
t.Error("expected wired builder 'depot status' in the menu")
}
}
// TestDepotRoutesToDepotRun asserts the depot special-case in runBuilder
// reaches depot.Run rather than the generic Commands/delegateLegacy path or
// the unwired fail-closed path. With no manifests dir present, depot.Run's
// status command fails resolving the backend/manifest (exit 70), but the
// stderr text must come from depot, never the dispatcher's unwiredMsg.
func TestDepotRoutesToDepotRun(t *testing.T) {
var out, errw bytes.Buffer
code := runBuilder("depot", []string{"status", "--target", "local"}, &out, &errw)
if errw.Len() == 0 {
t.Fatalf("depot status with no manifests dir: expected an error from depot.Run, got no stderr (exit=%d)", code)
}
if strings.Contains(errw.String(), "not wired yet") {
t.Fatalf("depot status: stderr shows the dispatcher's unwired message, want depot.Run's own error:\n%s", errw.String())
}
}