{"type": "meta", "slug": "cluster-source-of-truth", "base_branch": "origin/main", "gate0_mode": "strict", "preset": "cursor-codex"}
{"type": "goal", "text": "Make overdeck Git the single declared source of truth for one four-node fleet (workstation, debian1, debian2, debian3), converged by push from the workstation, with the workstation eligible to run builds or agents only under a controller-issued last-resort lease."}
{"type": "methodology", "text": "Contract-first: one fleet declaration, one convergence interface over local and ssh transports, idempotent audit/converge/harden. Delete the legacy capacity-spill path rather than adapt it. Land each wave through ship.sh; verify on live hosts, never only in tests."}
{"type": "intent", "text": "Tools, settings, CLAUDE.md/AGENTS.md and runtimes drifted per box because each was configured by hand; and the workstation silently absorbed build spill under load, which is exactly when it must stay free. One declaration and one lease make both impossible."}
{"type": "direction", "verbatim": "i basically want all 3 to have identical setup, the only difference between the 3 should be the number"}
{"type": "direction", "verbatim": "everything needs to be with one source of truth, and i guess it is overdeck?"}
{"type": "direction", "verbatim": "never synchronize one builder from another"}
{"type": "direction", "verbatim": "workstation is disabled unless debian1/debian2/debian3 are all down"}
{"type": "direction", "verbatim": "New key, legacy path stays dead"}
{"type": "anchor", "path": "docs/specs/2026-08-06-cluster-source-of-truth-design.md", "what": "spec"}
{"type": "anchor", "path": "docs/specs/2026-08-06-buildbox-second-disk-design.md", "what": "second-disk design, landed; debian1 converged, debian2/3 pending host return"}
{"type": "anchor", "path": "docs/plans/2026-08-06-cluster-source-of-truth.md", "what": "plan"}
{"type": "session_memory", "note": "Landing is ship.sh land <branch> <worktree> from /home/user/Projects/overdeck; raw pushes to main are rejected by the harness pre-push guard."}
{"type": "session_memory", "note": "debian2 and debian3 were offline for disk upgrades on 2026-08-06; a 17-minute cron re-probes and hardens them when they answer on port 2222."}
{"type": "session_memory", "note": "debian2/debian3 lost sshd on the boot after the extra disk went in: ip_nonlocal_bind was not yet set and Debian's ssh.service lists exit 255 as unrecoverable, so the failed ListenAddress bind was permanent. Only debian1 carried a hand-installed drop-in clearing RestartPreventExitStatus. Fixed in Git 2026-08-07 and proven on debian1; the two dark boxes need one power-cycle."}
{"type": "task", "id": "t1", "wave": 1, "seat": "coder", "tier": "high", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 1 (Fleet declaration, schema and loader) for the full contract. Add spec/fleet.schema.json, modules/fleet/fleet.json, the module descriptor modules/fleet/deck.module.json, the DesiredItem/Transport JSDoc typedefs in lib/fleet/types.mjs, and the sole validating loader lib/fleet/loader.mjs with its query interface. Acceptance: node --test modules/fleet/test/loader.test.mjs and bash tests/os/run-all.sh. Commit only: spec/fleet.schema.json modules/fleet/fleet.json modules/fleet/deck.module.json lib/fleet/types.mjs lib/fleet/loader.mjs modules/fleet/test/loader.test.mjs modules/fleet/test/node.test.sh.", "requires_decision": null, "files": ["spec/fleet.schema.json", "modules/fleet/fleet.json", "modules/fleet/deck.module.json", "lib/fleet/types.mjs", "lib/fleet/loader.mjs", "modules/fleet/test/loader.test.mjs", "modules/fleet/test/node.test.sh"], "deps": [], "verify": ["node --test modules/fleet/test/loader.test.mjs", "bash tests/os/run-all.sh"], "acceptance": ["node --test modules/fleet/test/loader.test.mjs passes", "bash tests/os/run-all.sh passes"]}
{"type": "task", "id": "t2", "wave": 2, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 2 (Profile expansion) for the full contract. Expand a node into ordered DesiredItems composed from existing manifests and devtools.json, never a second inventory. Acceptance: node --test modules/fleet/test/expand.test.mjs. Commit only: lib/fleet/expand.mjs modules/fleet/test/expand.test.mjs.", "requires_decision": null, "files": ["lib/fleet/expand.mjs", "modules/fleet/test/expand.test.mjs"], "deps": ["t1"], "verify": ["node --test modules/fleet/test/expand.test.mjs"], "acceptance": ["node --test modules/fleet/test/expand.test.mjs passes"]}
{"type": "task", "id": "t3", "wave": 3, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 3 (Local transport) for the full contract. Implement the Transport interface over the workstation filesystem and user systemd with staged atomic promote. Acceptance: node --test modules/fleet/test/transport-local.test.mjs. Commit only: lib/fleet/transport-local.mjs modules/fleet/test/transport-local.test.mjs.", "requires_decision": null, "files": ["lib/fleet/transport-local.mjs", "modules/fleet/test/transport-local.test.mjs"], "deps": ["t1"], "verify": ["node --test modules/fleet/test/transport-local.test.mjs"], "acceptance": ["node --test modules/fleet/test/transport-local.test.mjs passes"]}
{"type": "task", "id": "t4", "wave": 3, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 4 (SSH transport) for the full contract. Implement the Transport interface over the existing buildbox SSH path, resolving connection params from the buildbox registry. Acceptance: node --test modules/fleet/test/transport-ssh.test.mjs. Commit only: lib/fleet/transport-ssh.mjs modules/fleet/test/transport-ssh.test.mjs.", "requires_decision": null, "files": ["lib/fleet/transport-ssh.mjs", "modules/fleet/test/transport-ssh.test.mjs"], "deps": ["t1"], "verify": ["node --test modules/fleet/test/transport-ssh.test.mjs"], "acceptance": ["node --test modules/fleet/test/transport-ssh.test.mjs passes"]}
{"type": "task", "id": "t5", "wave": 4, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 5 (Shared audit/converge engine) for the full contract. Audit six fields and converge only drifted fleet-owned items; delegate agent-home items to remote-seat-provision.mjs and emit the parity report. Acceptance: node --test modules/fleet/test/engine.test.mjs. Commit only: lib/fleet/engine.mjs modules/fleet/test/engine.test.mjs docs/reports/fleet-agent-home-parity.md.", "requires_decision": null, "files": ["lib/fleet/engine.mjs", "modules/fleet/test/engine.test.mjs", "docs/reports/fleet-agent-home-parity.md"], "deps": ["t2", "t3", "t4"], "verify": ["node --test modules/fleet/test/engine.test.mjs"], "acceptance": ["node --test modules/fleet/test/engine.test.mjs passes"]}
{"type": "task", "id": "t6", "wave": 5, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 6 (Hardening path) for the full contract. Root convergence for exactly one SSH buildbox node with reboot verification and last-known-good restore. Acceptance: node --test modules/fleet/test/harden.test.mjs. Commit only: lib/fleet/harden.mjs modules/fleet/test/harden.test.mjs.", "requires_decision": null, "files": ["lib/fleet/harden.mjs", "modules/fleet/test/harden.test.mjs"], "deps": ["t5"], "verify": ["node --test modules/fleet/test/harden.test.mjs"], "acceptance": ["node --test modules/fleet/test/harden.test.mjs passes"]}
{"type": "task", "id": "t7", "wave": 6, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 7 (deckctl fleet command) for the full contract. Add the fleet audit/converge/harden/status surface behind the existing deckctl dispatcher contract; add fleet to BOTH the usage() string on bin/deckctl line 8 and the case allow-list on line 23. Acceptance: bash modules/fleet/test/cli.test.sh. Commit only: bin/deckctl lib/deckctl/fleet.sh modules/fleet/test/cli.test.sh.", "requires_decision": null, "files": ["bin/deckctl", "lib/deckctl/fleet.sh", "modules/fleet/test/cli.test.sh"], "deps": ["t6"], "verify": ["bash modules/fleet/test/cli.test.sh"], "acceptance": ["bash modules/fleet/test/cli.test.sh passes"]}
{"type": "task", "id": "t8", "wave": 7, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 8 (Convert deckctl sync apply into a fleet adapter) for the full contract. One mapping row only: convergeNode over the agent-home items of expandNode(fleet,'workstation'), wrapped by the existing sync_assert_not_denied / sync_backup_target / sync_record_baseline / sync_secret_scan layers, which must still run. Acceptance: bash modules/fleet/test/adapters.test.sh && bash tests/os/run-all.sh. Commit only: lib/deckctl/sync.sh tests/os/deckctl-sync.test.sh modules/fleet/test/adapters.test.sh.", "requires_decision": null, "files": ["lib/deckctl/sync.sh", "modules/buildbox/bin/buildbox", "modules/fleet/test/adapters.test.sh"], "deps": ["t7"], "verify": ["bash modules/fleet/test/adapters.test.sh", "bash tests/os/run-all.sh"], "acceptance": ["bash modules/fleet/test/adapters.test.sh passes", "bash tests/os/run-all.sh passes"]}
{"type": "task", "id": "t8b", "wave": 8, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 8b (Add engine audit coverage alongside the existing buildbox audit|bootstrap checks) for the full contract. Additive only: every pre-existing check stays, an auditNode/convergeNode pass is added, engine drift alone exits nonzero. Acceptance: bash modules/fleet/test/adapters-audit.test.sh && bash tests/os/run-all.sh. Commit only: modules/buildbox/bin/buildbox modules/fleet/test/adapters-audit.test.sh.", "requires_decision": null}
{"type": "task", "id": "t8c", "wave": 9, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 8c (Add hardenNode alongside the existing buildbox harden apply path) for the full contract. Additive only: apply.sh shipping stays, hardenNode runs inside the armed-deadman window before the reboot, zero selected items is a hard error. Acceptance: bash modules/fleet/test/adapters-harden.test.sh && bash tests/os/run-all.sh. Commit only: modules/buildbox/bin/buildbox modules/fleet/test/adapters-harden.test.sh.", "requires_decision": null}
{"type": "task", "id": "t9", "wave": 8, "seat": "coder", "tier": "medium", "desc": "Read docs/plans/2026-08-06-cluster-source-of-truth.md Task 9 (Move hosts out of build-remote.json) for the full contract. Host list becomes fleet.json only; the two real consumers of the hosts key (lib/deckctl/agents.sh, modules/workstation/claude/lib/remote-build.mjs) route through the loader, the four fixtures carrying a hosts key are updated, and a leftover hosts key fails the audit. cpu-guard.sh, remote-seat.mjs, remote-seat.sh, buildbox-checks.sh and host-config/apply.sh mention build-remote.json but read no hosts key and are OUT of scope. Acceptance: bash modules/fleet/test/hosts-migration.test.sh plus the repo os suite. Commit only: modules/fleet/fleet.json lib/fleet/loader.mjs lib/deckctl/agents.sh modules/workstation/claude/lib/remote-build.mjs modules/workstation/claude/tests/local-gate-requeue.test.sh modules/workstation/claude/tests/buildbox-registry.test.mjs modules/workstation/claude/tests/remote-build-transport.test.sh modules/harness/seat/test/seat-contract.test.sh modules/fleet/test/hosts-migration.test.sh.", "requires_decision": null, "files": ["modules/fleet/fleet.json", "lib/fleet/loader.mjs", "lib/deckctl/agents.sh", "modules/workstation/claude/lib/remote-build.mjs", "modules/workstation/claude/tests/local-gate-requeue.test.sh", "modules/workstation/claude/tests/buildbox-registry.test.mjs", "modules/workstation/claude/tests/remote-build-transport.test.sh", "modules/harness/seat/test/seat-contract.test.sh", "modules/fleet/test/hosts-migration.test.sh"], "deps": ["t8"], "verify": ["bash modules/fleet/test/hosts-migration.test.sh", "bash tests/os/run-all.sh"], "acceptance": ["bash modules/fleet/test/hosts-migration.test.sh passes", "bash tests/os/run-all.sh passes"]}
{"type": "session_memory", "note": "Prerequisites 0a/0b/0c are all landed and verified on 2026-08-07: second disk on debian1/2/3, zero-drift buildbox audit across all three, and lib/deckctl/system-units.sh is opt-in (declared paths only, dies on an undeclared systemd/system tree). The earlier note saying debian2/3 were pending host return is stale."}
{"type": "session_memory", "note": "fleet harden reboots the target. Gate it per host: the sshd RestartPreventExitStatus drop-in must be present on THAT host and that host must already have survived one power-cycle since it landed. debian1 proof does not transfer to debian2/debian3. Headless boxes, sshd on 2222 is the only door."}
