Skip to content

feat(fleet): implement durable fleet refill and attempt terminal lifecycle - #2006

Closed
Authentis wants to merge 59 commits into
kunchenguid:mainfrom
Authentis:fm/fm-fleet-refill-implementation-r1
Closed

feat(fleet): implement durable fleet refill and attempt terminal lifecycle#2006
Authentis wants to merge 59 commits into
kunchenguid:mainfrom
Authentis:fm/fm-fleet-refill-implementation-r1

Conversation

@Authentis

Copy link
Copy Markdown

Intent

Implement the accepted and reviewed 16-task plan at docs/superpowers/plans/2026-08-08-fleet-refill-terminal-lifecycle.md end to end on this single delivery branch. Preserve Task 0's explicit reconciliation merge of the original local main and origin/main histories, prove both original lines remain ancestors, retain the full green baseline and single canonical review.sh verdict evidence, and preserve the dependency-ordered, test-driven, small-green-commit implementation of Tasks 1 through 15. The accepted design in docs/architecture.md remains authoritative, and Decision OS beads remain the sole authority for work identity, priority, readiness, dependencies, claims, ownership, and closure. Apply firstmate-coding-guidelines: one owner per contract, valid documentation audiences, one sentence per Markdown line, shellcheck-clean scripts, behavioral tests rather than source-byte assertions, complete documentation review, and compatibility coverage across every affected supported worker runtime and provider.

Do not introduce a scheduler, daemon, dashboard, wrapper, parser, mutable phase model, duplicate capacity counter, legacy-arithmetic fallback, or second task authority. Automatic refill must remain inactive until fixture parity, live parity, latency, full tests, lint, documentation, and safety gates pass. Private-home operations involving data/fleet-depth-check.sh, crontab, config/refill-sentinel, or config/refill-auto must not modify anything outside this isolated delivery flow; keep refill-sentinel/refill-auto private activation prohibited until this PR is explicitly approved and merged, while preserving truthful read-only evidence of already-verified private state. Never delete, overwrite, reset, stash, rebase away, replace, or discard unknown or unlanded branches, copies, refs, stashes, attempt records, tracker evidence, or any pipeline fix commit. Final acceptance requires every plan checkbox and file-map check to have evidence, all tests and canonical lint green, documentation audiences valid, both original histories preserved, the fleet-depth legacy path removed only after proven cutover, rollback alert-only, and no unresolved decision or cleanup obligation.

Preserve and validate all captain-approved review corrections already committed through 8bd867a: distinct claim and closure receipt effects; normal lifecycle launch and exact landing receipts; complete terminal disposition classification for open/closed/merged PRs and authorized local-only merges using worker, endpoint, Git, forge, and exact-content evidence; propagation of the authoritative pre-land actual-diff refusal; tracker preflight before mutation plus recovery of already-applied transitions; authoritative endpoint stop and confirmed absence; verified preservation refs before destructive cleanup; effect-by-effect forward cleanup recovery; preflight and provider-owned return support for every supported runtime/provider; effective FM_HOME and home-local context in capacity deadline children; installed array-shaped bead JSON normalization; bounded refill waves and propagated claim/launch failures; full admission checks for paths, exclusive seams, shared mutable state, semantic dependencies, lane contracts, and same-wave conflicts; migration binding only after live reconciliation with no synthetic effects; terminal and refill-sentinel recovery wired through existing heartbeat/completion/session owners without a new scheduler; truthful sentinel-removal verification; shared canonical repository identity normalization for observed paths and frozen remote URLs while preserving local-only exact proof, with remote-backed regression coverage; terminal replay after runtime-record removal may bypass the pre-land diff gate only when the exact cleanup.endpoint receipt proves the endpoint stopped; and spawn resume may recover a missing launch receipt only when the duplicate guard verifies the exact recorded endpoint identity is live. Keep Claude disabled and use the configured non-Claude [pi, codex] routing. Deliver one reviewed PR containing the reconciled history, implementation, and all pipeline-applied fixes.

What Changed

  • Implement the write-once, receipt-derived attempt model: new bin/fm-attempt-lib.sh (immutable fm-attempt.v1 envelopes, effect receipts, append-only observation journal, obligations derived from missing receipts), the single shared capacity projection bin/fm-capacity-lib.sh (fm-fleet-capacity.v1 over structured fm-crew-state.sh --json), the centralized live disposition reader bin/fm-disposition-lib.sh, and a read-only legacy migration helper bin/fm-attempt-migrate.sh.
  • Add the attempt-to-terminal and refill machinery: bin/fm-terminal.sh (sole terminal orchestrator composing structured per-effect cleanup from bin/fm-cleanup-lib.sh and tracker receipts via the attended Decision OS steward adapter bin/fm-br-receipt.sh, inside one outer non-reentrant attempt lock), the --refill admission action in bin/fm-fleet-refill.sh with full admission checks and the private consumer-only bin/fm-refill-sentinel.sh, claim-before-allocation and attempt-bound provider ownership in bin/fm-spawn.sh/bin/fm-home-seed.sh/bin/fm-backend.sh, and provisional forge observations plus the write-once final landing receipt in the PR delivery scripts; automatic refill stays gated (alert-only without config/refill-auto or FM_REFILL_AUTO=1).
  • Cut consumers over to the shared projection after fixture/live parity and remove legacy machinery: the human refill verdict and fleet snapshot now derive from fm-fleet-capacity.v1, the legacy owned-manifest/output-mtime arithmetic is deleted with alert-only rollback, attempt reconciliation is surfaced through the existing heartbeat and session-start owners without a new scheduler, and the branch lands ~20 new behavioral test files, the reviewed 16-task plan and design documentation, the docs/verification/fleet-capacity.md acceptance record, the reconcile commit merging local main and origin/main with both histories preserved, and the persistent Calm footer indicator in .pi/extensions/fm-calm.ts.

Risk Assessment

✅ Low: All three authorized corrections are implemented exactly as instructed with discriminating behavioral tests and verified invariants (automatic refill still fails closed, automatic terminal reconciliation still retains the quiet gate, terminal success preserved on refill refusal); no new material defect or intent contradiction was found in the fix round.

Testing

Round-1 auto-fixes (orca claim-effect name, calm getMarkdownTransformers stub) were re-verified passing on the final HEAD ebcf659: fm-backend-orca 53/53 and the calm renderer/lifecycle test including the branch's new '● calm on / ○ calm off' footer-status assertions. All 26 targeted fleet-refill/terminal/documentation suites passed. The branch's real-Pi TUI E2E coverage was exercised individually for the first time (the pre-existing followup flake aborts the full script): hidden-block geometry and working-ship geometry pass; test_interactive_terminal_e2e fails consistently in this sandbox ('/export did not complete while calm mode was on') and fails identically on a base 833a9a2 snapshot, confirming pre-existing Pi 0.84.1 environment drift rather than a branch regression - reported as an informational warning. The end-to-end CLI transcript was rebuilt on the final HEAD against the real binaries and demonstrates the complete user-visible lifecycle: shared fm-fleet-capacity.v1 projection, alert-only sentinel verdict, in-flight terminal refusal that preserves ownership with zero destructive effects, and the full landed terminal run (pre-land diff gate, attended Decision OS steward bead closure, per-effect structured cleanup, retirement, fresh post-terminal projection, and the real refill consumer's 'fleet-ok: alert-only' verdict), ending with the complete receipt chain claim/provider/launch/landing/tracker/cleanup.*/retirement. Worktree left clean; evidence in /tmp/no-mistakes-evidence/01KZJRV1Y3DBPJEDXAJBAQVRER/.

Evidence: End-to-end fleet-refill + terminal-lifecycle CLI transcript (real binaries, HEAD ebcf659)
==================================================================
FLEET REFILL + ATTEMPT TERMINAL LIFECYCLE - end-to-end CLI transcript
real binaries: bin/fm-fleet-refill.sh, bin/fm-refill-sentinel.sh, bin/fm-terminal.sh
external owners faked hermetically (gh, br, treehouse, tmux, steward, crew-state)
branch: fm/fm-fleet-refill-implementation-r1  HEAD ebcf659
==================================================================

DEMO_HOME=/tmp/fm-e2e-demo.yD6LCo/home
AID_X=dos-x-a1 AID_Y=dos-y-a1

===== 1) bin/fm-fleet-refill.sh --count-json (shared capacity object) =====
{
  "schema": "fm-fleet-capacity.v1",
  "generated": "2026-08-09T09:20:18Z",
  "home_id": "/tmp/fm-e2e-demo.yD6LCo/home",
  "observation_complete": true,
  "total_timeout": false,
  "schema_ok": true,
  "alert_only": false,
  "reconciliation_required": false,
  "rows": [
    {
      "attempt_id": "dos-x-a1",
      "task_key": "dos-x",
      "generation": 1,
      "kind": "ship",
      "classification": "active",
      "source": "fake",
      "productive": true,
      "reserved": true,
      "ambiguity_reasons": [
        "missing_receipt:landing"
      ],
      "missing_receipts": [
        "landing"
      ],
      "reconciliation_required": false
    },
    {
      "attempt_id": "dos-y-a1",
      "task_key": "run-dos-y-a1",
      "generation": 1,
      "kind": "ship",
      "classification": "active",
      "source": "fake",
      "productive": true,
      "reserved": true,
      "ambiguity_reasons": [
        "missing_receipt:landing"
      ],
      "missing_receipts": [
        "landing"
      ],
      "reconciliation_required": false
    }
  ],
  "aggregate": {
    "productive_count": 2,
    "reserved_ownership_count": 2,
    "ambiguous_count": 2,
    "observation_complete": true,
    "alert_only": false,
    "reconciliation_required": false,
    "refill_safe": true
  }
}

===== 2) bin/fm-refill-sentinel.sh (alert-only consumer verdict) =====
sentinel: consumed aggregate cadence=600 mode=alert-only refill_safe=true productive=2 reserved=2 alert_only=false reconciliation=false
REFILL-ALERT: productive 2 below target 6
sentinel exit=1 (nonzero means an alert was raised, never a dispatch)
sentinel log:
2026-08-09T09:20:18Z sentinel: cadence=600 mode=alert-only REFILL-ALERT: productive 2 below target 6 candidates=1

===== 3) bin/fm-terminal.sh <in-flight attempt> (refusal before any effect) =====
terminal: delivery state 'unknown' for dos-x-a1; ownership preserved for reconciliation
terminal exit=1 (expect nonzero: not terminal, no destructive effect)
{"receipts":["claim","launch","provider"]}
attempt dos-x-a1 still present and NOT retired (ownership preserved)

===== 4) bin/fm-terminal.sh <landed attempt> (full receipt-derived lifecycle) =====
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  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 /home/holu/.no-mistakes/worktrees/8cb5feef5404/01KZJRV1Y3DBPJEDXAJBAQVRER/.pi/extensions/fm-primary-turnend-guard.ts -e /home/holu/.no-mistakes/worktrees/8cb5feef5404/01KZJRV1Y3DBPJEDXAJBAQVRER/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
diff base: origin/main
no changes vs origin/main
tracker_receipt: dos-y-a1 close dos-y closed
cleanup: dos-y-a1 disposition=landed complete
terminal: dos-y-a1 disposition=landed retired
post-terminal capacity: productive=1 reserved=1 refill_safe=true
refill: admission productive=1 reserved=1 candidates=0 admitted=0 serialized=0
fleet-ok: alert-only
terminal exit=0 (expect zero: landed attempt retired)

attempt record after terminal (dos-y-a1):
{"receipts":["claim","provider","launch","landing","tracker","cleanup.endpoint","cleanup.branch","cleanup.provider","cleanup.runtime","retirement"]}
===== 5) post-terminal shared projection (fresh observation) =====
{"productive_count":1,"reserved_ownership_count":1,"ambiguous_count":1,"observation_complete":true,"alert_only":false,"reconciliation_required":false,"refill_safe":true}

===== 6) final attempt record (dos-y-a1) =====
{
  "envelope": {
    "task_source": "pi",
    "task_key": "dos-y",
    "home_id": "holu",
    "attempt_id": "dos-y-a1",
    "generation": 1
  },
  "receipts": {
    "claim": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:17Z",
        "evidence": {
          "bead": "dos-y",
          "status": "claimed",
          "agent": "pi-primary"
        }
      }
    ],
    "provider": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:18Z",
        "evidence": {
          "provider": "tmux",
          "copy": "/tmp/fm-e2e-demo.yD6LCo/wt-y"
        }
      }
    ],
    "launch": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:18Z",
        "evidence": {
          "endpoint": "w-y"
        }
      }
    ],
    "landing": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:19Z",
        "evidence": {
          "disposition": "landed",
          "reason": "merged-exact-pr-head",
          "attempt_id": "dos-y-a1",
          "generation": 1,
          "evidence": {
            "kind": "github-pr",
            "observation": {
              "provider": "github",
              "repo": "kunchenguid/firstmate",
              "source": "dos-y",
              "target": null,
              "head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
              "state": "merged",
              "before_sha": null,
              "after_sha": null,
              "pr": "https://github.com/kunchenguid/firstmate/pull/9"
            },
            "forge": {
              "state": "MERGED",
              "headRefOid": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
              "baseRefName": "main"
            },
            "copy_head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
            "target_head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e"
          },
          "facts": {
            "bead": {
              "id": "dos-y",
              "status": "open",
              "owner": ""
            },
            "worker": {
              "schema": "fm-crew-state.v1",
              "id": "run-dos-y-a1",
              "state": "working",
              "source": "fake",
              "detail": "working"
            },
            "endpoint": {
              "backend": "tmux",
              "id": "s:fm-run-dos-y-a1",
              "state": "missing",
              "copy": "/tmp/fm-e2e-demo.yD6LCo/wt-y",
              "task_id": "run-dos-y-a1"
            }
          }
        }
      }
    ],
    "tracker": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:19Z",
        "evidence": {
          "bead": "dos-y",
          "status": "closed"
        }
      }
    ],
    "cleanup.endpoint": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:20Z",
        "evidence": {
          "backend": "tmux",
          "endpoint": "s:fm-run-dos-y-a1",
          "task_id": "run-dos-y-a1",
          "copy": "/tmp/fm-e2e-demo.yD6LCo/wt-y",
          "confirmed_gone": true
        }
      }
    ],
    "cleanup.branch": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:20Z",
        "evidence": {
          "attempt": "dos-y-a1",
          "branch": "main",
          "deleted": true
        }
      }
    ],
    "cleanup.provider": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:20Z",
        "evidence": {
          "provider": "tmux",
          "returned": true,
          "copy": "/tmp/fm-e2e-demo.yD6LCo/wt-y"
        }
      }
    ],
    "cleanup.runtime": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:20Z",
        "evidence": {
          "records_removed": true,
          "task_id": "run-dos-y-a1",
          "confirmed_absent": true
        }
      }
    ],
    "retirement": [
      {
        "seq": 1,
        "state": "observed",
        "generation": 1,
        "observed_at": "2026-08-09T09:20:20Z",
        "evidence": {
          "audit": "terminal",
          "disposition": "landed",
          "landing": {
            "disposition": "landed",
            "reason": "merged-exact-pr-head",
            "attempt_id": "dos-y-a1",
            "generation": 1,
            "evidence": {
              "kind": "github-pr",
              "observation": {
                "provider": "github",
                "repo": "kunchenguid/firstmate",
                "source": "dos-y",
                "target": null,
                "head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
                "state": "merged",
                "before_sha": null,
                "after_sha": null,
                "pr": "https://github.com/kunchenguid/firstmate/pull/9"
              },
              "forge": {
                "state": "MERGED",
                "headRefOid": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
                "baseRefName": "main"
              },
              "copy_head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e",
              "target_head": "d1a11b245396bc3c79dcf2c7f5178f1c834c2c2e"
            },
            "facts": {
              "bead": {
                "id": "dos-y",
                "status": "open",
                "owner": ""
              },
              "worker": {
                "schema": "fm-crew-state.v1",
                "id": "run-dos-y-a1",
                "state": "working",
                "source": "fake",
                "detail": "working"
              },
              "endpoint": {
                "backend": "tmux",
                "id": "s:fm-run-dos-y-a1",
                "state": "missing",
                "copy": "/tmp/fm-e2e-demo.yD6LCo/wt-y",
                "task_id": "run-dos-y-a1"
              }
            }
          },
          "authority": "captain:merge"
        }
      }
    ]
  }
}
Evidence: Reproducible E2E demo harness (real bin/ binaries, hermetic external owners)
#!/usr/bin/env bash
# End-to-end demo of the fleet-refill + attempt-terminal lifecycle using the
# REAL bin/ binaries with only external owners (gh, br, treehouse, tmux, the
# attended Decision OS steward, crew-state) faked hermetically, exactly as the
# repo's own test suites do. Drives:
#   bin/fm-fleet-refill.sh --count-json   (shared fm-fleet-capacity.v1 object)
#   bin/fm-refill-sentinel.sh             (alert-only consumer verdict, Task 15)
#   bin/fm-terminal.sh <in-flight>        (refusal preserving ownership)
#   bin/fm-terminal.sh <landed>           (full receipt-derived terminal flow)
# Produces a reviewer-visible CLI transcript.
set -u
ROOT=/home/holu/.no-mistakes/worktrees/8cb5feef5404/01KZJRV1Y3DBPJEDXAJBAQVRER
DEMO=$(mktemp -d /tmp/fm-e2e-demo.XXXXXX)
STATE="$DEMO/home/state"
PROJECT="$DEMO/project"
FAKEBIN="$DEMO/fakebin"
mkdir -p "$STATE" "$FAKEBIN" "$PROJECT/.beads" "$DEMO/home/config"

export ROOT=$ROOT                            # fake steward sources fm-attempt-lib.sh from it
# shellcheck disable=SC2155
export FM_GATE_REFUSE_BYPASS=1          # real guard would refuse a gate worktree
export FM_STATE_OVERRIDE="$STATE"
export FM_REFILL_PROJECT="$PROJECT"
export FM_HOME="$DEMO/home"
export PATH="$FAKEBIN:$PATH"
export FM_TERMINAL_QUIET_SECS=0
export FM_AUTHORITY_FILE="$STATE/authority-current.json"
export FM_REFILL_TARGET_PRODUCTIVE=6
export FM_REFILL_RESERVED_CEILING=10
export FM_REFILL_SENTINEL_LOG="$DEMO/home/state/refill-sentinel.log"

# open Decision OS beads graph (the sole work-identity authority)
printf '%s\n' '{"id":"dos-x","status":"open"}' > "$PROJECT/.beads/issues.jsonl"
printf '%s\n' '{"id":"dos-y","status":"open"}' >> "$PROJECT/.beads/issues.jsonl"

# --- fake external owners ---------------------------------------------------

# gh: forge reports the PR merged -> fm_disposition_live classifies "landed"
cat > "$FAKEBIN/gh" <<'SH'
#!/usr/bin/env bash
head=$(git -C "$FM_E2E_COPY" rev-parse HEAD 2>/dev/null || echo deadbeef)
printf '%s\n' "{\"state\":\"${FM_E2E_PR_STATE:-MERGED}\",\"headRefOid\":\"$head\",\"baseRefName\":\"main\"}"
SH
chmod +x "$FAKEBIN/gh"

# br: live bead reads (show/ready/list) answer the real br array shape
cat > "$FAKEBIN/br" <<'SH'
#!/usr/bin/env bash
if [ "${1:-}" = show ]; then
  id="${3:-}"
  [ -n "$id" ] || id="${2:-}"
  if [ -e "$FM_STATE_OVERRIDE/tracker-closed" ]; then status=closed; else status=open; fi
  printf '[{"id":"%s","status":"%s","priority":1,"claimed_by":"","blocked_by":[]}]\n' "$id" "$status"
elif [ "${1:-}" = ready ]; then
  printf '[{"id":"dos-y","status":"open","priority":1}]\n'
elif [ "${1:-}" = list ]; then
  printf '{"total":2}\n'
else
  exit 1
fi
SH
chmod +x "$FAKEBIN/br"

# treehouse: provider return (the real binary refuses copies it does not manage)
cat > "$FAKEBIN/treehouse" <<'SH'
#!/usr/bin/env bash
[ "${1:-}" = return ] || exit 1
copy=${3:-}
rm -rf -- "$copy"
SH
chmod +x "$FAKEBIN/treehouse"

cat > "$FAKEBIN/tmux" <<'SH'
#!/usr/bin/env bash
case "${1:-}" in
  kill-window) exit 0 ;;
  list-windows) exit 0 ;;
  *) exit 1 ;;
esac
SH
chmod +x "$FAKEBIN/tmux"

# attended Decision OS steward: observes the tracker closure receipt through
# the lock-held primitive, exactly like the real fm-br-receipt.sh handshake
cat > "$FAKEBIN/fm-br-receipt.sh" <<'SH'
#!/usr/bin/env bash
set -u
. "$ROOT/bin/fm-attempt-lib.sh"
REQ=$(cat "$1")
attempt=$(echo "$REQ" | jq -r '.attempt_id')
gen=$(echo "$REQ" | jq -r '.generation')
bead=$(echo "$REQ" | jq -r '.bead_id')
fm_attempt_effect_observe_held "$attempt" "$gen" tracker \
  "$(jq -n --arg bead "$bead" '{bead:$bead,status:"closed"}')" || exit 1
: > "$FM_STATE_OVERRIDE/tracker-closed"
echo "tracker_receipt: $attempt close $bead closed"
SH
chmod +x "$FAKEBIN/fm-br-receipt.sh"
export FM_BR_RECEIPT_BIN="$FAKEBIN/fm-br-receipt.sh"

# crew-state: structured worker liveness for the capacity projection and the
# terminal's worker evidence
cat > "$FAKEBIN/fm-crew-state.sh" <<'SH'
#!/usr/bin/env bash
id="${!#}"
jq -nc --arg id "$id" '{schema:"fm-crew-state.v1",id:$id,state:"working",source:"fake",detail:"working"}'
SH
chmod +x "$FAKEBIN/fm-crew-state.sh"
export FM_CREW_STATE_BIN="$FAKEBIN/fm-crew-state.sh"

# --- fixtures ---------------------------------------------------------------

. "$ROOT/bin/fm-attempt-lib.sh"

ensure_copy() {  # <dir>: real git copy with origin/main, so the pre-land diff
  local dir=$1
  mkdir -p "$dir"
  if [ ! -d "$dir/.git" ]; then
    git -C "$dir" init -q -b main .
    git -C "$dir" -c user.name=t -c user.email=t@t commit -q --allow-empty -m init
  fi
  git -C "$dir" checkout -q -B main 2>/dev/null || true
  if ! git -C "$dir" remote get-url origin >/dev/null 2>&1; then
    git init -q --bare "$DEMO/remote.git"
    git -C "$dir" remote add origin "$DEMO/remote.git"
  fi
  git -C "$dir" push -q --force origin HEAD:main
  git -C "$dir" fetch -q origin main
}

# task-x: in-flight attempt (claimed, allocated, launched; no landing yet)
AID_X=$(fm_attempt_alloc pi dos-x holu)
GEN_X=$(fm_attempt_generation "$AID_X")
fm_attempt_effect_observe "$AID_X" "$GEN_X" claim '{"bead":"dos-x","status":"claimed","agent":"pi-primary"}'
fm_attempt_freeze_allocation "$AID_X" "$GEN_X" "{\"provider\":\"tmux\",\"copy\":\"$DEMO/wt-x\"}" \
  '{"mode":"direct-PR","base":"main","target":"origin/main","repo_identity":"https://github.com/kunchenguid/firstmate.git","planned_path":"docs/"}'
fm_attempt_effect_observe "$AID_X" "$GEN_X" launch '{"endpoint":"w-x"}'
printf 'kind=ship\nmode=direct-PR\nattempt=%s\nworktree=%s\nproject=%s\n' "$AID_X" "$DEMO/wt-x" "$DEMO/wt-x" > "$STATE/dos-x.meta"

# task-y: landed attempt (claim, allocation, launch, exact forge journal,
# endpoint meta, fresh closure authority)
AID_Y=$(fm_attempt_alloc pi dos-y holu)
GEN_Y=$(fm_attempt_generation "$AID_Y")
fm_attempt_effect_observe "$AID_Y" "$GEN_Y" claim '{"bead":"dos-y","status":"claimed","agent":"pi-primary"}'
fm_attempt_freeze_allocation "$AID_Y" "$GEN_Y" "{\"provider\":\"tmux\",\"copy\":\"$DEMO/wt-y\"}" \
  '{"mode":"direct-PR","base":"main","target":"origin/main","repo_identity":"https://github.com/kunchenguid/firstmate.git","planned_path":"docs/"}'
fm_attempt_effect_observe "$AID_Y" "$GEN_Y" launch '{"endpoint":"w-y"}'
ensure_copy "$DEMO/wt-y"
export FM_E2E_COPY="$DEMO/wt-y"
HEAD_Y=$(git -C "$DEMO/wt-y" rev-parse HEAD)
fm_attempt_observe "$AID_Y" "$GEN_Y" forge "$(jq -nc --arg head "$HEAD_Y" \
  '{provider:"github",repo:"kunchenguid/firstmate",source:"dos-y",target:null,head:$head,state:"merged",before_sha:null,after_sha:null,pr:"https://github.com/kunchenguid/firstmate/pull/9"}')"
printf 'window=s:fm-run-%s\nendpoint_task_id=run-%s\nworktree=%s\nproject=%s\nkind=ship\nmode=direct-PR\nattempt=%s\n' \
  "$AID_Y" "$AID_Y" "$DEMO/wt-y" "$DEMO/wt-y" "$AID_Y" > "$STATE/run-$AID_Y.meta"
printf '%s\n' "{\"transition\":\"close\",\"task_key\":\"dos-y\",\"attempt_id\":\"$AID_Y\",\"generation\":$GEN_Y,\"authority\":\"captain:merge\"}" > "$STATE/authority-current.json"

# hermetic admission inputs for the real --refill consumer (empty candidate set)
printf '[]\n' > "$DEMO/candidates.json"
printf '[]\n' > "$DEMO/current-evidence.json"
export FM_REFILL_CANDIDATES_FILE="$DEMO/candidates.json"
export FM_REFILL_CURRENT_EVIDENCE_FILE="$DEMO/current-evidence.json"

# --- transcript -------------------------------------------------------------
OUT="$DEMO/transcript.txt"
{
  echo "=================================================================="
  echo "FLEET REFILL + ATTEMPT TERMINAL LIFECYCLE - end-to-end CLI transcript"
  echo "real binaries: bin/fm-fleet-refill.sh, bin/fm-refill-sentinel.sh, bin/fm-terminal.sh"
  echo "external owners faked hermetically (gh, br, treehouse, tmux, steward, crew-state)"
  echo "branch: fm/fm-fleet-refill-implementation-r1  HEAD $(git -C "$ROOT" rev-parse --short HEAD)"
  echo "=================================================================="
  echo
  echo "DEMO_HOME=$DEMO/home"
  echo "AID_X=$AID_X AID_Y=$AID_Y"
  echo

  echo "===== 1) bin/fm-fleet-refill.sh --count-json (shared capacity object) ====="
  "$ROOT/bin/fm-fleet-refill.sh" --count-json
  echo

  echo "===== 2) bin/fm-refill-sentinel.sh (alert-only consumer verdict) ====="
  FM_REFILL_SENTINEL_VERBOSE=1 "$ROOT/bin/fm-refill-sentinel.sh"; rc=$?
  echo "sentinel exit=$rc (nonzero means an alert was raised, never a dispatch)"
  echo "sentinel log:"
  cat "$FM_REFILL_SENTINEL_LOG"
  echo

  echo "===== 3) bin/fm-terminal.sh <in-flight attempt> (refusal before any effect) ====="
  "$ROOT/bin/fm-terminal.sh" "$AID_X" 2>&1; rc=$?
  echo "terminal exit=$rc (expect nonzero: not terminal, no destructive effect)"
  jq -c '{receipts:(.receipts|keys)}' "$STATE/attempts/$AID_X.json"
  if FM_STATE_OVERRIDE="$STATE" bash -c '. "$0/bin/fm-attempt-lib.sh"; fm_attempt_is_retired "$1"' "$ROOT" "$AID_X"; then
    echo "attempt $AID_X IS retired (unexpected)"
  else
    echo "attempt $AID_X still present and NOT retired (ownership preserved)"
  fi
  echo

  echo "===== 4) bin/fm-terminal.sh <landed attempt> (full receipt-derived lifecycle) ====="
  "$ROOT/bin/fm-terminal.sh" "$AID_Y" 2>&1; rc=$?
  echo "terminal exit=$rc (expect zero: landed attempt retired)"
  echo
  echo "attempt record after terminal ($AID_Y):"
  jq -c '{receipts:[.receipts|to_entries[]|.key]}' "$STATE/attempts/$AID_Y.json"

  echo "===== 5) post-terminal shared projection (fresh observation) ====="
  "$ROOT/bin/fm-fleet-refill.sh" --count-json | jq -c '.aggregate'
  echo

  echo "===== 6) final attempt record ($AID_Y) ====="
  jq '{envelope,receipts}' "$STATE/attempts/$AID_Y.json"
} 2>&1 | tee "$OUT"

echo "TRANSCRIPT: $OUT"
Evidence: Consolidated test evidence summary (round 2)
Fleet refill + attempt terminal lifecycle - targeted test evidence (local test phase, round 2)

Branch: fm/fm-fleet-refill-implementation-r1  base 833a9a2..HEAD ebcf659 (includes round-1 auto-fixes)

AUTO-FIXED TESTS (round 1, committed as ebcf659) - re-verified on this round:
- tests/fm-backend-orca.test.sh   53/53 ok (fake claim steward now observes the "claim"
                                   receipt effect; post-spawn obligations = landing)
- tests/fm-calm-pi-extension.test.sh  getMarkdownTransformers() stub added; the branch's
                                   calm footer-status assertions run and pass in
                                   test_rendering_and_session_lifecycle

PASSING (all run in this sandbox on HEAD ebcf659):
- fm-attempt 11 ok, fm-capacity 17 ok, fm-disposition 8 ok, fm-br-receipt 7 ok,
  fm-cleanup 12 ok, fm-terminal 17 ok, fm-attempt-migrate 4 ok,
  fm-refill-admission 16 ok, fm-refill-sentinel 10 ok, fm-fleet-refill 25 ok,
  fm-crew-state 51 ok, fm-spawn-worktree-settle 12 ok, fm-brief 23 ok,
  fm-teardown-endpoint-safety 5 ok, fm-pr-merge 12 ok, fm-secondmate-safety 72 ok,
  fm-decision-os-review-migrate 8 ok, fm-spawn-decision-os-reviews 5 ok,
  fm-fleet-snapshot-view 17 ok, fm-gotmp 3 ok, fm-pr-check-security 40 ok,
  fm-session-start 44 ok, fm-watch-triage 49 ok, fm-gate-refuse 7 ok,
  fm-documentation-audiences 4 ok
- Real-Pi TUI E2E run individually (unblocked by the followup flake):
  test_hidden_block_geometry_e2e        ok
  test_working_ship_geometry_and_lifecycle  ok

PRE-EXISTING ENVIRONMENT ISSUES (fail identically on base 833a9a2; not regressions):
- test_operational_followup_turn_e2e: tmux pane-capture flake vs real Pi 0.84.1
  (round-1 finding calm-followup-e2e-pi-drift, unchanged).
- test_interactive_terminal_e2e: NEW observation this round - "/export did not
  complete while calm mode was on"; fails consistently (3 runs) on HEAD and
  identically on a base 833a9a2 snapshot; live-Pi TUI timing drift, untouched
  by this branch's calm footer-status change.
- tests/fm-teardown.test.sh: requires the real lsof binary, absent in this sandbox
  (round-1 finding teardown-lsof-missing, unchanged).

END-TO-END CLI DEMONSTRATION (product-level evidence, refreshed on HEAD ebcf659):
  /tmp/no-mistakes-evidence/01KZJRV1Y3DBPJEDXAJBAQVRER/fleet-terminal-e2e-cli-transcript.txt
  (rebuilt by fm-fleet-terminal-e2e-demo.sh, real bin/ binaries, hermetic fakes)
  1) fm-fleet-refill.sh --count-json -> fm-fleet-capacity.v1: two active rows
     (dos-x-a1, run-dos-y-a1) productive+reserved, missing receipt landing,
     aggregate refill_safe=true
  2) fm-refill-sentinel.sh -> mode=alert-only, REFILL-ALERT: productive 2 below
     target 6; exit 1 (alert, never a dispatch); read-only log line written
  3) fm-terminal.sh <in-flight attempt> -> "delivery state 'unknown' ...
     ownership preserved for reconciliation", exit 1, attempt not retired
     (receipts claim,launch,provider unchanged - no destructive effect)
  4) fm-terminal.sh <landed attempt> -> real fm-review-diff pre-land check
     ("no changes vs origin/main"), attended steward closes bead
     ("tracker_receipt: dos-y-a1 close dos-y closed"), structured cleanup
     ("cleanup: dos-y-a1 disposition=landed complete"), "terminal: ... retired",
     fresh post-terminal projection, real refill consumer verdict
     ("refill: admission productive=1 reserved=1 candidates=0 admitted=0
     serialized=0" / "fleet-ok: alert-only"), exit 0
  5) Final attempt record: receipts claim, provider, launch, landing, tracker,
     cleanup.endpoint, cleanup.branch, cleanup.provider, cleanup.runtime,
     retirement - the complete receipt-derived terminal lifecycle
  6) Automatic refill stays inactive: nothing in bin/ creates config/refill-auto
     (only reads it); sentinel and refill both report mode=alert-only without it

Worktree clean after testing (no committed or transient files added).
- Outcome: ⚠️ 1 warning across 2 runs (55m24s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⚠️ **Rebase** - 1 warning

Push main to origin, or rebase your branch onto origin/main, before gating.

🔧 **Review** - 5 issues found → auto-fixed ✅
  • ⚠️ bin/fm-terminal.sh:339 - Step 8 treats a nonzero fm-fleet-refill.sh --refill exit as a terminal failure (exit 1 after the attempt was already retired with all receipts observed). The refill can legitimately exit nonzero for environmental reasons unrelated to the attempt's outcome: REFILL-UNSAFE when any other row of the fresh projection is ambiguous, or fm_refill_query_candidates failing when br ready is unavailable in the decision-os clone. The stderr message 'post-retirement refill unavailable or refused' then propagates to the watch/session-start heartbeat callers as a reconciliation failure for an attempt that is already retired and therefore never retried (retired rows are not reconciliation-required), so the nonzero is both misleading and silent. Refill after retirement is a best-effort notification step; the exit code should reflect the terminal outcome (log the refusal, exit 0).
  • ⚠️ bin/fm-fleet-refill.sh:364 - In the documented Task 15 alert-only rollback mode, fm_refill_admit_and_dispatch runs the live candidates query (fm_refill_query_candidates, which requires br ready against the decision-os clone) and the lane-contract checker (line 366, invoked with no timeout) unconditionally BEFORE the automatic-gate check at line 409. On a host where br is missing or the clone is broken, --refill exits 1 without ever printing the header-promised fleet-ok: alert-only verdict (header lines 20-31), contradicting the rollback contract that rollback reports and never dispatches. The sentinel handles the identical absence safely (refill_candidates_json falls back to echo &#39;[]&#39;), and fm-serialization-debt.sh bounds the same checker at 90s while this call has no bound (a hung checker would wedge the terminal's step-8 refill, which the watcher runs synchronously). Recommend: in alert-only mode tolerate candidate-query failure (empty candidates, serialize) and bound the checker invocation, still printing the verdict.
  • ⚠️ bin/fm-cleanup-lib.sh:640 - The default 2h terminal-quiet gate (FM_TERMINAL_QUIET_SECS=7200, age measured from the launch receipt) also applies to the operator-initiated fm-teardown.sh &lt;id&gt; compatibility wrapper for attempt-bound tasks. A captain who tears down a task less than 2h after launch gets 'cleanup: refused: quiet interval immature' and a 1 exit, with only the undocumented FM_TERMINAL_QUIET_SECS=0 as an override; every retry within the window fails the same way. The quiet gate is deliberate for automated terminal reconciliation, but for an explicit operator teardown it turns a previously-immediate reclaim into a 2h refusal loop. Confirm that operator-initiated teardown of attempt-bound tasks is intended to inherit the quiet window (and if so, the refusal should name the override).
  • ℹ️ bin/fm-capacity-lib.sh:58 - The new projection and structured-cleanup paths rely on GNU-only tooling: find -printf (fm-capacity-lib.sh:58), xargs -P (fm-capacity-lib.sh:196), GNU timeout (fm_capacity_crew_state), date -d (fm-cleanup-lib.sh:460), and a /proc cwd readlink walk (fm-cleanup-lib.sh:437), whereas existing supervision code carries Darwin branches (e.g. stat -f/-c). On a non-Linux host the projection silently drops attempt rows (a short undercount window when the task meta is also gone) and the cleanup live-process scan silently reports 'no live processes' instead of refusing, so the preflight's fail-safe behavior differs by platform. Target host and CI are Linux, so this is informational.
  • ℹ️ bin/fm-backend.sh:881 - fm_backend_stop_receipt is defined and unit-tested (tests/fm-backend-orca.test.sh) but never called by the production cleanup path: fm-cleanup-lib.sh builds a richer inline endpoint receipt (backend/endpoint/task_id/copy/confirmed_gone plus orca_worktree_id/tasktmp). The helper is dead code whose evidence shape can drift from the real receipt contract; either wire it into the endpoint-receipt construction or drop it.

🔧 Fix: Fix refill exit, alert-only tolerance, teardown quiet bypass
✅ Re-checked - no issues remain.

⚠️ **Test** - 1 warning
  • 🚨 tests/fm-backend-orca.test.sh:1353 - New Task-7 test code in this branch could never pass: the fake attended steward (make_orca_claim_steward) observed the 'tracker' receipt effect, but fm-spawn.sh's claim handshake verifies .receipts.claim[] (and the real bin/fm-br-receipt.sh maps claim -> effect=claim), so every attempt-bound orca spawn refused with 'claim_pending: <id> (reconcile before allocation)'. Fixed by observing the 'claim' effect; also corrected the follow-on assertion (a successful spawn publishes the launch receipt, so the outstanding obligation is 'landing', not 'launch'; crash-after-allocation launch-obligation coverage already exists in tests/fm-spawn-worktree-settle.test.sh). Test now passes 53/53 across consecutive runs. Fix is applied in the working tree.
  • 🚨 tests/fm-calm-pi-extension.test.sh:830 - The InteractiveMode test stub (operationalMode) lacked getMarkdownTransformers(), which Pi >= 0.84's addMessageToChat requires ('TypeError: this.getMarkdownTransformers is not a function'); test_rendering_and_session_lifecycle died before reaching this branch's new calm footer-status assertions (statuses.get('firstmate-calm') === theme.fg('accent', '● calm on')). Added the stub method (returns [] — inert on older Pi, correct on 0.84.1); the renderer test now passes including the branch's footer assertions. Fix is applied in the working tree.
  • ⚠️ tests/fm-calm-pi-extension.test.sh:1561 - test_operational_followup_turn_e2e fails in this sandbox ('Pi follow-up loaded_off case rendered a duplicate captain answer') against the real Pi 0.84.1 TUI: the tmux pane-capture assertion is timing-sensitive with this Pi version (answer absent when the case runs alone, duplicated in the full suite). Verified pre-existing: fails identically on base 833a9a2, and this branch only changed calm footer-status assertions, not the follow-up path. Environment/upstream drift, not a branch regression; CI runners with a different Pi version may pass it.
  • ⚠️ tests/fm-teardown.test.sh:2182 - test_leaked_worktree_process_is_reaped fails in this sandbox because the real lsof binary is not installed (pids_with_cwd_under and the extraction are byte-identical to base, and the test fails identically on base 833a9a2 — environment issue, not a regression). With a faithful /proc-based lsof-equivalent prepended to PATH the entire fm-teardown suite passes, including the leaked-process reap, tasktmp reap, exec-changed reap, and the extraction-identity byte-for-byte test. GitHub CI runners ship lsof, so CI should be unaffected.
  • bin/fm-test-run.sh tests/fm-attempt.test.sh tests/fm-capacity.test.sh tests/fm-disposition.test.sh tests/fm-br-receipt.test.sh tests/fm-cleanup.test.sh (5/5 pass)
  • bin/fm-test-run.sh tests/fm-terminal.test.sh tests/fm-attempt-migrate.test.sh tests/fm-refill-admission.test.sh tests/fm-refill-sentinel.test.sh tests/fm-fleet-refill.test.sh (5/5 pass)
  • bin/fm-test-run.sh tests/fm-crew-state.test.sh tests/fm-spawn-worktree-settle.test.sh tests/fm-brief.test.sh tests/fm-teardown-endpoint-safety.test.sh tests/fm-pr-merge.test.sh tests/fm-secondmate-safety.test.sh tests/fm-decision-os-review-migrate.test.sh tests/fm-spawn-decision-os-reviews.test.sh tests/fm-fleet-snapshot-view.test.sh tests/fm-gotmp.test.sh (11/11 pass)
  • bin/fm-test-run.sh tests/fm-pr-check-security.test.sh tests/fm-session-start.test.sh tests/fm-watch-triage.test.sh (3/3 pass)
  • tests/fm-backend-orca.test.sh x2 after auto-fix (53/53 ok, exit 0 both runs)
  • tests/fm-calm-pi-extension.test.sh after auto-fix (renderer/lifecycle contract incl. new footer-status assertions pass)
  • PATH=/tmp/fm-fake-lsof-bin:$PATH tests/fm-teardown.test.sh (full suite pass incl. leaked-process-reap, extraction byte-for-byte)
  • Base-commit comparison on worktree at 833a9a2: tests/fm-teardown.test.sh and tests/fm-calm-pi-extension.test.sh fail identically (pre-existing environment issues, not regressions)
  • Manual E2E CLI demo /tmp/fm-e2e-demo.sh: fm-fleet-refill.sh --count-json emits fm-fleet-capacity.v1 (refill_safe=true), fm-refill-sentinel.sh emits alert-only REFILL-ALERT, fm-terminal.sh refuses in-flight attempt preserving ownership, fm-terminal.sh runs a landed attempt through tracker close -> cleanup -> retirement -> post-terminal capacity projection -> alert-only refill

🔧 Fix: Auto-fixed orca claim and calm stub tests pass; Pi E2E flake pre-existing
1 warning still open:

  • ⚠️ tests/fm-calm-pi-extension.test.sh:2821 - test_interactive_terminal_e2e fails in this sandbox with the real Pi 0.84.1 TUI: 'not ok - /export did not complete while calm mode was on'. Fails consistently (3 consecutive runs) on HEAD ebcf659 and fails identically on a base 833a9a2 snapshot, so it is live-Pi TUI timing/behavior drift in this environment, not a branch regression (the branch's calm change is the footer-status indicator, untouched by the /export path). Same category as the previously reported test_operational_followup_turn_e2e flake but a distinct test that had never been run (the followup flake aborts the script before it). CI runners with a different Pi version may pass it.
  • bash tests/fm-backend-orca.test.sh - 53/53 ok (round-1 claim-effect fix verified)
  • bash tests/fm-calm-pi-extension.test.sh - rendering/lifecycle test with branch's footer-status assertions passes; only pre-existing followup E2E flake fails
  • bash tests/fm-terminal.test.sh (17 ok), fm-fleet-refill.test.sh (25 ok), fm-refill-admission.test.sh (16 ok), fm-refill-sentinel.test.sh (10 ok), fm-capacity.test.sh (17 ok), fm-disposition.test.sh (8 ok), fm-br-receipt.test.sh (7 ok), fm-cleanup.test.sh (12 ok), fm-attempt.test.sh (11 ok), fm-attempt-migrate.test.sh (4 ok), fm-crew-state.test.sh (51 ok), fm-spawn-worktree-settle.test.sh (12 ok), fm-brief.test.sh (23 ok), fm-teardown-endpoint-safety.test.sh (5 ok), fm-pr-merge.test.sh (12 ok), fm-secondmate-safety.test.sh (72 ok), fm-decision-os-review-migrate.test.sh (8 ok), fm-spawn-decision-os-reviews.test.sh (5 ok), fm-fleet-snapshot-view.test.sh (17 ok), fm-gotmp.test.sh (3 ok), fm-pr-check-security.test.sh (40 ok), fm-session-start.test.sh (44 ok), fm-watch-triage.test.sh (49 ok), fm-gate-refuse.test.sh (7 ok), fm-documentation-audiences.test.sh (4 ok) - all pass
  • Real-Pi TUI E2E run individually via patched copies: test_hidden_block_geometry_e2e ok, test_working_ship_geometry_and_lifecycle ok, test_interactive_terminal_e2e fails '/export did not complete while calm mode was on' - re-verified identical on a git archive 833a9a2 base snapshot (pre-existing env drift)
  • End-to-end CLI demo (real bin/fm-fleet-refill.sh --count-json, bin/fm-refill-sentinel.sh, bin/fm-terminal.sh with hermetic gh/br/treehouse/tmux/steward/crew-state fakes): count-json emits fm-fleet-capacity.v1 (2 active rows, refill_safe=true); sentinel reports mode=alert-only REFILL-ALERT productive 2 < 6; terminal on in-flight attempt refuses 'ownership preserved for reconciliation' exit 1 with receipts claim,launch,provider unchanged; terminal on landed attempt runs diff gate -> steward tracker close -> structured cleanup -> retirement -> fresh projection -> real refill 'fleet-ok: alert-only', final receipts claim,provider,launch,landing,tracker,cleanup.endpoint,cleanup.branch,cleanup.provider,cleanup.runtime,retirement
  • Constraint check: rg confirms nothing in bin/ creates config/refill-auto (only reads it); automatic refill stays inactive, sentinel and refill both report alert-only
  • git status --short clean after testing; transient demo dirs and patched copies removed
⚠️ **Document** - 1 error
  • ⚠️ docs/superpowers/plans/2026-08-08-fleet-refill-terminal-lifecycle.md:1 - The plan is implemented end to end with evidence recorded in docs/verification/fleet-capacity.md (Task-by-task commits, tests, lint, doc-audience), but all 96 execution checkboxes remain '- [ ]' and none are ticked, so the plan's own tracking state claims the work was never done. Ticking is executing-plans bookkeeping tied to the final-acceptance evidence audit, so the docs phase did not mutate it; the executor should decide whether the acceptance pass ticks the boxes after verifying each step's evidence or leaves them as the plan record.
  • ℹ️ docs/verification/fleet-capacity.md:264 - The Final acceptance file-map section records the branch diff state at the d769ccc era (80 paths; non-map deviations = 34 Task-0 files + live-decision-os-contract.test.sh). This docs phase adds two further non-map diff paths (docs/scripts.md inventory rows for the 10 new scripts, docs/architecture.md design-framing fixes), and the later review commits already added more. The final Step 5 file-map re-check must record these as documented deviations before acceptance; the plan itself anticipates the follow-up docs commit modifying the design owner.

🔧 Fix: docs: tick plan checkboxes and record current-head evidence
1 error still open:

  • 🚨 bin/fm-lint.sh:1 - The canonical lint gate (pinned by .no-mistakes.yaml to bin/fm-lint.sh, ShellCheck 0.11.0) exits 1 at the current head d670a84 with eight findings in seven locations across six files: bin/fm-br-receipt.sh (SC1010 line 129, SC2015 line 217), bin/fm-fleet-refill.sh (SC2329 line 78, fm_refill_paths_overlap unused after the rework), bin/fm-capacity-lib.sh (SC1007 line 226), bin/fm-session-start.sh (SC1007 line 738), bin/fm-watch.sh (SC1007 line 689), and tests/fm-capacity.test.sh (SC2016 line 195). The same command was clean at the Task 15 head f2383b8 (rc 0) and the full canonical set was clean at the base 833a9a2 (rc 0); all eight findings were introduced by the review commit 4f6e780, so the user intent's mandatory 'all tests and canonical lint green' acceptance criterion is currently violated at HEAD. The documentation phase cannot fix shellcheck findings in executable code, so the downstream lint gate must resolve these before final acceptance; the exact finding list and the dated clean baselines are recorded in docs/verification/fleet-capacity.md (Current-head record).
🔧 **Lint** - 1 issue found → auto-fixed ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: Fix shellcheck findings across receipt refill and capacity scripts
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

Firstmate and others added 30 commits August 6, 2026 17:44
The /calm toggle and session_start previously cleared the footer status
(setStatus(key, undefined)), so active Calm was indistinguishable from no
Calm. The footer now always shows the current choice: '● calm on' (accent)
when active, '○ calm off' (dim) when not, set on every session start and
toggle, with a plain-string fallback when ui.theme.fg is unavailable.

Tests updated to require the indicator instead of the cleared status;
home-resolution + presentation suites pass (the follow-up e2e flake is
pre-existing, verified identical with these changes stashed).
The 5x 'wake the crew' recurrence root cause was a window bug (completed
agents counted active for 25 min), fixed in the sentinel (window 25->5).
This script is the lead-side mechanical obligation: run it as the FIRST
action of any turn; it prints DISPATCH-NEEDED + exits 1 when the
manifest-scoped battery is below threshold with open dispatchable work.
The exit code is the reminder, not memory.
…guid#1798)

* feat(session-start): order the startup digest for truncation safety and bound its bulk

The digest is delivered through a harness that truncates an oversized payload
from the tail, and it really has been truncated: a 70KB digest arrived as lines
1-435 of 578, cutting off eight lines before the live-task inventory. That
session took the helm without ever seeing which tasks were live or where their
endpoints were.

Three changes, one file's worth of composition:

- FLEET STATE is emitted before CONTEXT, so a truncated tail drops curated
  memory - stable session to session, already governed by a captain-set budget,
  recoverable with one targeted read - instead of live fleet identity. The
  LOCK/BOOTSTRAP/WAKE-QUEUE safety preamble keeps its order. The read-once
  contract moves out of the closing reminder into its own section ahead of both,
  and now names the condition that voids it: a stage the truncation banner
  reports as never emitted.

- Status-tail lines are capped per line, reusing the cut the wake digest's OPEN
  DECISIONS section already applies. An observed tail line ran 865 characters
  and nothing bounded it. The cut and its marker now live in one place,
  bin/fm-line-cap-lib.sh, so the two digests cannot drift apart; each task's
  full status log path is still printed beside its tail.

- The backlog listing is composed as a recovery input: done rows are never
  listed, every in-flight, held, and blocked row is shown in full with its hold
  and blocked-by metadata, and only the dispatchable-now listing is bounded -
  with an exact remainder count and the command that shows the rest.
  FM_SESSION_START_QUEUED_LIMIT (default 20) replaces
  FM_SESSION_START_BACKLOG_LIMIT, which bounded the whole listing
  indiscriminately and so could drop a held or blocked row.

Tests exercise the real digest output: section ordering with the preamble
pinned, the per-line cap and its marker, and the backlog composition including
the remainder counters on both the tasks-axi and manual paths.

* no-mistakes(document): Clarify digest source recovery comments
…kunchenguid#1842)

A captain decision opened by a keyed needs-decision:/blocked: status line
orphaned as permanently open whenever the answer kicked off work: the
worker's next event is working [key=<workstream>] in a different key
namespace, so no resolved [key=<decision>] ever landed and the OPEN
DECISIONS fold kept listing the answered decision forever.

Remove the writer-dependency at its source: the answering firstmate
already holds the decision key when it sends the answer, so fm-send's new
--resolve-key flag (repeatable) appends the closing resolved line to this
home's own state/<id>.status after the submit is confirmed. The close is
a local ledger append for crewmates, local secondmates, and remote
secondmates alike - a remote mate's escalations reach this ledger through
the parent-replies ingest, so only the answer message crosses the
transport.

Safety: each named key must currently be open per the authoritative
status_open_decisions fold or fm-send refuses before sending; a failed or
unconfirmed send never closes a key; an append failure after a delivered
answer exits nonzero with the manual close command so the decision
re-surfaces instead of silently vanishing; a send without the flag closes
nothing, and working:/done: still never clear a captain decision.

Complementary fixes: the wake-drain OPEN DECISIONS section prints the
answer-with-close command hint at the moment of use; brief scaffolds
separate resolved's two duties (keyed-phase end vs decision closure) and
state that a done:/working: line never closes a decision even when the
answer started that work, keeping worker self-close for blockers that
clear without a firstmate reply; AGENTS.md and docs/architecture.md carry
the one-line pointers to the fm-send contract.
…1836)

* feat(secondmate): seed a remote home from a supplied project origin

Remote seeding required a local projects/<name> clone purely to read
`git remote get-url origin` into the provisioning manifest, so setting up
a remote second mate forced disposable clones and no-mistakes inits in the
primary home for projects that home has no reason to hold.

Firstmate now resolves the origin itself and names it as <project>=<origin-url>.
The seed validates and transports what it is given, and the receiving host
re-validates it rather than trusting the sender; bin/fm-project-origin-lib.sh
is the single owner of which URLs are accepted, refusing executable remote-helper
transports, option-shaped values, and unusable spellings at both ends. A bare
<project> still reads an already-present clone's origin, so nothing that works
today has to change. Registry consistency is unchanged: an unregistered or
local-only project is still refused.

A remote seed therefore creates nothing in the primary home beyond the route,
the charter, and its launch record.

The lifecycle test now seeds a registered project the primary has never cloned
and asserts the primary project tree is byte-identical afterwards, alongside
refusals for a missing origin, an unsafe origin, a local-only project, and an
unregistered project.

* no-mistakes(review): Clarify project origin documentation ownership

* no-mistakes(document): Document supplied-origin remote seeding contract

* feat(secondmate): accept project origins from any host or forge

Firstmate is a shared template, so a project origin must be able to name any
host: GitHub Enterprise on a private domain, GitLab hosted or self-hosted,
Bitbucket, Gitea, Codeberg, sr.ht, a bare IP, an SSH config alias, or a plain
server nobody else has heard of. The validator already decided on structure
rather than on a forge allowlist, and this makes that guarantee explicit and
closes the two gaps that a host-agnostic rule exposed:

- a bracketed IPv6 literal in the scp-like form is now accepted, so a host
  reachable only by address is not excluded
- a "/../" traversal inside a local or file: origin is now refused, because
  that names a path on the cloning host's own filesystem

The library is the single owner of the accepted forms, and its header says
plainly that there is no host, domain, or forge allowlist and there must never
be one. The skill keeps its distinct agent-operating lines (the agent resolves
and supplies the origin; a remote seed creates nothing in the primary home
beyond the route, the charter, and its launch record) and points at the library
for URL acceptance and at the operator doc for the rest.

The lifecycle test now drives Bitbucket, a self-hosted enterprise domain, a
self-hosted GitLab over ssh with a port, and a bare scp-like custom host through
the real seed, manifest, transport, and remote provisioning path in one seed,
asserting each URL reaches git unchanged and each clone carries its own origin's
content. The unit matrix leads with non-GitHub hosts for the same reason.

* no-mistakes(review): Validate project origin authorities safely

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes
…guid#1851)

* fix(tests): copy the whole bin/ tree into the old-vs-new conformance shim

main went red on tests/fm-backend.test.sh's "fm-send --key: old vs new
exit code" assertion, which reads as an fm-send fail-closed regression from

build_old_bin enumerated by hand the sibling scripts it copied into the
synthetic pre-refactor tree. kunchenguid#1842 made bin/fm-send.sh source
bin/fm-line-cap-lib.sh (added by kunchenguid#1798) and the list never learned about it,
so the pinned old fm-send.sh aborted at `. "$SCRIPT_DIR/fm-line-cap-lib.sh"`
under set -eu and exited 1 before parsing a single argument, while the
current one delivered the key and exited 0. The parity check compared a
crashed process against a working one and reported a behavior divergence
that never happened - the more so because BASE_REF collapses to HEAD on
main, where both sides run byte-identical source and a genuine divergence is
impossible. fm-send's --key exit path is unchanged and its fail-closed
contract is intact.

Copy the tree whole instead of enumerating it. An enumerated list has to be
extended by hand every time an entrypoint gains a dependency and is the only
thing that knows; it has been patched a dozen times for exactly that. A
whole-tree copy has nothing to forget. Extracting a refactored entrypoint the
baseline does not have now fails loudly instead of writing an empty file.

Only old-vs-new parity covered that exit contract, and parity is near-vacuous
on main. Pin it directly: tests/fm-send-strict.test.sh drives delivery both
ways from one stub and asserts an undelivered key exits nonzero naming the
key, so swallowing that error fails the suite.

* no-mistakes(review): Materialize historical fixture dependencies from baseline

* no-mistakes(document): Clarify fm-send key regression scope
* fix(bin): mirror the whole remote secondmate status stream

A remote secondmate's reply channel required corr=<16hex> on every line and
failed the entire delta when one line lacked it, so the cursor could never
advance past that line and the channel wedged permanently.

The charter tells a secondmate to report its own progress phases and to raise
new decisions with no correlation token, because correlation only answers a
marked parent request. Those lines were therefore unrepresentable on the remote
channel, while a local secondmate writes them straight into the parent's status
file.

Treat the channel as what it is: a mirror of the mate's status stream. A remote
mate now presents the same status and decision model as a local one, so a newly
raised needs-decision reaches the parent's open-decision fold identically, and
correlation goes back to being a per-line property that settles a pending
request rather than a gate on the stream.

Only what crossing a machine boundary genuinely adds stays behind: cursor
continuity, confined document fetch and rewrite, at-most-once append, and
control-byte normalization that rewrites bytes without ever dropping a line.
Line framing and size bounding already belong to fm-remote-delta-read.sh. A
document the remote reader refuses is named in one escalation instead of
stalling the stream, while an unavailable transport still leaves the delta for
the existing retry.

* refactor(bin): give the remote reply stream one append owner

Every line entering the parent status stream - a mirrored line, the continuity
escalation, and the undelivered-document escalation - now goes through one
at-most-once append, so the idempotence a replayed generation depends on is
stated once instead of copied at three call sites.

* no-mistakes(review): Keep local document transfer failures retryable

* no-mistakes(review): Isolate reply headers and normalize payload bytes

* no-mistakes(review): Correct remote reply mirror contract wording

* no-mistakes(review): Update remote reply script catalog description

* no-mistakes(document): Document remote status-stream mirroring
…chenguid#1847)

fm_secondmate_parent_record_parse read the .fm-secondmate-parent record
with bash's read, which drops NUL bytes - and different bash generations
disagree on the result: 3.2 truncates the value at the NUL while 5.x
splices the surrounding bytes together. A NUL-bearing parent_home could
therefore resolve to a home the record's bytes never name contiguously,
and which home fm-teardown.sh's promised-public-reply resolution read
(registration, registry, relay state) - or whether that protection
engaged at all - depended on which interpreter ran the cleanup.
Reproduced end to end: the same NUL-bearing record cleaned up under bash
5.x by resolving the spliced-together registered parent, while bash 3.2
refused it as unresolved, and a literal truncated path refused under
both.

Reject any NUL byte in the record before field parsing, putting corrupt
records in the same fail-closed bucket as duplicate fields, malformed
local bindings, unsupported routes, and symlinked records. The
regression test drives the real bin/fm-teardown.sh over the proven
clean-cleanup fixture with a NUL spliced mid-path into the recorded
parent_home, so before the fix it reproduced the wrong-home cleanup and
now it must refuse with the explicit binding refusal.
…unchenguid#1853)

* docs(secondmate-provisioning): require record intake for an inherited domain

A new mate seeded for an existing or inherited domain previously pulled in
charter, inherited config, captain-shared preferences, project clones, and
queued backlog rows with zero instruction about the domain's shipped history,
so it assumed a greenfield domain. A live backlog keeps only the configured
recent Done entries, so an inherited queue structurally over-represents plans
and under-represents deliveries, and already-delivered work resurfaced as open.

Add a record-intake step to the creation/seed path: classify greenfield versus
existing or inherited, and for the latter reconcile every inherited plan
against origin/main plus the live deployment, take only genuinely open work
and still-live durable knowledge, never carry a plan row for shipped work, and
record what could not be reconciled. Greenfield domains are untouched.

The skill owns the procedure; the backlog handoff section carries a one-line
reinforcement at the point where plan rows actually move.

* no-mistakes(document): Clarify secondmate record-intake scope
…guid#1860)

* perf(session-start): run every network check off the blocking path

The session-start digest runs on a session-open hook that blocks session
initialization, and every external-network call it made was individually
unbounded: `gh auth status`, secondmate liveness, secondmate convergence,
pending remote handoff delivery, and the fleet-sync fetch. One unreachable
remote secondmate could consume the whole FM_SESSION_START_TIMEOUT and
truncate the digest, so a slow network could cost the work queue itself.
Measured against a host hanging 25s per SSH connection, that startup took
1m18s.

The digest is now composed from local reads alone. bin/fm-startup-network.sh
runs the same checks concurrently in a bounded detached worker and the digest
harvests whatever finished, without ever waiting. Same fixture: 0.84s.

Nothing is dropped. fm-bootstrap.sh stays the single owner of every sweep and
still runs all of them, through a new FM_BOOTSTRAP_NETWORK phase split whose
`skip` and `only` halves are a partition of the unsplit run. Deferral is safe
because the sweeps are idempotent detectors, the result is durable and always
surfaces (inline, or as a `check: startup-network` wake), and the worker
re-verifies that the fleet lock still names the session that asked before it
mutates anything. While the worker is still running the digest names exactly
what is unconfirmed rather than implying it passed.

A relaunch performed by the deferred pass is now always reported, because the
digest that printed the superseded endpoint record is already out.

Also collapses the duplicate tasks-axi compatibility probe: the verdict is
computed once and handed to the bootstrap child for one process hop, then
consumed so it never reaches a spawned agent's environment. 10 tasks-axi
invocations per startup become 7.

Verified on Claude Code 2.1.222 that a worker detached by the session-open
hook survives the hook returning, the one vendor behavior this design needs
and no portable test can see.

Re-landed on current main, superseding PR kunchenguid#1845, which was cut from a
pre-kunchenguid#1842 base. The digest's section numbering in AGENTS.md section 3 now
states the emission order directly - supervision block and its read-once
contract, fleet state, network checks, then context - which keeps kunchenguid#1826's
fleet-state-before-context ordering. The old-bin test shim keeps main's
git-archive baseline from kunchenguid#1851, which already subsumes this branch's reason
for widening that shim.

* docs(verification): re-measure the deferred startup stage on the current base

Re-runs the unreachable-remote latency fixture against default-branch tip
8398d31 rather than the now-historical 345de4e, and records the sweep-result
comparison the deferral's safety argument rests on: the deferred worker's
published report is byte-identical to the three sweep lines the blocking
baseline printed, with the unreachable route preserved in both.

* no-mistakes(review): Fail deferred startup when report publication fails

* no-mistakes(document): Document deferred startup network behavior accurately
* fix(procevent): apply a captured adapter result in code, not by instruction

A remote secondmate's reply was captured and announced, but never applied.
Nothing dispatched the reply adapter's `handle` on a `procevent remote-reply`
wake, and the handling instruction named only the generic acknowledgement, so
the wake was retired while everything it carried was dropped: the reply never
reached the secondmate's local status mirror, the request it answered kept
escalating as a missed report, and the relay - whose registration each capture
retires, and which only that same handling re-arms - was left dead until the
next session start armed it again.

Applying such a result carries no judgement, so it belongs in code. After
publishing, the runner now calls
`bin/fm-procevent-<adapter>.sh autohandle <source-id> <sequence> <result-file>`
and lets the adapter apply and acknowledge its own result, through the same kind
of seam that already owns the terminal verdict. It runs strictly after terminal
retirement, because a handling adapter re-arms its own next source and retiring
afterwards would drop that fresh registration. An adapter with no such command,
or one whose pass does not complete, leaves the result unacknowledged and
therefore still announced, so a handler receives it exactly as before.

Resolving the request was not enough on its own either. An escalation opens a
durable keyed decision in the parent status log, and nothing ever closed it, so
a request the remote had answered kept surfacing in every later open-decisions
fold. The pending-reply library now owns both ends of that decision: it opens
one under a per-request key rather than the shared default key, and closes it
once the record resolves, appending the closing line only while that exact
decision is still open in the fold so it can neither double-close nor clear an
unrelated decision that has since taken the same key.

The handling instruction still routes a wake to its adapter, now as the
idempotent confirmation of what the runner already did rather than as the
guarantee.

Verified end to end in a throwaway isolated home driving the real armed source,
blocking delta reader, runner, and wake queue, with the handler doing only the
generic acknowledgement and no part of the ingest stubbed: before, seven failed
observations reproducing the incident; after, none. Each half is independently
load-bearing - without the runner change the reply never reaches the mirror,
without the escalation close the settled request still surfaces as an open
decision.

* no-mistakes(review): Prevent legacy reply closure from masking decisions

* no-mistakes(review): Serialize pending reply resolution and escalation closure

* no-mistakes(review): Serialize pending reply escalation with resolution

* no-mistakes(review): Clarify guarded legacy escalation closure behavior

* no-mistakes(review): Guard legacy closure and reserve pending reply keys

* no-mistakes(review): Match pending reply escalations by construction

* no-mistakes(document): Document automatic remote reply resolution

* no-mistakes(lint): Fix unused concurrent escalation loop variable

* no-mistakes(lint): Fix unused concurrent resolution loop binding

* no-mistakes(review): Version fold cache and gate autohandle on publication

* no-mistakes(document): Clarify remote reply relay documentation
…kill (kunchenguid#1841)

Bring the public installer-facing stow skill up to the internal skill's
current curation behavior while keeping it fully standalone:

- Replace the total-capture thesis with the compact-operating-map framing.
- Add read-the-destination-before-writing with the inspect-then-update
  triad (supersedes what, one-sentence rewrite, delete stale now).
- Add the concrete prune list together with its unique-fact guard, as an
  accuracy discipline with no size-budget machinery.
- Curate every memory file the pass has open, not only the routed one.
- Add the standing-decisions sweep category.
- Add the stronger-owner pointer-over-copy test before filing.
- Add tool-agnostic task-note discipline (inspect, classify, considered
  replacement body, never blind-append) and blocked-on recording.
- Give .stow-notes.md a closed set of three exits.
- Forbid storing, creating, or editing a skill as a stow destination.
- Report per-file action verbs in the completion receipt.
- Consolidate the repeated local-vs-external and .gitignore prose and fix
  the second-person voice slip, so the file does not grow (11334 -> 11276
  bytes).
Extend the bounded read-only serialization-debt probe to also report when
the canonical Decision OS checkout has remained off the configured base
branch (any other branch, or a detached HEAD) strictly beyond the shift
threshold. The checkout transition is dated from the most recent checkout:
entry in the bounded HEAD reflog window, never from the target commit's
age, so switching to an old branch cannot false-positive. Missing,
malformed, or truncated reflog evidence fails closed as
SERIALIZATION-DEBT-EVIDENCE-UNAVAILABLE. The probe never switches the
checkout.

Tests cover clean base, freshly switched non-base, the exact age
boundary, over-age non-base debt, detached HEAD, missing and truncated
reflog evidence, and propagation through the fleet refill cadence.
…probe

The standing serialization-debt/refill cadence now invokes the Decision OS
executable lane-contract checker (<project>/scripts/check_lane_contract.py,
override with --lane-checker or FM_LANE_CONTRACT_CHECKER) with --repo
<project> and maps its exit code to debt: 0 adds nothing, 1 surfaces each
stdout violation line as SERIALIZATION-DEBT source=lane-contract-checker,
and any other nonzero exit surfaces each stderr reason as
SERIALIZATION-DEBT-EVIDENCE-UNAVAILABLE. An unlaunchable checker (missing,
not executable, timed out, over-bounded output) is the same concrete
evidence-unavailable debt. Checker debt is appended to other collected debt,
never printed instead of it. The checker owns its invariants; the probe only
maps exit codes. Behavioral tests cover clean, exit-1 violation, exit-2
cannot-run, and unavailable-checker outcomes including proof that the
unavailable checker does not hide other debt, plus propagation through
fm-fleet-refill.sh.
…tation plan

Implementation-ready plan for the accepted design in docs/architecture.md
section "Durable implementation capacity and attempt lifecycle design",
composing Decision OS plan Tasks 7.1-7.6 with one shared capacity
classifier, one structured cleanup operation, claim-before-allocation,
ordered terminal orchestration, migration stages, rollback to alert-only,
and deletion of legacy arithmetic after measured parity. Local main owns
bin/fm-fleet-refill.sh absent from origin/main (merge-base 2cf0283);
both histories preserved, local main at 38b4eb6 is the canonical base.
Adds the plan's mandatory doc-audience classification entry.
…1-F8 and fleet-depth correction

Addresses the single canonical review's eight material findings exactly:
write-once receipt-derived attempts with no mutable phase or parallel
obligation files (F1); one outer non-reentrant terminal transaction with
lock-held primitives and preflight-all-refusals-before-effects (F2); real
history reconciliation in Task 0 with shadow-only consumers until post-parity
cutover (F3); structured crew-state contract and enforceable total deadline
(F4); Decision OS steward transaction against installed br/storage contracts
with pathspec-only guarded fast-forward publish (F5); centralized live
disposition with fresh per-effect authority (F6); attempt-bound copy
ownership, final landing receipt, frozen planned-path evidence, and pre-land
actual-diff recheck (F7); deterministic parity, brief wiring, exhaustive
file map, and Tasks 5/8 split (F8). Adds the fleet-depth correction: private
cron sentinel quarantined in Task 3, removed after cutover proof in Task 13,
verified gone in Task 15. Normalized to 16 tasks (Task 0 through Task 15).
… base

Local main owns bin/fm-fleet-refill.sh and the accepted fleet lifecycle design
(merge-base 2cf0283). Both histories are preserved: this merge keeps every
local-only commit (refill, serialization probes, design docs, calm footer,
bead closure discipline) and every upstream-only commit (833a9a2, be32879,
167ff42, 60eb534, 06b33aa). The twelve duplicated PRs resolve as identical
content. fm-fleet-refill.sh is preserved from the local side.

# Conflicts:
#	AGENTS.md
#	bin/fm-bootstrap.sh
#	bin/fm-startup-network.sh
#	tests/fm-bootstrap.test.sh
#	tests/fm-startup-network.test.sh
The fleet-depth quarantine makes legacy capacity unknown: the owned-manifest
and output-mtime battery count and the DISPATCH-NEEDED verdict are disabled,
and the script never stages work. Serialization-debt and the authoritative
bead-query diagnostics remain. Focused regression tests pin the quarantined
verdict shape, the ignored legacy manifest, and the surviving debt diagnostic.
Firstmate added 26 commits August 8, 2026 15:20
… budget

Per the captain-approved latency follow-up, FM_CAPACITY_PARALLEL defaults to 4
(the smallest measured value clearing the 2000 ms real-home budget) instead of
shortening the per-endpoint timeout: slow-but-live workers keep their full
per-row timeout, rows stay deterministically sorted, and the total deadline
still bounds the whole collection. Regression tests pin byte-identical
parallel output at the default, the total deadline under parallelism, and the
JSON-null attempt_id shape on ambiguous rows (found via the real-home read).
The coverage guard's comm calls ran under the ambient locale while their
inputs are LC_ALL=C-sorted, so an en_US.UTF-8 environment produced
'not in sorted order' warnings that failed the runner test. The gotmp
fixture's fake roots predated the Task 4/9 cleanup extraction and were
missing the fm-disposition-lib / fm-cleanup-lib / fm-attempt-lib siblings
teardown now sources.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 658773222a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread bin/fm-terminal.sh
# and owned provider identity
bead=$(fm_attempt_load "$attempt" | jq -r '.envelope.task_key')
[ -n "$bead" ] || { echo "terminal: no bead binding for $attempt" >&2; exit 1; }
bead_live=$(br show --json "$bead" 2>/dev/null) || bead_live=

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve bead reads inside the registered clone

Captain, automatic reconciliation invokes this script from the Firstmate home, but br discovers .beads/*.db from the current directory. The added live contract explicitly documents this at tests/live-decision-os-contract.test.sh:34-37, and the refill reader correctly uses cd "$PROJECT" at bin/fm-fleet-refill.sh:180; this unscoped read therefore fails before every automatic terminal transaction can close the bead or clean up the task. Run both terminal br show reads from the registered project and preserve the read-only flag.

AGENTS.md reference: AGENTS.md:L354-L356

Useful? React with 👍 / 👎.

Comment thread bin/fm-cleanup-lib.sh
Comment on lines +632 to +634
live=$(cleanup_copy_live_pids "$FM_CLEANUP_COPY" 2>/dev/null) || {
echo "cleanup: refused: cannot determine live processes under $FM_CLEANUP_COPY" >&2; return 1; }
[ -z "$live" ] || { echo "cleanup: refused: live process(es) in copy $FM_CLEANUP_COPY: $(printf '%s' "$live" | tr '\n' ' ')" >&2; return 1; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude the owned endpoint from process preflight

For a completed tmux, Herdr, Zellij, or cmux task whose pane still exists, the pane's shell or agent process itself has a cwd under the recorded worktree. Because this scan runs before cleanup.endpoint stops that validated endpoint, live is nonempty and every normal terminal cleanup refuses at preflight; the tests miss this by using fake endpoints with no corresponding process. Distinguish the owned endpoint process tree from leaked descendants, or stop the validated endpoint before checking for remaining processes.

Useful? React with 👍 / 👎.

Comment thread bin/fm-fleet-refill.sh
Comment on lines +68 to +71
fm_refill_automatic() { # 0 when automatic refill is authorized by the gate
[ "${FM_REFILL_AUTO:-0}" = 1 ] && return 0
[ -f "$CONFIG_DIR/refill-auto" ] && return 0
return 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enable refill on the production path

The newly added fleet contract requires safe P0/P1 capacity to refill without an operator request, but the default path always returns false here because no tracked code creates config/refill-auto or sets FM_REFILL_AUTO. Consequently the session, watcher, and terminal calls to --refill only print fleet-ok: alert-only after installation and never claim or launch ready work, leaving the primary behavior introduced by this commit disabled indefinitely.

AGENTS.md reference: AGENTS.md:L280-L281

Useful? React with 👍 / 👎.

Comment thread bin/fm-fleet-refill.sh
# and FM_REFILL_CURRENT_EVIDENCE_FILE are TEST-ONLY overrides;
# every other knob is a real runtime default.

FM_REFILL_HARNESS="${FM_REFILL_HARNESS:-pi}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve dispatch profile and quota before claiming work

When the automatic gate is enabled, refill hard-codes pi and proceeds to allocate and claim without consulting dispatch profiles or quota-axi. In a home whose matched profile selects another runtime, or whose Pi quota lacks headroom, this can claim a bead and create a reserved attempt before fm-spawn fails, or consume the wrong quota despite quota headroom being a required admission condition. Resolve the normal per-intake profile and current quota before creating the claim request.

AGENTS.md reference: AGENTS.md:L181-L185

Useful? React with 👍 / 👎.

Comment thread bin/fm-spawn.sh
echo "error: cannot resolve generation for $ATTEMPT_ID; refusing to allocate without the claim" >&2
return 1
}
source_hash=$(cd "$repo" 2>/dev/null && sha256sum .beads/issues.jsonl 2>/dev/null | cut -d' ' -f1) || {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a portable SHA-256 command for attempt claims

On a standard macOS installation sha256sum is absent while shasum -a 256 is available, so every attempt-bound spawn reaches this command with an empty hash and refuses before allocation. The repository already provides portable SHA-256 selection in fm_pr_sha256; use that shared behavior here and in the new receipt/refill hash sites so Decision OS dispatch is not Linux-only.

Useful? React with 👍 / 👎.

Comment thread bin/fm-capacity-lib.sh
Comment on lines +58 to +59
find "$dir/attempts" -maxdepth 1 -name '*.json' ! -name '*.request.*.json' -printf 'attempt:%f\n' 2>/dev/null \
| sed 's/\.json$//'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enumerate attempts portably on BSD find

BSD find on macOS does not support -printf, and stderr is suppressed here, so attempt records disappear from the entity list. Attempts that have metadata become false record_disappeared ambiguities, while claim-pending attempts with no metadata are omitted entirely; the latter are not counted as reserved ownership and repeated refill waves can allocate additional attempts despite the safe-capacity requirement. Emit basenames through a portable loop or -exec.

AGENTS.md reference: AGENTS.md:L280-L281

Useful? React with 👍 / 👎.

Comment thread bin/fm-cleanup-lib.sh
Comment on lines +439 to +440
proc_root=${FM_PROC_ROOT_OVERRIDE:-/proc}
for pid_dir in "$proc_root"/*/; do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refuse cleanup when the process source is unavailable

On systems without /proc, notably macOS, this glob is left literal and every iteration is skipped, so the function returns success with empty output instead of reporting that live-process inspection was unavailable. With the quiet interval disabled or bypassed, cleanup can then force-return a worktree while an unobserved child still has that directory as its cwd and may hold or create uncommitted work. Validate that the process root is usable or fall back to a portable process scanner before reporting no live PIDs.

AGENTS.md reference: AGENTS.md:L30-L32

Useful? React with 👍 / 👎.

Comment thread bin/fm-fleet-refill.sh

FM_REFILL_HARNESS="${FM_REFILL_HARNESS:-pi}"
FM_REFILL_MODE="${FM_REFILL_MODE:-direct-PR}"
FM_REFILL_PRIORITY_THRESHOLD="${FM_REFILL_PRIORITY_THRESHOLD:-2}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit automatic candidates to P0 and P1

The priority parser maps P2 to numeric 2, and availability accepts every value less than or equal to this default, so an enabled refill wave admits P2 beads alongside P0/P1 work. That consumes productive and reserved capacity outside the explicitly authorized automatic-refill scope and can delay the urgent beads the feature is intended to fill first; default the threshold to 1.

AGENTS.md reference: AGENTS.md:L280-L281

Useful? React with 👍 / 👎.

…ery branch

PR kunchenguid#2006's base advanced from 833a9a2 to 74230fc (docs: add project vision
kunchenguid#1997, feat(stow): tiered decaying memory kunchenguid#1984, feat(bin): deterministic
agent lifecycle control kunchenguid#1568). Merging the advanced base into the delivery
branch keeps Task 0's reconciliation merge (454b10d) and both original lines
(local main d40fd7f, origin/main 833a9a2) as ancestors instead of rebasing
them away, per the plan's preservation invariant.

Conflict resolutions (minimal; both sides preserved):
- bin/fm-spawn.sh: keep main's --relaunch adoption/validation flow and the
  branch's Task 6/7 attempt claim (claim-aware treehouse get --lease) in one
  if/elif; the meta writer keeps both attempt= and the relaunch publish flow
  (preserve_relaunch_meta, control_relaunch_tx) under SPAWN_META_PATH.
- bin/fm-teardown.sh: keep the branch's structured attempt wrapper (cleanup
  functions live in fm-cleanup-lib.sh); keep main's lifecycle locks
  (CONTROL_LOCK/META_LOCK/descendant task-set locks) and the META_LOCK
  release at the teardown tail.
- bin/fm-cleanup-lib.sh: adopt main's fm-control-lib.sh-owned turn-end token
  path contract (remove_grok/kimi_turnend_auth) and remove the new
  control-relaunch state files with the task state.
@kunchenguid

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#2006 at 65877322.

@Authentis Authentis closed this Aug 9, 2026
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.

2 participants