Skip to content

Adopt upstream baseline and active watcher fix#5

Merged
JTInventory merged 22 commits into
mainfrom
fm/controlled-baseline-sync-0627
Jun 28, 2026
Merged

Adopt upstream baseline and active watcher fix#5
JTInventory merged 22 commits into
mainfrom
fm/controlled-baseline-sync-0627

Conversation

@JTInventory

Copy link
Copy Markdown
Owner

Summary

Validation

  • bash tests/fm-watcher-lock.test.sh
  • bash tests/fm-wake-queue.test.sh
  • bash tests/fm-wake-daemon-lifecycle-e2e.test.sh
  • bash tests/fm-watch-session.test.sh
  • bash -n bin/fm-guard.sh bin/fm-watch-session.sh tests/fm-wake-daemon-lifecycle-e2e.test.sh tests/fm-wake-queue.test.sh tests/fm-watcher-lock.test.sh tests/fm-watch-session.test.sh tests/wake-helpers.sh

Do not merge until captain approval.

kunchenguid and others added 22 commits June 24, 2026 14:54
* fix: make watcher singleton race-proof and re-arm home-scoped

Concurrent fm_lock_try_acquire could produce two lock holders: the
stale-steal path destroyed and recreated the lock dir without
serialization, so two racers could both reclaim it. That let two
fm-watch.sh watchers run in one home, doubling every wake.

- fm-wake-lib.sh: single-winner acquire. Reclaim is serialized through a
  sibling ".steal" mutex (its abandonment floor decoupled from
  FM_LOCK_STALE_AFTER, never below 2s) and the holder pid is re-verified
  dead immediately before the rmdir. Every claim writes its pid and reads
  it back; a stomped pid means we lost. mkdir is the atomic arbiter, so at
  most one acquirer ever returns 0.
- fm-watch.sh: self-eviction. Each poll a watcher checks the lock still
  names its pid; if another took over, it exits cleanly, so any transient
  duplicate self-resolves within one poll.
- fm-watch-arm.sh: safe re-arm. Default no-ops via the singleton;
  --restart signals only this home's recorded watcher pid, never a broad
  pkill that would kill sibling homes' watchers.
- AGENTS.md section 8: route re-arm through fm-watch-arm.sh and warn that
  pkill -f bin/fm-watch.sh kills other homes' watchers.
- Tests: concurrency single-winner, dead-pid steal, live-lock no-op,
  watcher self-eviction.

* no-mistakes(review): Harden watcher lock ownership

* no-mistakes(review): Captain, harden watcher lock claims

* no-mistakes(review): Captain, harden lock steal mutex ownership

* no-mistakes(review): Captain, harden steal-aware lock claims

* no-mistakes(document): Document watcher arm workflow
* fix: correct no-mistakes gate contract in brief and AGENTS

The no-mistakes definition-of-done told crewmates to "fix auto-fix
findings yourself", which reads as self-committing fixes - the exact
behavior the pipeline forbids. Crewmates anchored on it self-committed
or aborted a healthy long run, tangling git state and stalling the fleet.

- fm-brief.sh: the crewmate drives the gates and never edits or commits
  code while a run is active; it advances auto-fix findings with
  `no-mistakes axi respond --action fix`; the run blocks for minutes and
  auto-fixes itself with no gate, so do not cancel, abort, re-run, or
  background it, or idle-wait for a notification.
- AGENTS.md: fix the supervisor-facing line (the pipeline fixes, the
  crewmate responds and never edits during a run) and scope the
  "background long work" guidance to firstmate's own session, so a
  crewmate runs its own validation in the foreground.

* no-mistakes(document): Sync no-mistakes gate docs
* docs: add built-in skills table to README

* no-mistakes(document): Sync skill invocation docs

* docs: drop user-level no-mistakes from built-in skills

no-mistakes is a user-level prerequisite (~/.claude/skills), not a
firstmate-shipped skill. Remove the stale vendored .agents/skills/no-mistakes
duplicate and its README table row; keep only /afk and /updatefirstmate.
Retarget the codex invocation example to a skill that remains in the table.

* no-mistakes(document): Tighten built-in skills README wording
* fix(watch): honest self-verifying watch-arm + prominent no-watcher guard banner

fm-watch-arm.sh now forks the watcher as a tracked child, verifies a live
watcher with a fresh beacon (reusing FM_GUARD_GRACE), and prints one honest
line - started/healthy/FAILED - exiting non-zero when none can be confirmed.
Never reports healthy off a stale beacon or dead/reused pid.

fm-guard.sh leads the no-watcher case with a bordered ●-marked banner (in-flight
count, beacon age, exact re-arm command) so it cannot be skimmed past; queued-
wakes warning and grace-window quiet preserved.

AGENTS.md section 8 codifies arming only via the harness's tracked background
mechanism, never a fire-and-forget shell &.

Tests: arm started/healthy/FAILED + never-healthy-off-dead-pid; guard banner-
first and fresh-watcher-quiet. Restart test updated for the child topology.

* no-mistakes(review): Propagate immediate watcher wakes

* no-mistakes(review): Trap HUP during watcher arm cleanup

* no-mistakes(review): Wait for peer watcher beacon

* no-mistakes(document): Sync watcher arm docs

* no-mistakes: apply CI fixes
* docs: restructure firstmate agent manual

* no-mistakes(document): Sync agent skill documentation
* test: consolidate suite into lifecycle e2e, safety matrices, shared helpers

Move operator-visible lifecycle behavior into focused end-to-end tests, table-
drive repetitive cases, and add shared test helpers, without weakening any
safety-critical coverage.

- Add tests/lib.sh (reporters, self-cleaning temp root, fakebin/git/meta
  helpers, common assertions) plus behavior-area helpers tests/secondmate-
  helpers.sh and tests/wake-helpers.sh; refactor every test file onto them.
- Split the two giant files by behavior area: fm-secondmate -> lifecycle e2e +
  safety; fm-wake-queue -> wake-queue losslessness + fm-watcher-lock +
  fm-daemon.
- Add fm-secondmate-lifecycle-e2e (seed -> spawn -> routed send -> backlog
  handoff -> recovery respawn -> teardown) and fm-wake-daemon-lifecycle-e2e
  (routine/terminal routing across a watcher restart, one buffered digest, no
  duplicate, stale transient/persistent/resume).
- Expand fm-afk-inject-e2e with a normal single-digest scenario and delete the
  five overlapping fake-tmux injection cases (persistent-swallow kept as a unit).
- Table-drive the secondmate teardown path-boundary matrix, watch-arm
  started/self-heal, guard warnings, spawn-batch and bootstrap parsers, and the
  fm-update worlds.
- Add an explicit timeout-minutes (15) to the behavior-test CI job.

Preserved as focused matrices/units: teardown unlanded-work safety, lock-
primitive races and watcher singleton, wake-queue losslessness and no-duplicate
escalation, secondmate path-boundary validation on every entrypoint, fast-
forward-only update skips, the captain-relevant status-phrase matrix, and
no-mistakes init / origin preservation / never-write-through-a-project.

* no-mistakes(review): Require tmux for AFK e2e coverage

* no-mistakes(review): Captain, require AFK e2e self-check coverage

* no-mistakes(review): Captain: preserve secondmate launch isolation coverage

* no-mistakes(document): sync test documentation

* no-mistakes: apply CI fixes
* docs: restructure README into concise overview plus docs/ tree

Trim README from 267 to 138 lines by relocating reference material into a
new docs/ tree and CONTRIBUTING, with no information loss:

- docs/architecture.md: the How It Works internals (supervision engine,
  secondmates, project modes, fleet sync, self-update, dev notes).
- docs/configuration.md: config prose, FM_HOME, harness support, the full
  toolchain list, and the env-var reference block (verbatim).
- docs/scripts.md: the bin/ toolbelt table (verbatim).
- CONTRIBUTING.md: fold in the foreground/no-mistakes-gate prose and the
  dev/test command block (verbatim), which it previously lacked.

Rewrite README to: tightened pitch, one-line feature bullets, merged Quick
Start + Install, How It Works reduced to diagram + synopsis, a Documentation
section linking the new docs, and Contributing + License sections. Keep the
user-invocable slash-command table front-and-center. Reference AGENTS.md by
topic, not section number, so it stays robust to the AGENTS restructure.

* docs: swap README banner to new PNG asset

* no-mistakes(review): Fix stale secondmate test checklist

* no-mistakes(review): Fix harness documentation ownership

* no-mistakes(review): Correct targeted test checklist

* no-mistakes(review): Restore spawn batch checklist reference

* no-mistakes(document): sync documentation references
* feat(guards): prevent and detect firstmate primary worktree tangle

Firstmate is a treehouse-pooled git repo of itself: the primary checkout
and every crewmate worktree / secondmate home are linked worktrees of one
repo. A crewmate sent to work firstmate-on-itself can branch and commit in
the PRIMARY checkout instead of its own isolated worktree, stranding the
primary on a feature branch (e.g. fm/readme-restructure-d3). Add two guards:

Prevention (spawn-time):
- fm-brief.sh: ship-brief Setup now opens with a worktree-isolation
  assertion ahead of the branch step. The crewmate confirms it is in its
  own treehouse worktree, and stops with
  `blocked: launched in primary checkout, not an isolated worktree`
  if it resolves to the primary.
- fm-spawn.sh: after treehouse get, refuse to launch unless the resolved
  worktree is a genuine isolated worktree root distinct from the primary
  checkout (PROJ_ABS); abort with a clear error otherwise.

Detection (immediate):
- bin/fm-tangle-lib.sh: shared classifier. A NAMED non-default branch in
  the primary is the tangle; the default branch and detached HEAD (the
  legitimate state of worktrees and secondmate homes) stay silent.
- fm-guard.sh: bordered alarm, in the watcher-down banner's spirit, on the
  very next fleet action when the primary is on a feature branch.
- fm-bootstrap.sh: same assertion at session start as a TANGLE: line.

Tests: tests/fm-tangle-guard.test.sh covers the lib classification, the
guard banner, the bootstrap line, brief assertion ordering, and the spawn
abort. fm-bootstrap and fm-watcher-lock guard tests now pin FM_ROOT so the
ambient (feature-branch) checkout never leaks a TANGLE line into them.

Docs: AGENTS.md sections 3, 7, 8, 11 document both guards.

* no-mistakes(review): Make brief isolation path authoritative

* no-mistakes(document): Sync tangle guard docs

* no-mistakes: apply CI fixes

* no-mistakes(document): Sync tangle guard docs
* fm-send: pause after submit so the receiving turn is visibly up

A successful text send only proves the composer cleared - the Enter
landed and the text was submitted - but the harness needs a beat to spin
up the turn before its busy footer appears. An immediate peek after
fm-send returns would see the stale idle pane. Add a fixed FM_SEND_SETTLE
pause (default 1s, 0 disables) on the successful text-submit path only.

Scoped to fm-send's text path: not the --key path, and not the shared
fm_tmux_submit_core, so the away-mode daemon (which only needs
'submitted') never pays it. Purely additive; exit codes unchanged.

* no-mistakes(document): Document fm-send settle pause
* feat: track primary local HEAD for secondmate homes

Make every secondmate home follow the primary firstmate checkout's current
default-branch commit via a purely local fast-forward (no origin fetch), so
deliberate captain updates to the primary converge the fleet automatically.

- Extract fm-update's ff machinery into bin/fm-ff-lib.sh; ff_target gains a
  base_mode parameter (origin -> origin/<default> with fetch; any commit-ish
  -> that local commit, no fetch) so both sync paths share one implementation.
- fm-spawn: fast-forward a secondmate worktree to the primary's HEAD before
  launch (covers fresh spawns and recovery-respawns).
- fm-bootstrap: sweep every live secondmate home to the primary's HEAD and
  emit NUDGE_SECONDMATES: only for running secondmates whose instructions
  actually changed; already-current or no-instruction-change homes are left
  undisturbed.
- Document spawn + bootstrap behavior in AGENTS.md.
- Add hermetic tests for the local-HEAD ff helper (updated/current/dirty/
  diverged/in-flight), no-fetch guarantee, nudge gating, the bootstrap sweep,
  and the spawn hook.

* no-mistakes(review): Fix secondmate sync git shim

* no-mistakes(review): Surface skipped secondmate sync diagnostics

* no-mistakes(document): Document secondmate sync
…uid#93)

* feat(send): mark firstmate->secondmate requests for status-path reply

fm-send now prepends a distinct from-firstmate marker (new bin/fm-marker-lib.sh:
the label [fm-from-firstmate] followed by ASCII unit separator 0x1f) when the
resolved target is a bare fm-<id> whose meta records kind=secondmate. The marker
reuses the afk untypable-separator concept but is deliberately distinct from the
daemon's bare leading 0x1f, so it never conflates with a secondmate's own afk
escalation marker.

A secondmate is itself a firstmate, so a relayed request lands in its own chat,
which the main firstmate never reads. The secondmate charter (fm-brief.sh) and
AGENTS.md now document the recognize-and-respond-via-status contract: a marked
request is answered via the status file (or a doc plus a status pointer), never
chat-only; an unmarked message stays conversational captain intervention.

Crewmate/scout targets, explicit session:window targets, and the --key path are
unchanged. Backbone (watcher, daemon, tmux-lib, afk) untouched.

Hermetic tests cover marked vs unmarked sends and the exact marker bytes.

* no-mistakes(document): Sync secondmate marker docs
* fix(teardown): recognize squash-merged work as landed

fm-teardown refused to tear down a worktree whose PR was squash-merged
and whose head branch was then deleted - the most common GitHub flow -
because its safety check used commit reachability (HEAD --not --remotes)
rather than whether the work actually landed. A squash merge writes one
new commit on the default branch, so the branch's own commits are on no
remote-tracking branch, and auto-delete-on-merge drops the head ref too;
reachability was non-empty and teardown false-refused merged work.

For a normal ship task whose commits are not remote-reachable, before
refusing, treat the work as landed if either its PR is merged (resolved
from meta pr= or the branch name via gh-axi; authoritative for squash,
rebase, and merge alike, and surviving branch deletion) or its content
is already in the up-to-date default branch (a 3-way merge-tree that adds
nothing the default branch lacks - robust to the default having advanced
past the merge-base). Dirty worktrees and genuinely unlanded work still
refuse; a gh lookup error falls back to the content check and, if that is
inconclusive, refuses (fail-safe, never silently allows). Fork, local-only,
scout, secondmate, and --force paths are unchanged.

Tests cover squash-merged+deleted-branch (allow), genuinely unlanded
(refuse), dirty (refuse), normally pushed (allow), content-in-default
fallback (allow), and gh-error+content-absent (refuse, fail-safe).

* no-mistakes(review): Guard teardown against stale PR proof

* no-mistakes(review): Verify teardown PR heads

* no-mistakes(document): Sync teardown landing docs

* no-mistakes: apply CI fixes
* fix: harden no-mistakes validation contract against gate-park and self-fix duplication

Rewrite the no-mistakes validation block scaffolded into every ship brief so a
crewmate knows the right move at each gate: the pipeline owns every fix
(including the fix for a real bug review finds in the crewmate's own code);
respond, never self-implement/abort/re-run; the ask-user loop end to end (feed
the decision back via axi respond, do not hand-fix); process every return
(backgrounding ok, never idle-wait for auto-advance); avoid --yes (it
auto-resolves ask-user with zero escalation); and review findings always gate
(review auto-fix is disabled).

Add the supervisor heuristic in AGENTS.md: firstmate keys off the no-mistakes
run step status - running/fixing/ci means working, awaiting_approval/fix_review
means parked (surfaced as awaiting_agent: parked <duration> on axi status) - not
shell liveness, plus a self-fix-duplication red flag (hand-commits/abort/re-run
mid-validation).

Resolves the gate-park-deadlock and self-fix-duplication failure modes.

* no-mistakes(document): Sync validation docs

* no-mistakes(lint): Lint checks clean
* fix(watch): tighten watcher re-arm discipline and assert liveness on drain

The watcher supervision chain could silently lapse: re-arms were bundled
at the tail of multi-command calls (where fm-watch-arm no-ops when a cycle
is briefly still alive, so no fresh cycle got established), and text-only
"holding" turns ran no supervision script, so fm-guard's liveness banner -
which only fires when a guarded script runs - never triggered.

AGENTS.md section 8: make the re-arm rules unambiguous - keep exactly one
live cycle while work is in flight; re-arm after each FIRE (a completed arm
task carrying a wake reason) and never churn on a healthy/started no-op; run
fm-watch-arm standalone, never bundled; never end a turn blind, holds
included. Existing material (singleton lock, beacon, guard banner, afk
exception) is preserved.

fm-wake-drain.sh: assert watcher liveness after draining by reusing
fm-guard.sh's existing graced, beacon-based banner, so a lapse also surfaces
on a plain drain-and-handle turn. Called after the queue is emptied so the
guard never re-prints its own queued-wakes notice, and best-effort so it
never changes the drain's exit status. The grace beacon keeps it silent right
after a normal fire and warns only on a stale-beyond-grace lapse with work in
flight. Watcher core (fm-watch.sh, lock, beacon-touch, wake-queue) untouched.

tests: keep the worktree-tangle check inert across the drain-invoking suites
(wake-helpers points FM_ROOT at a non-git dir, the same trick the direct
fm-guard.sh tests use), and add a regression test asserting the drain warns on
a lapse and stays silent right after a fire.

* no-mistakes(document): Document drain-time watcher liveness
…id#102)

* fix(brief): slim no-mistakes contract to pointer + firstmate wrapper

no-mistakes v1.31.2 self-documents the validation mechanics in its own
SKILL.md (refreshed from the binary on init) and live axi help output,
including the review-always-gates, no-abort/rerun-mid-run, and
never-idle-wait facts. fm-brief.sh's no-mistakes-mode Definition of done
was restating all of that, a drift hazard since the brief lives in a
different repo from the version-matched SKILL.md.

Collapse the duplicated mechanics block to a ~3-line pointer at no-mistakes'
own guidance and keep only the firstmate-specific wrapper: the done
handshake, ask-user escalation to firstmate via rule 6, the --yes captain
stance, and the CI-green reporting line.

* no-mistakes(review): Enforce no-mistakes bootstrap version floor

* no-mistakes(document): Sync no-mistakes docs

* no-mistakes(lint): Fix ShellCheck version parsing
* fix(send): settle codex $skill popup before submit

A `$<skill>` invocation (e.g. $no-mistakes) opens codex's $-autocomplete
popup; submitting too fast lets it swallow the Enter so the invocation
never lands - biting every pipeline trigger to a codex crew/secondmate.
Mirror the existing `/` slash-popup handling: give a `$...` message the
1.2s popup-settle before the (retried) Enter, but scope it to
harness=codex (read from the target's meta) so `$`-prefixed plain text
("$5/month", "$HOME") to claude/opencode/pi keeps the 0.3s fast path. An
explicit session:window target has no meta -> harness unknown -> treated
as non-codex. The retried Enter in fm_tmux_submit_core still backs the
settle up; the `/` case, --key path, marker, and meta-resolution
contract are unchanged.

Record the codex $-popup fact in the harness-adapters skill, and add a
per-harness settle-selection test (codex $ ->1.2, claude $ ->0.3,
explicit $ ->0.3, any / ->1.2, plain ->0.3).

* no-mistakes(document): Sync fm-send popup docs
* feat(supervise): deterministic crew current-state helper

Add bin/fm-crew-state.sh, a deterministic one-line read of a crew's
CURRENT state that ends the stale-status-line friction in supervision.
The status log is an append-only wake-event log, so tail -1 reports the
last event, not the current state: after firstmate resolves a
needs-decision/blocked and the crew silently resumes, the log stays
stale. The helper takes the active no-mistakes run-step as the source of
truth (attributed to the crew's branch, via axi status then the run
list), reconciles a possibly-stale needs-decision/blocked log line
against it (flagging it superseded when the run has resumed), and falls
back to the pane busy-signature + status log when no run is active.
Handles scout/secondmate kinds and torn-down/missing crews gracefully.

Reuses fm-tmux-lib.sh busy detection and meta parsing; fully
deterministic (run-step/pane/log reads only, no heuristics, no LLM).

Wire AGENTS.md sections 7 and 8 to read current state via the helper and
to stop inferring state from a tail of the status log; the status file
stays the wake-event log. Watcher core, status-file/brief contracts, and
wake/dedup machinery are unchanged.

Add tests/fm-crew-state.test.sh covering run-step authority, superseded
stale log lines, genuine-parked, cross-branch attribution, pane and
status-log fallback, scout skip, and torn-down/missing-meta cases.

* no-mistakes(review): Fix dead-window crew state fallback

* no-mistakes(review): Harden crew state liveness checks

* no-mistakes(review): Harden crew state gate handling

* no-mistakes(review): Harden crew state gate parsing

* no-mistakes(document): Document crew state helper

* no-mistakes: apply CI fixes

* fix(crew-state): keep run-step authoritative over a closed pane

The dead-window guard ran before the no-mistakes run lookup, so a
finished crew whose agent had exited and closed its window reported
'unknown' instead of its authoritative run-step state (e.g. done) - the
normal gap between a crew completing and teardown, and a regression
against the helper's core principle of judging by the run-step, not the
shell. Move the pane-readability guard into the no-run fallback path so
it runs only after the run-step lookup: a crew with a run reports its
run-step state regardless of pane liveness, while a genuinely runless
dead window still reports unknown rather than trusting a stale log.

Replace the test that encoded the regression with two that pin the
correct behavior: a closed pane with a terminal run reports done, and a
closed pane with an active run reports working.

* no-mistakes(document): Sync crew-state docs
* feat(watch): always-on wake triage absorbs benign wakes in bash

Factor the afk daemon's classify_signal/classify_stale/heartbeat-scan into a
shared bin/fm-classify-lib.sh used by both the always-on watcher and the daemon.
fm-watch.sh now loops internally: it classifies each wake and absorbs the benign
majority (working: signals, bare turn-ended, non-terminal stale, no-change
heartbeat) by advancing the suppression marker and logging, without queuing or
exiting; it writes the durable queue and exits only on an actionable wake
(captain-relevant signal, any check, terminal stale, a non-terminal stale that
persists past FM_STALE_ESCALATE_SECS, or the heartbeat fleet-scan backstop). So
firstmate re-arms once per actionable event instead of once per wake.

Safety preserved: singleton lock + beacon (touched every poll including while
absorbing), durable queue for actionable wakes, kind=secondmate stale-skip,
per-task check polling, heartbeat backoff, bounded wedge latency. While state/.afk
exists the daemon owns triage and the watcher reverts to one-shot, so the two
never double-triage. AGENTS.md section 8 + afk skill updated; tests added.

* no-mistakes(review): Captain, repair watcher triage edge cases

* no-mistakes(document): Sync watcher triage docs

* no-mistakes: apply CI fixes
…unchenguid#87)

The firstmate side of "listen on X and reply", shipped inside the repo so every
user has it but INERT until they opt in (a non-empty FMX_PAIRING_TOKEN in .env).
Purely additive: the watcher backbone (fm-watch.sh, fm-watch-arm.sh,
fm-wake-lib.sh) and the afk daemon (fm-supervise-daemon.sh, afk skill) are
untouched.

- bin/fm-x-poll.sh: one short-poll of GET /connector/poll; hard no-op without a
  token; requires non-empty text; stashes the full mention (incl. in_reply_to
  conversation context) to state/x-inbox/<id>.json behind a path-traversal guard;
  prints "x-mention <id>" (or a rate-limited "x-mode-error ...") for the watcher.
- bin/fm-x-reply.sh: POST /connector/answer; bearer token via a 0600 header file
  (never argv); reply via --text-file/stdin so mention text is never inlined into
  a shell command. Long replies auto-split into a premium-independent numbered
  "(k/n)" thread (codepoint-aware, capped); single tweet stays unnumbered. Wire:
  {request_id, text}, plus {texts:[...]} for a thread. FMX_DRY_RUN previews to
  state/x-outbox and posts nothing.
- bin/fm-x-lib.sh: .env/env config (token, relay default, dry-run, max chars,
  thread cap; env wins over .env) and the thread splitter.
- bin/fm-bootstrap.sh: .env-presence activation - drop the check shim + 30s
  cadence config on opt-in, remove on opt-out, idempotent, silent off.
- .agents/skills/fmx-respond: public-safe answer playbook - drain inbox, judge
  follow-up worthiness (skip pure acks), conversation continuity via in_reply_to,
  concise by default, dry-run aware.
- AGENTS.md section 14 + README/CONTRIBUTING/docs; tests/fm-x-mode.test.sh
  (hermetic: fake curl, real jq).
* fix(fmx): X-mode mentions are captain instructions - act, then reply

Owner-only relay routing means every routed mention is from the firstmate's
own owner, so fmx-respond no longer frames the asker as a stranger and may
address them as captain. Enabling X mode is the standing authorization, so
replies are composed and posted autonomously with no per-reply confirmation;
dry-run stays the only non-posting path.

A mention's request is a real captain instruction to act on, not merely to
reply to: the drain loop now classifies each mention as an actionable request
(run firstmate's normal lifecycle - intake, backlog, dispatch, scout, ship -
then report the outcome), a question (answer from fleet state), or a pure
acknowledgment (skip). The public channel keeps the yolo carve-out: anything
destructive, irreversible, or security-sensitive is flagged to the captain
through the trusted channel first, never executed straight from a mention.

Public-safety (outcomes only, no secrets/internals), the untrusted-in_reply_to
caution, and the skip-acknowledgment judgment remain intact. AGENTS.md §14
reflects the owner identity, autonomous answering, and act-on-requests carve-out.

* no-mistakes(review): Captain: clarify X-mode consent safeguards

* no-mistakes(review): Captain: document X-mode action consent

* no-mistakes(document): Captain, sync X-mode docs
* fix(fleet-sync): self-heal safe detached drift, loudly flag stuck clones

A pooled clone that drifts off its default branch was silently skipped
forever by both the post-merge teardown sync and bootstrap fleet-sync,
falling further behind on every merge with only an easy-to-miss skip line.

- Auto-recover the one safe case: a clean, detached HEAD that is an
  ancestor of origin/<default> and whose <default> is free to check out
  is re-attached and fast-forwarded, reported as 'recovered:'.
- Every other off-default state (dirty, non-default named branch,
  detached with unique commits, diverged) is left untouched and reported
  as a quantified 'STUCK: ... N commits behind ... - needs attention'
  warning instead of a quiet drift. Nothing is forced, stashed, or discarded.
- Relay the new recovered:/STUCK: outcomes through bootstrap FLEET_SYNC
  lines; document both in AGENTS.md section 3.
- Add tests/fm-fleet-sync.test.sh covering recover, every stuck variant,
  ordinary fast-forward, already-current, local-only/no-origin skips, the
  whole-fleet form, and the bootstrap relay.

* no-mistakes(review): Guard detached recovery from diverged local defaults

* no-mistakes(document): Sync fleet refresh docs

* no-mistakes: apply CI fixes
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