Skip to content

fix: wake native away mode on captain-relevant events - #18

Merged
ruby-dlee merged 3 commits into
mainfrom
fm/afk-reapwake-p1
Jul 22, 2026
Merged

fix: wake native away mode on captain-relevant events#18
ruby-dlee merged 3 commits into
mainfrom
fm/afk-reapwake-p1

Conversation

@ruby-dlee

Copy link
Copy Markdown
Owner

Intent

Fix the Claude away-mode overnight escalation wedge in one single-concern PR, with a postmortem committed before the implementation. Preserve bash triage and batching so routine wakes remain token-free, but deliver captain-relevant needs-decision, ready-to-merge, and failure events by completing Claude's native tracked background task instead of typing into the composer. Keep state/.wake-queue as the lossless backlog, remove pane busy/composer guards from native delivery, retain honest legacy compatibility semantics, update the /afk skill and AGENTS.md section 8, and prove in isolated regression tests that native delivery succeeds under the old false-defer conditions. Hold the PR for captain merge and do not merge it.

What Changed

  • Route captain-relevant native AFK escalations through tracked background-task completion while preserving token-free Bash triage, batching, and the lossless wake queue.
  • Keep terminal-backed injection compatibility, but bypass pane-target, busy, and composer guards for native delivery and support re-arming without leaving away mode.
  • Add regression coverage for the former false-defer conditions and document native/compatibility semantics alongside the injection-wedge postmortem.

Risk Assessment

✅ Low: Captain, the change is focused, preserves the durable wake backlog and legacy delivery path, and conforms to the stated native-delivery requirements without source-verifiable defects.

Testing

The supplied full baseline already passed; focused regressions and an isolated three-event runtime scenario also passed with CLI transcripts captured, and final cleanup left the worktree clean. No screenshot was applicable to this non-UI shell behavior.

Evidence: All escalation classes batched through native delivery
^D��ROUTINE: tracked task remained parked; no completion notification
COMPLETION:
  afk: starting supervise daemon in foreground; keep this command as a tracked background session
  afk-reap-wake: Supervisor escalate (3 event(s)): needs.status: needs-decision: choose the safe rollout | ready.status: done: PR https://github.com/Ruby-Labs/relvino/pull/4242 checks green | failure.status: failed: deployment smoke test exited 2 (pre-read); drain state/.wake-queue, handle the batch, then restart the away daemon as a native tracked background task
DURABLE WAKE BACKLOG:
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/routine.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/routine.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/needs.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/needs.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/ready.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/ready.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/failure.status
  - kind=signal payload=signal: /Users/dongkeun/.no-mistakes/worktrees/b174997b7206/01KY50TWT58BM1EWXNFSQH98MD/.afk-native-all-events.4jEJRE/failure.status
PERSISTED STATUS RECORDS REFERENCED BY THE BACKLOG:
  - needs.status: needs-decision: choose the safe rollout
  - ready.status: done: PR https://github.com/Ruby-Labs/relvino/pull/4242 checks green
  - failure.status: failed: deployment smoke test exited 2
POST-DELIVERY: away mode remains active; legacy injection-wedge marker absent
Evidence: Focused false-defer regression transcript
^D��ok - routine wake stays in bash and keeps the native task parked
ok - captain-relevant wake completes natively despite old pane-guard false-defer inputs
ok - completed native task can be re-armed without leaving away mode
all native afk reap-wake tests passed
Evidence: Reproducible multi-event evidence harness
#!/usr/bin/env bash
set -euo pipefail
export FM_GATE_REFUSE_BYPASS=1

ROOT=${1:?usage: native-reap-wake-all-events.sh <repo-root>}
START="$ROOT/bin/fm-afk-start.sh"
STATE_DIR=
DAEMON_PID=

cleanup() {
  if [ -n "${DAEMON_PID:-}" ] && kill -0 "$DAEMON_PID" 2>/dev/null; then
    kill "$DAEMON_PID" 2>/dev/null || true
    wait "$DAEMON_PID" 2>/dev/null || true
  fi
  [ -z "${STATE_DIR:-}" ] || rm -rf "$STATE_DIR" 2>/dev/null || true
}
trap cleanup EXIT

wait_for_seen() {
  local marker=$1 expected=$2
  for _ in $(seq 1 120); do
    if [ -f "$marker" ] && grep -F "$expected" "$marker" >/dev/null 2>&1; then
      return 0
    fi
    sleep 0.1
  done
  echo "error: watcher did not classify '$expected'" >&2
  return 1
}

STATE_DIR=$(mktemp -d "$ROOT/.afk-native-all-events.XXXXXX")
: > "$STATE_DIR/.afk"
printf 'none\t-\tnative\n' > "$STATE_DIR/.afk-daemon-terminal"

FM_STATE_OVERRIDE="$STATE_DIR" \
FM_AFK_STATE_PREPARED=1 \
FM_ESCALATE_BATCH_SECS=10 \
FM_HOUSEKEEPING_TICK=1 \
FM_POLL=1 \
FM_SIGNAL_GRACE=1 \
FM_HEARTBEAT=999999 \
FM_CHECK_INTERVAL=999999 \
FM_BUSY_REGEX='.' \
FM_SUPERVISOR_BACKEND=unsupported \
FM_SUPERVISOR_TARGET=missing-pane \
"$START" > "$STATE_DIR/daemon.out" 2> "$STATE_DIR/daemon.err" &
DAEMON_PID=$!

for _ in $(seq 1 60); do
  [ -f "$STATE_DIR/.supervise-daemon.pid" ] && break
  sleep 0.1
done
if [ ! -f "$STATE_DIR/.supervise-daemon.pid" ]; then
  echo "error: native away daemon did not start" >&2
  sed 's/^/  /' "$STATE_DIR/daemon.err" >&2
  exit 1
fi

printf 'working: routine progress\n' > "$STATE_DIR/routine.status"
sleep 3
kill -0 "$DAEMON_PID"
[ "$(grep -c 'afk-reap-wake:' "$STATE_DIR/daemon.out" 2>/dev/null || true)" -eq 0 ]
echo "ROUTINE: tracked task remained parked; no completion notification"

needs='needs-decision: choose the safe rollout'
ready='done: PR https://github.com/Ruby-Labs/relvino/pull/4242 checks green'
failed='failed: deployment smoke test exited 2'

printf '%s\n' "$needs" > "$STATE_DIR/needs.status"
wait_for_seen "$STATE_DIR/.subsuper-seen-status-needs" "$needs"
printf '%s\n' "$ready" > "$STATE_DIR/ready.status"
wait_for_seen "$STATE_DIR/.subsuper-seen-status-ready" "$ready"
printf '%s\n' "$failed" > "$STATE_DIR/failure.status"
wait_for_seen "$STATE_DIR/.subsuper-seen-status-failure" "$failed"

for _ in $(seq 1 240); do
  kill -0 "$DAEMON_PID" 2>/dev/null || break
  sleep 0.1
done
if kill -0 "$DAEMON_PID" 2>/dev/null; then
  echo "error: batched native delivery did not complete" >&2
  exit 1
fi
wait "$DAEMON_PID"
DAEMON_PID=

grep -F 'Supervisor escalate (3 event(s))' "$STATE_DIR/daemon.out" >/dev/null
grep -F "$needs" "$STATE_DIR/daemon.out" >/dev/null
grep -F "$ready" "$STATE_DIR/daemon.out" >/dev/null
grep -F "$failed" "$STATE_DIR/daemon.out" >/dev/null
grep -F 'needs.status' "$STATE_DIR/.wake-queue" >/dev/null
grep -F 'ready.status' "$STATE_DIR/.wake-queue" >/dev/null
grep -F 'failure.status' "$STATE_DIR/.wake-queue" >/dev/null
[ -e "$STATE_DIR/.afk" ]
[ ! -e "$STATE_DIR/.subsuper-inject-wedged" ]

echo "COMPLETION:"
sed 's/^/  /' "$STATE_DIR/daemon.out"
echo "DURABLE WAKE BACKLOG:"
awk -F '\t' '{printf "  - kind=%s payload=%s\n", $3, $5}' "$STATE_DIR/.wake-queue"
echo "PERSISTED STATUS RECORDS REFERENCED BY THE BACKLOG:"
for status_file in needs.status ready.status failure.status; do
  printf '  - %s: ' "$status_file"
  cat "$STATE_DIR/$status_file"
done
echo "POST-DELIVERY: away mode remains active; legacy injection-wedge marker absent"

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; uv run --directory tools/agent-fleet --locked pytest || rc=1; uv run --directory tools/agent-fleet --locked python -m compileall -q src || rc=1; exit "$rc"
  • Pre-supplied successful baseline: command -v tmux &gt;/dev/null || { echo &#34;tmux is required for e2e tests&#34; &gt;&amp;2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo &#34;== $t ==&#34;; bash &#34;$t&#34; || rc=1; done; uv run --directory tools/agent-fleet --locked pytest || rc=1; uv run --directory tools/agent-fleet --locked python -m compileall -q src || rc=1; exit &#34;$rc&#34;
  • bash tests/fm-afk-reap-wake-e2e.test.sh captured via script
  • bash tests/fm-daemon.test.sh
  • bash …/native-reap-wake-all-events.sh &lt;worktree&gt; captured via script
  • git diff-tree --no-commit-id --name-only -r bda8705 and ordered base-to-target git log
  • Final git status --short --untracked-files=all and transient test-directory scan
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@ruby-dlee
ruby-dlee merged commit 22090c3 into main Jul 22, 2026
5 checks passed
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