Skip to content

feat(bin): support safe record-only teardown for reassigned pool slots - #7601

Closed
ichts wants to merge 10 commits into
kunchenguid:mainfrom
ichts:fm/fm-record-only-teardown-upstream
Closed

ichts wants to merge 10 commits into
kunchenguid:mainfrom
ichts:fm/fm-record-only-teardown-upstream

Conversation

@ichts

@ichts ichts commented Oct 21, 2026

Copy link
Copy Markdown

Intent

The translateimage second mate's project pool (16 worktree slots for nanobanana2) is jammed by 34 task records: each slot has one slot-owner claim file naming the task that really holds it, plus 1 to 4 stale records naming the same path. bin/fm-teardown.sh refuses any record on a shared slot at its record-exclusivity scan, which runs before the slot-owner claim check, so owner and stale records block each other and no record can be cleaned up safely. Verified live: tearing down ti-eur-pricing (its PR 85 merged, branch head equals merged head) was refused naming pe-q1-refine-seams. No supported command exists to retire a stale record; the only workaround is hand-editing state/.meta, which is forbidden. Several records hold genuinely unlanded work (local-only commits, open PRs, dirty files), so forcing is not acceptable. Full per-slot evidence: 16 slots each contain a positive owner claim and stale task records, so a record-only retirement must not read, return, prune, reset, or run git inside the occupied slot. The ask: a safe, supported way to retire a finished task record whose worktree slot is owned by a different task, without touching that other task's worktree.

What Changed

  • Allow completed task records to be retired when a verified Treehouse pool slot is claimed by another task, without accessing or modifying the occupied worktree.
  • Require valid foreign ownership and proof that the recorded branch landed; malformed claims, unverifiable slots, and unlanded work remain fail-closed even with --force.
  • Expand teardown safety coverage and document the record-only cleanup path.

Risk Assessment

✅ Low: The record-only path is narrowly gated by verified pool metadata, a foreign owner claim, project locking, and fail-closed landing proof while occupied-slot operations are skipped.

Testing

The targeted teardown safety suite passed. An end-to-end merged-PR fixture retired only the stale record, preserved the current owner's record, claim, and occupied files, made zero git calls inside the occupied slot, and never called Treehouse return. The same focused test also exercised unlanded, forced, malformed-claim, and unverifiable-pool refusal paths.

Evidence: Record-only teardown CLI transcript and persisted-state evidence

Source: Record-only teardown CLI transcript and persisted-state evidence

Record-only teardown end-to-end evidence
========================================
Command under test: bin/fm-teardown.sh old-task

CLI stderr:
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  2 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e /Users/ichts/.no-mistakes/worktrees/7bf215c2d2ab/01M5EYWGE6ANSZ547YG71HR7XX/.pi/extensions/fm-primary-turnend-guard.ts -e /Users/ichts/.no-mistakes/worktrees/7bf215c2d2ab/01M5EYWGE6ANSZ547YG71HR7XX/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
warning: task old-task's recorded worktree /private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/pool/1/project was reassigned to task new-task (home /private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home), which claimed that pool slot after this record was written; that slot is no longer old-task's, so its processes, copy, and claim are left untouched and only old-task's own cleanup runs.

CLI stdout:
teardown old-task complete (window firstmate:fm-old-task; pool slot /private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/worktree left to task new-task (home /private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home), which it was reassigned to)
Backlog: old-task is closed in /private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home/data/backlog.md. Run bin/fm-tasks-axi.sh ready for dependency-cleared candidates, check date gates, and dispatch only work whose blockers are gone and date is due.

Persisted state after teardown:
stale record: ABSENT (retired)
slot-owner record: PRESENT (preserved)
slot-owner claim:
task=new-task
home=/private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home
occupied-slot files: sentinel=present neighbor=present
git calls targeting occupied slot: 0 bytes

Lifecycle calls:
tasks-axi <--version>
tasks-axi <update> <--help>
tasks-axi <mv> <--help>
tasks-axi <show> <old-task> <--file> </private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home/data/backlog.md>
tmux <kill-window> <-t> <=firstmate:=fm-old-task>
tasks-axi <done> <old-task> <--pr> <https://example.invalid/owner/repo/pull/85> <--file> </private/var/folders/zw/ryfg7cv539v2ht9m08vg55pm0000gn/T/fm-teardown-endpoint-safety.B6xhIX/shared-slot-record-only/home/data/backlog.md>
tmux <list-windows> <-t> <=firstmate> <-F> <#{window_name}>
tmux <list-windows> <-t> <=firstmate> <-F> <#{window_name}>
tmux <list-windows> <-t> <firstmate> <-F> <#{window_name}>
treehouse return: NOT CALLED

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 3 issues found → auto-fixed (2) ✅
  • 🚨 bin/fm-teardown.sh:471 - This contradicts the required constraint that record-only retirement “must not read, return, prune, reset, or run git inside the occupied slot.” For a foreign claim, this changed path calls fm_treehouse_pool_slot, which executes git -C &#34;$slot&#34; rev-parse in bin/fm-wake-lib.sh:1583; teardown_live_slot_path repeats the same check during ownership and landing proof. Move pool identity verification to the earliest shared boundary using only the separate project checkout and pool/claim metadata, without entering or invoking git in the occupied slot.
  • ⚠️ tests/fm-teardown-endpoint-safety.test.sh:1249 - The malformed-claim and unverifiable-pool test functions are each defined twice, and neither function is invoked by the test runner at the bottom of the file. Consequently the safety paths added by commits 06be872 and 2d774cc are never exercised. Keep one definition of each and invoke both alongside the record-only regression test.
  • ⚠️ docs/scripts.md:156 - This change adds entries for bin/fm-task-proc-lib.sh and bin/fm-host-janitor.sh, but neither file exists in the target tree and neither relates to record-only teardown. Remove these two unrelated entries so the script inventory does not advertise unavailable commands.

🔧 Fix: Avoid occupied-slot access during record-only teardown
1 warning still open:

  • ⚠️ bin/fm-teardown.sh:2669 - Record-only teardown always queries PR state with gh pr view, so a landed task with a recorded Forgejo PR URL is refused. The normal landing path at lines 1686-1692 already handles Forgejo through fm_pr_url_parse and fm_pr_forgejo_read_record. Reuse or extract that provider-aware proof while keeping Git operations scoped to project_path.

🔧 Fix: Support Forgejo proof for record-only teardown
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-teardown-endpoint-safety.test.sh
  • Executed the focused test_shared_slot_record_only_requires_landed_branch fixture against bin/fm-teardown.sh old-task and captured CLI output, persisted records, owner claim, occupied-slot access log, and lifecycle calls.
  • git status --short confirmed testing left no transient worktree files.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@ichts

ichts commented Oct 21, 2026

Copy link
Copy Markdown
Author

This narrow replacement addresses the scope and live-evidence requests on closed PR #7586 and maintainer comment #7586 (comment) . It excludes the host-janitor and default process-reaping changes. Validation uses a real linked Git worktree in an isolated fixture, not the live translateimage pool; running teardown against that pool would alter another home and has not been authorized.

@ichts

ichts commented Oct 22, 2026

Copy link
Copy Markdown
Author

Opened against the wrong repository by mistake; moving to our fork. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant