type: meta

slug: buildbox-scratch-gc

base_branch: origin/main

gate0_mode: strict

preset: claudex-workflow


type: goal

text: Reclaim remote build-box scratch on run completion, with a crash backstop, so debian1/debian2 never wedge at 0 bytes and silently hang every gated build.


type: methodology

text: Spec first (docs/specs/2026-08-05-buildbox-scratch-gc-design.md), then claudex-workflow with gpt-5.6-terra/medium as coder and gpt-5.6-sol/low as reviewer, waves of strictly file-disjoint tasks, then land and verify live on both hosts.


type: intent

text: A GC already exists and did not save the box: its floor pass requires a mirror to be older than 24h before it is eligible, so on the day that fills the disk nothing is evictable. Replace clock-based policy with ownership-based policy — a mirror is garbage exactly when its local root is gone.


type: direction

verbatim: you must build garbage collection and verify it works, these runs depend on it


verbatim: there should be a mechanism that will determine that a run is finished and will clear after each run. no point for waiting 1 day or 1 hour for anything


verbatim: and need to have some cleanup reaper also in addition to that, in case some run "crashed" just before it's cleaned up


verbatim: you write the spec for it, launch /claudex-workflow to implement. this is critical, so use gpt-5.6-terra/medium instead of luna/max


note: Run happens in worktree /home/user/Projects/overdeck/.worktrees/buildbox-gc on branch feat/buildbox-scratch-gc cut from origin/main. The main overdeck checkout is dirty with unrelated work and must not be touched.


note: Acceptance for the GC unit suite is `cd modules/workstation/claude && node tests/remote-build-gc.test.mjs` — it runs green on origin/main today, so a red after the change is a real regression signal.


note: debian1 carries 9 pre-existing candidate-* orphan mirrors. Reclaiming them via `bin/remote-mirror sweep --force` is the live acceptance evidence.


type: task

seat: coder

tier: medium

desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 1 (GC module) for the full contract. Add ownerRecord/releaseMirror/sweepOrphanMirrors and rewrite gcScript around the live-job guard instead of the clock. Commit only: modules/workstation/claude/lib/remote-build-gc.mjs.

acceptance: cd modules/workstation/claude && node --input-type=module -e "import('./lib/remote-build-gc.mjs').then(m=>{const need=['ownerRecord','releaseMirror','sweepOrphanMirrors','gcScript','GC_DEFAULTS'];const miss=need.filter(k=>!(k in m));if(miss.length)throw new Error('missing '+miss);if('gc_interval_hours' in m.GC_DEFAULTS||'gc_retention_days' in m.GC_DEFAULTS)throw new Error('removed keys still present');if(/86400\s*\)\s*\]\s*\|\|\s*continue/.test(m.gcScript('/tmp/x',{minFreeGb:1,repoRetentionDays:30,unownedGraceHours:24})))throw new Error('floor age gate still present');console.log('T1 OK')})" — expect exit 0 printing T1 OK


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 2 (owner record write) for the full contract. Write .rb-owner in syncPush, exclude it both directions, and sweep orphans before syncPush. Commit only: modules/workstation/claude/lib/remote-build.mjs.

acceptance: cd modules/workstation/claude && node tests/remote-build.test.mjs && node tests/remote-argv.test.mjs && node --input-type=module -e "import('./lib/remote-build.mjs').then(async m=>{const src=await (await import('node:fs/promises')).readFile('lib/remote-build.mjs','utf8');if(!src.includes('.rb-owner'))throw new Error('no owner write');console.log('T2 OK')})" — expect exit 0 printing T2 OK


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 3 (host-side backstop) for the full contract. Ship buildbox-gc plus item_scratch_gc. Commit only: modules/buildbox/bin/buildbox-gc, modules/buildbox/lib/buildbox-checks.sh.

acceptance: cd modules/buildbox && bash -n bin/buildbox-gc && bash -n lib/buildbox-checks.sh && grep -q 'item_scratch_gc' lib/buildbox-checks.sh && echo 'T3 OK' — expect exit 0 printing T3 OK


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 4 (remote-mirror CLI) for the full contract. release/sweep --force/status subcommands. Commit only: modules/workstation/claude/bin/remote-mirror.

acceptance: cd modules/workstation/claude && test -x bin/remote-mirror && bin/remote-mirror --help >/dev/null && echo 'T4 OK' — expect exit 0 printing T4 OK


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 5 (unit coverage) for the full contract. All 14 cases plus the disposition of the three existing cases. Commit only: modules/workstation/claude/tests/remote-build-gc.test.mjs.

acceptance: cd modules/workstation/claude && node tests/remote-build-gc.test.mjs — expect exit 0


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 6 (release at teardown) for the full contract. Non-fatal release call at every worktree removal path. Commit only: modules/workstation/claude/workflows/lib/finish-branch.sh, modules/workstation/claude/bin/wt-reaper.sh.

acceptance: cd modules/workstation/claude && bash -n workflows/lib/finish-branch.sh && bash -n bin/wt-reaper.sh && grep -q 'remote-mirror' workflows/lib/finish-branch.sh && grep -q 'remote-mirror' bin/wt-reaper.sh && echo 'T6 OK' — expect exit 0 printing T6 OK


desc: Read docs/plans/2026-08-05-buildbox-scratch-gc.md Task 7 (CLI integration test) for the full contract. Loopback ssh against a fabricated remote_root. Commit only: modules/workstation/claude/tests/remote-mirror-cli.test.sh.

acceptance: cd modules/workstation/claude && bash tests/remote-mirror-cli.test.sh — expect exit 0