Skip to content

feat(first-mate-skill): 1.3.0 - one coordinator and loop per effort, plus lessons from a long session - #5

Merged
mogul merged 12 commits into
mainfrom
feat/first-mate-1.3.0
Oct 31, 2026
Merged

mogul merged 12 commits into
mainfrom
feat/first-mate-1.3.0

Conversation

@mogul

@mogul mogul commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

Summary

Skill 1.3.0 for first-mate-skill, from a long coordinating session that showed several repeated failures. The wait loop now holds one owner-keyed lock, a forked or resumed session is a non-coordinator by default, the detector watches branches and workflows, and the skill gains rules for fresh bases, verified agent claims, runtime acceptance signals, post-merge proof, repo PR standards, and a handoff section for pending decisions.

AI-assisted: written by an AI coding agent (OpenCode, Claude Sonnet 5.5) under the owner's direction. A human should review before merge.

Changes

  • first-mate-wait.sh: flock on FIRST_MATE_DIR/.wait.lock, keyed on PASEO_AGENT_ID.
    • A second start never stacks: exit 4 if you own the running loop, 5 if another agent does.
    • New status, stop [--force], and take-over. The lock frees itself when the loop dies, so there is no stale pidfile.
    • The detector runs in the background so stop works mid-run.
    • Callers wait (up to about 5s) for the owner file to name a live loop before deciding, so a start, status, or stop that lands on a starting loop or a lock probe does not misreport it.
    • Uses ps, not /proc, so it runs on macOS. Needs flock (util-linux, or brew install flock).
  • first-mate-check.py: branches and workflows watch entries; atomic state write; unregistered repos keep their full OWNER/repo.
  • SKILL.md (1.3.0): invariant 12 (one coordinator per effort; take-over and stop --force need the user's explicit word), fork-aware adoption steps, fresh-base rule, verified agent claims, runtime acceptance signal, mutation proof, post-merge proof, repo PR standard overrides the template, Pending Decisions section, shape-only host reads, drafts for human threads, cleanup on merge, new troubleshooting rows.
  • scripts/test-wait-lock (new, run by scripts/verify), README, TROUBLESHOOTING, and an update to decision record 0001.

Verification

  • CI: passes at 7a8492c.
  • scripts/verify: 21 of 21 on the author's macOS host (including shellcheck and acq kit validate); 20 of 20 on Linux, five runs in a row (acq not installed there).
  • scripts/test-wait-lock: 26 assertions, with a watchdog (default 120s, FIRST_MATE_TEST_WATCHDOG) so a regression fails instead of hanging.
  • Each of these makes the test fail, then pass again once restored:
    • disabling the lock check;
    • removing the owner check in stop;
    • the earlier command-substitution detector call (slow-detector case);
    • the earlier script without the wait for a published owner (slow-start case);
    • the earlier start without the retry (lock-probe case).
  • Review history: two read-only adversarial passes before the first push, then CI and host runs found and led to fixes for a startup race, a start-versus-probe race, a macOS /proc hang, and two shellcheck warnings. Each fix has a regression test.
  • Live read-only check of the new branch and workflow watchers against this repo's main and ci.yml.
  • Not verified: a live multi-agent fork run. The PASEO_AGENT_ID keying is exercised only with injected owner names.

Merge Guard

None. Standalone change.

Rollback

Revert the merge. Kit consumers pin by SHA, so existing pins are unaffected.

Security Impact

No new network access, secrets, or commands. stop kills a process only after checking that the recorded PID's command line contains first-mate-wait.sh.

mogul added 12 commits October 31, 2026 08:41
The wait script now holds an flock on FIRST_MATE_DIR/.wait.lock keyed on the
caller's PASEO_AGENT_ID. Starting is idempotent (exit 4 if you own the running
loop, 5 if another agent does), and status, stop, and take-over subcommands
are added. The skill treats a resumed or forked session as a non-coordinator
until the user says otherwise, and never reports a loop as running without a
status result from the same turn.

scripts/test-wait-lock covers the lock offline with a stub detector and is run
from scripts/verify.
The detector can now watch a branch (each new commit) and a workflow (latest
run on a branch: head, status, conclusion), alongside PRs, issues, and
releases. A PR-only watch list misses work that lands from other sessions and
a deploy that stays red after a merge. The skill says to always watch the
default branch and the workflows the milestone depends on, and to reconcile
open recommendations against any merge a branch report shows.
New workspaces are created from a freshly fetched origin/<default> with the
base SHA recorded in the prompt and re-checked by the agent. Coordinator
reports must verify agent claims about the baseline (fails on main, CI
started, no conflicts) before repeating them, and must read a finish notice
against the agent's status and the latest prompt sent.
…st-merge proof

A green pipeline does not show the deployed thing runs, so the acceptance
signal must be a named runtime observable, and changes that can only be
proven by the first run after merge stay open until it is watched and
observed. New guards need a mutation proof stating what the test does not
cover.
…he handoff

The seven-section PR template is now only a fallback. The target repository's
own PR standard (prefixes, closing keywords, attribution, sections) takes
precedence and goes into every delegation prompt, including a newer standard
the user points at. The handoff gains a Pending Decisions and Held Items
section recording recommendations with their triggers, and records standing
answers to routine permission prompts.
…ds, cleanup on merge

Adds the remaining working rules: ask the user for shape-only output when
reading live systems, show a draft and options before posting on a human's
thread, cite the log line or file:line when naming a cause, report checks a
sandbox could not run instead of skipping them, apply recorded permission
policies, and retire an agent, workspace, watch entry and exact-match local
branch when a PR merges.
…; clearer mechanisms

Adversarial review findings:
- first-mate-wait.sh ran the detector in a command substitution, so SIGTERM
  was deferred until its network calls finished and stop/take-over timed out.
  The detector now runs in the background and is waited on, and the cleanup
  trap kills it. The owner file is written then renamed so status and stop
  never read a partial file. A test with a slow stub detector fails on the
  previous script and passes now.
- first-mate-check.py stored a bare repo name for unregistered repos, which
  later failed to resolve and aborted the whole run; it now keeps the full
  OWNER/repo. The state file is written atomically.
- SKILL.md: take-over and stop --force need the user's explicit word each time
  (invariant 12), and the heartbeat preference is folded into the heartbeat
  mechanism instead of reading as a third one.
stop verified the loop's pid through /proc, which macOS lacks, so stop failed
and the test's bare wait blocked forever on the loop it could not stop. The
script now uses ps -p -o args=. The test uses gtimeout when timeout is
missing, avoids pgrep -c (not on macOS), and has a watchdog (default 120s,
FIRST_MATE_TEST_WATCHDOG) so a regression fails instead of hanging. Also drops
an unused local flagged by shellcheck (SC2034).
…as not published its owner

CI failed in the take-over test. The loop takes the lock first and writes its
owner file after, so on a slow runner a start, status, or stop in that gap saw
a held lock with no owner (or a stale file from a SIGKILLed loop) and treated
the loop as someone else's or as unverifiable. Callers now wait up to about 5s
for an owner file naming a live first-mate-wait.sh process before deciding, and
fall back to none if the loop dies meanwhile.

The test gains a slow-starting-loop case (lock held, owner file delayed, stale
file present). It fails on the previous script and passes now, including with
the owner write delayed 1s everywhere and under CPU load.
…status probe

shellcheck (host and CI) flagged an unused loop variable in await_info; it is
now `_`. The local shellcheck install also made the gap visible: scripts/verify
failed intermittently in the locking test.

Cause: status and stop probe the lock by briefly taking it, so a start that
runs at the same instant failed flock -n and was refused as if another owner
held a loop. cmd_start now retries until the owner file names a live loop, the
lock is free, or about 5s pass. A test holds the lock for 1s with no owner file
and starts in the middle; it fails on the previous script and passes now.
@mogul
mogul merged commit 103885d into main Oct 31, 2026
10 checks passed
@mogul
mogul deleted the feat/first-mate-1.3.0 branch November 4, 2026 20:46
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