From 61e99d65cec3d2d76ad740f194d1f26f8d007dfa Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 16:24:19 -0700 Subject: [PATCH 1/6] docs: make stow inspect-then-update --- .agents/skills/stow/SKILL.md | 19 ++++++++++++++----- AGENTS.md | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.agents/skills/stow/SKILL.md b/.agents/skills/stow/SKILL.md index bab4d4d692b..da7546500e6 100644 --- a/.agents/skills/stow/SKILL.md +++ b/.agents/skills/stow/SKILL.md @@ -29,17 +29,26 @@ The goal is a session that is safe to reset or destroy because everything durabl 3. **Write within firstmate's existing write boundaries.** This skill does not grant any new write permission; it only prompts firstmate to use the boundaries that already exist (AGENTS.md section 1): - - Captain preferences and fleet-local operational facts: hand-write directly, to `data/captain.md` and `data/learnings.md` respectively. - `data/learnings.md` may not exist yet; create it on first learning, in the same dated, evidence-backed, curated style as `data/captain.md` - rewrite and prune stale or superseded entries rather than appending forever. + - Captain preferences and fleet-local operational facts: hand-write directly, to `data/captain.md` and `data/learnings.md` respectively, using inspect-then-update every time. + Before writing, inspect the destination, find the existing bullet or section the finding duplicates or supersedes, and rewrite it in place rather than adding a new trailing entry. + `data/learnings.md` may not exist yet; create it on first learning, in the same dated, evidence-backed, curated style as `data/captain.md`. - Project-intrinsic knowledge: never hand-write a project's `AGENTS.md`. Route it through a normal ship task so a crewmate records it via `bin/fm-ensure-agents-md.sh` and commits it through that project's delivery pipeline, exactly as section 6 describes. If the fleet is live, delegate this to a crewmate rather than doing it inline. - Knowledge generalizable to every firstmate user: this repo's own `AGENTS.md` (or other shared, tracked material), shipped through the normal branch -> no-mistakes -> PR -> captain-merge pipeline for this repo (section 1), never hand-committed straight to `main`. - - Task-scoped notes: append to the relevant backlog item's notes with `tasks-axi update --append ""`, or hand-edit `data/backlog.md` per the active backend (section 10). + - Task-scoped notes: inspect the relevant backlog item with `tasks-axi show --full`, judge whether the new note is new, duplicate, superseding, or obsolete, then write a considered replacement body with `tasks-axi update --body-file `. + Use `--archive-body` when the replacement intentionally supersedes prior state that should remain recoverable. + Never append. + If hand-editing `data/backlog.md` per the active backend, make the same inspect-then-update edit in place. - Undone next steps: file each as a queued backlog item (section 10), with `blocked-by` recorded if it genuinely depends on something else. -4. **Curate, don't just append.** - When a finding overlaps or supersedes something already on disk, prefer rewriting or pruning the existing entry over piling on a new one. +4. **Curate with inspect-then-update.** + Every write starts by reading the current destination and deciding how the finding changes what is already there. + Use this checklist before writing: + - Which existing bullet, section, or task body does this supersede? + - Can this be a one-sentence rewrite instead of a new entry? + - Should an older bullet or note be deleted, retired, or archived because it is now obsolete? + When a finding overlaps or supersedes something already on disk, rewrite or prune the existing entry instead of piling on a new one. Graduation moves are limited to exactly three: promote a learning to the shared `AGENTS.md` via PR, fold it into `data/captain.md`, or delete a stale entry. Do not invent other graduation paths. diff --git a/AGENTS.md b/AGENTS.md index 6ba527a6242..23442b50218 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -415,7 +415,7 @@ Route each piece of durable knowledge to its most specific home: | Project-intrinsic knowledge | that project's own `AGENTS.md`, via normal crewmate delivery, never hand-written by firstmate | | Fleet-local operational facts and gotchas | `data/learnings.md` | | Knowledge generalizable to every firstmate user | the shared `AGENTS.md`, shipped via PR through the pipeline | -| Task-scoped notes | backlog item notes (`tasks-axi update --append ""`, or hand-edit per the active backend) | +| Task-scoped notes | backlog item notes, inspect first with `tasks-axi show --full`, then replace the body with `tasks-axi update --body-file ` or hand-edit per the active backend | | Investigation findings | scout reports at `data//report.md` | When the captain invokes `/stow`, load the `stow` skill. @@ -846,7 +846,7 @@ Map firstmate's real backlog operations to the approved commands: - File an item: `tasks-axi add "" --kind --repo `, plus `--start` for immediate dispatch (In flight) or the default queue placement, and `--blocked-by ` (repeatable) when it waits on another task. - Start an existing queued item: `tasks-axi start ` before dispatching work from Queued, after checking that blockers are gone and any time/date gate has arrived. - Move a finished task to Done: `tasks-axi done --pr ` for a PR-based ship, `--report ` for a scout, or `--note "local main"` for a local-only merge. -- Append a status note: `tasks-axi update --append ""`; replace fields with `--title`, `--body`, or `--body-file `. +- Update task notes: inspect first with `tasks-axi show --full`, then replace the considered body with `tasks-axi update --body-file `, adding `--archive-body` when superseding prior state should remain recoverable. - Manage dependencies: `tasks-axi block --by ` and `tasks-axi unblock --by `, then `tasks-axi ready` to list queued work with no unresolved blockers. This is a dependency check only; future-dated items still stay queued until their date arrives. - Read an item's full notes: `tasks-axi show --full`. From a41c33974881f16c918498508d9da9215d72e93a Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 16:31:01 -0700 Subject: [PATCH 2/6] no-mistakes(review): Remove unsupported archive-body guidance --- .agents/skills/stow/SKILL.md | 2 +- AGENTS.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.agents/skills/stow/SKILL.md b/.agents/skills/stow/SKILL.md index da7546500e6..63a49e89367 100644 --- a/.agents/skills/stow/SKILL.md +++ b/.agents/skills/stow/SKILL.md @@ -37,7 +37,7 @@ The goal is a session that is safe to reset or destroy because everything durabl If the fleet is live, delegate this to a crewmate rather than doing it inline. - Knowledge generalizable to every firstmate user: this repo's own `AGENTS.md` (or other shared, tracked material), shipped through the normal branch -> no-mistakes -> PR -> captain-merge pipeline for this repo (section 1), never hand-committed straight to `main`. - Task-scoped notes: inspect the relevant backlog item with `tasks-axi show --full`, judge whether the new note is new, duplicate, superseding, or obsolete, then write a considered replacement body with `tasks-axi update --body-file `. - Use `--archive-body` when the replacement intentionally supersedes prior state that should remain recoverable. + When the replacement intentionally supersedes prior state that should remain recoverable, carry that context into the replacement body before updating. Never append. If hand-editing `data/backlog.md` per the active backend, make the same inspect-then-update edit in place. - Undone next steps: file each as a queued backlog item (section 10), with `blocked-by` recorded if it genuinely depends on something else. diff --git a/AGENTS.md b/AGENTS.md index 23442b50218..d3bd643a039 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -846,7 +846,8 @@ Map firstmate's real backlog operations to the approved commands: - File an item: `tasks-axi add "" --kind --repo `, plus `--start` for immediate dispatch (In flight) or the default queue placement, and `--blocked-by ` (repeatable) when it waits on another task. - Start an existing queued item: `tasks-axi start ` before dispatching work from Queued, after checking that blockers are gone and any time/date gate has arrived. - Move a finished task to Done: `tasks-axi done --pr ` for a PR-based ship, `--report ` for a scout, or `--note "local main"` for a local-only merge. -- Update task notes: inspect first with `tasks-axi show --full`, then replace the considered body with `tasks-axi update --body-file `, adding `--archive-body` when superseding prior state should remain recoverable. +- Update task notes: inspect first with `tasks-axi show --full`, then replace the considered body with `tasks-axi update --body-file `. + When superseding prior state should remain recoverable, carry that context into the replacement body before updating. - Manage dependencies: `tasks-axi block --by ` and `tasks-axi unblock --by `, then `tasks-axi ready` to list queued work with no unresolved blockers. This is a dependency check only; future-dated items still stay queued until their date arrives. - Read an item's full notes: `tasks-axi show --full`. From 41923b3aa7b1f74974e25d9415ab09aa846a9d4a Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 16:34:38 -0700 Subject: [PATCH 3/6] no-mistakes(review): Clarify stow read-before-write exception --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index d3bd643a039..4b58486d9b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -140,6 +140,7 @@ It composes today's `fm-lock.sh`, `fm-bootstrap.sh`, and `fm-wake-drain.sh` - ca Do not separately run `bin/fm-bootstrap.sh`, `bin/fm-lock.sh`, or `bin/fm-wake-drain.sh`, and do not separately read `data/projects.md`, `data/secondmates.md`, `data/captain.md`, `data/learnings.md`, `data/backlog.md`, or any `state/*.meta` afterward - they were just printed in full, and re-reading them defeats the entire point of collapsing session start into one command. Do not bulk-read `state/*.status` afterward either: the digest printed bounded tails with full log paths for targeted follow-up when older wake-event history is actually needed. Re-read a file only if the digest flagged it `ABSENT` (then rebuild or create it per the guidance in this section and section 6), its contents looked unparseable or corrupt, or an individual full status log is needed for older wake-event history. +This read-once rule does not block a targeted current-state read immediately before a workflow writes one of these files, such as `/stow`'s inspect-then-update pass or a backlog backend mutation. Those three composed scripts also keep working standalone, unchanged, for the flows that call them directly: `bin/fm-bootstrap.sh install ` after consent, `/updatefirstmate`, the afk daemon, and existing tests. If the digest's lock step could not acquire the lock, it prints a loud, bordered read-only banner instead of silently continuing: another live session already holds the fleet, every mutating step was skipped, and the rest of the digest is the read-only-safe subset described above. From 19d09e814824f3569e1f8b0d632d8823b1dbdd53 Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 17:08:20 -0700 Subject: [PATCH 4/6] no-mistakes(test): Require archive-body for stow task notes --- .agents/skills/stow/SKILL.md | 2 +- AGENTS.md | 10 +++++----- bin/fm-bootstrap.sh | 5 +++-- bin/fm-tasks-axi-lib.sh | 19 +++++++++++++++---- docs/cmux-backend.md | 2 +- docs/configuration.md | 4 ++-- docs/herdr-backend.md | 2 +- docs/orca-backend.md | 2 +- docs/tmux-backend.md | 2 +- docs/zellij-backend.md | 2 +- tests/fm-bootstrap.test.sh | 28 +++++++++++++++++++++++----- tests/fm-stow-contract.test.sh | 30 ++++++++++++++++++++++++++++++ tests/fm-teardown.test.sh | 7 +++++++ 13 files changed, 91 insertions(+), 24 deletions(-) create mode 100755 tests/fm-stow-contract.test.sh diff --git a/.agents/skills/stow/SKILL.md b/.agents/skills/stow/SKILL.md index 63a49e89367..c35175f1bc9 100644 --- a/.agents/skills/stow/SKILL.md +++ b/.agents/skills/stow/SKILL.md @@ -37,7 +37,7 @@ The goal is a session that is safe to reset or destroy because everything durabl If the fleet is live, delegate this to a crewmate rather than doing it inline. - Knowledge generalizable to every firstmate user: this repo's own `AGENTS.md` (or other shared, tracked material), shipped through the normal branch -> no-mistakes -> PR -> captain-merge pipeline for this repo (section 1), never hand-committed straight to `main`. - Task-scoped notes: inspect the relevant backlog item with `tasks-axi show --full`, judge whether the new note is new, duplicate, superseding, or obsolete, then write a considered replacement body with `tasks-axi update --body-file `. - When the replacement intentionally supersedes prior state that should remain recoverable, carry that context into the replacement body before updating. + When the replacement intentionally supersedes prior state that should remain recoverable, add `--archive-body` to that update command so the prior body stays recoverable without copying it into the replacement. Never append. If hand-editing `data/backlog.md` per the active backend, make the same inspect-then-update edit in place. - Undone next steps: file each as a queued backlog item (section 10), with `blocked-by` recorded if it genuinely depends on something else. diff --git a/AGENTS.md b/AGENTS.md index 4b58486d9b8..2f63a768c8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -165,7 +165,7 @@ Otherwise it prints one line per problem or capability fact; handle each: - `MISSING: (install: )` - list the missing tools to the captain with a one-line purpose each plus the printed install commands, wait for consent (one approval may cover the list), then run `bin/fm-bootstrap.sh install `. For `treehouse`, this also covers an installed version whose `treehouse get` lacks `--lease`; treat it as an upgrade request. For `no-mistakes`, this also covers an installed version older than 1.31.2, because crewmate validation briefs delegate gate mechanics to no-mistakes' version-matched guidance. - For `tasks-axi`, this also covers an installed build whose `tasks-axi --version` is older than 0.1.1; `config/backlog-backend=manual` only suppresses the `TASKS_AXI: available` capability line, not this missing-tool report. + For `tasks-axi`, this also covers an installed build whose `tasks-axi --version` is older than 0.1.1 or whose `tasks-axi update --help` lacks `--archive-body`; `config/backlog-backend=manual` only suppresses the `TASKS_AXI: available` capability line, not this missing-tool report. For `quota-axi`, bootstrap requires it because crew-dispatch `quota-balanced` may call it; `bin/fm-dispatch-select.sh` still degrades at runtime when quota data is unavailable. - `NEEDS_GH_AUTH` - ask the captain to run `! gh auth login` (interactive; you cannot run it for them). - `TANGLE: ` - the primary checkout is stranded on a feature branch instead of its default branch; section 8 explains why this guard exists and what it protects. @@ -182,7 +182,7 @@ Otherwise it prints one line per problem or capability fact; handle each: - `SECONDMATE_LIVENESS: secondmate : already-live|respawned|skipped: |respawn failed: ` - the session-start liveness sweep checked a live secondmate's recorded endpoint for a real agent process. Treat `already-live` and `respawned` as handled; investigate `skipped` or `respawn failed` because that secondmate is not guaranteed live. - `TASKS_AXI: available` - a default-backend capability fact, not a problem; record it silently and use section 10 for backlog mutations. - It prints only when `config/backlog-backend` is absent or set to `tasks-axi` and the compatibility probe accepts `tasks-axi --version` as 0.1.1 or newer. + It prints only when `config/backlog-backend` is absent or set to `tasks-axi` and the compatibility probe accepts `tasks-axi --version` as 0.1.1 or newer plus `tasks-axi update --help` exposing `--archive-body`. If the backend is not opted out and `tasks-axi` is missing or incompatible, bootstrap reports `MISSING: tasks-axi (install: npm install -g tasks-axi)` but still falls back to hand-editing and never blocks work. If `config/backlog-backend=manual`, bootstrap hand-edits and does not suggest installing `tasks-axi`. - `NUDGE_SECONDMATES: fm-...` - the secondmate sweep fast-forwarded one or more *running* secondmate homes to firstmate's current version and their instruction surface (`AGENTS.md`, `bin/`, or `.agents/skills/`) actually changed; send a one-line re-read nudge with `FM_HOME= bin/fm-send.sh 'firstmate was updated to the latest - please re-read your AGENTS.md to pick up the new instructions.'` unless `FM_HOME` is already set to the active firstmate home. @@ -416,7 +416,7 @@ Route each piece of durable knowledge to its most specific home: | Project-intrinsic knowledge | that project's own `AGENTS.md`, via normal crewmate delivery, never hand-written by firstmate | | Fleet-local operational facts and gotchas | `data/learnings.md` | | Knowledge generalizable to every firstmate user | the shared `AGENTS.md`, shipped via PR through the pipeline | -| Task-scoped notes | backlog item notes, inspect first with `tasks-axi show --full`, then replace the body with `tasks-axi update --body-file ` or hand-edit per the active backend | +| Task-scoped notes | backlog item notes, inspect first with `tasks-axi show --full`, then replace the body with `tasks-axi update --body-file `, adding `--archive-body` when superseded prior state should remain recoverable, or hand-edit per the active backend | | Investigation findings | scout reports at `data//report.md` | When the captain invokes `/stow`, load the `stow` skill. @@ -831,7 +831,7 @@ Re-evaluate Queued on every teardown and every heartbeat: anything whose blocker A tracked `.tasks.toml` at this repo root pins the default `tasks-axi` markdown backend to `data/backlog.md`, with `done_keep = 10` and an archive at `data/done-archive.md`. The local, gitignored `config/backlog-backend` file is the explicit opt-out knob. Absent or `tasks-axi` means use the default tasks-axi backend; `manual` means force hand-editing even when `tasks-axi` is installed. -Compatible means the shared bootstrap probe accepts `tasks-axi --version` as 0.1.1 or newer. +Compatible means the shared bootstrap probe accepts `tasks-axi --version` as 0.1.1 or newer and `tasks-axi update --help` exposes `--archive-body`. When the default backend is selected and compatible `tasks-axi` is on PATH, firstmate mutates the backlog through its verbs instead of hand-editing, with secondmate handoffs still going through the validated helper described in section 6. When the default backend is selected but `tasks-axi` is missing or incompatible, bootstrap reports it through the normal `MISSING:` consent flow in `docs/configuration.md` "Toolchain", and every firstmate home falls back to hand-editing `data/backlog.md` exactly as this section describes until it is installed. When `config/backlog-backend=manual`, every firstmate home hand-edits; bootstrap still requires compatible `tasks-axi` on `PATH` but does not print `TASKS_AXI: available`. @@ -848,7 +848,7 @@ Map firstmate's real backlog operations to the approved commands: - Start an existing queued item: `tasks-axi start ` before dispatching work from Queued, after checking that blockers are gone and any time/date gate has arrived. - Move a finished task to Done: `tasks-axi done --pr ` for a PR-based ship, `--report ` for a scout, or `--note "local main"` for a local-only merge. - Update task notes: inspect first with `tasks-axi show --full`, then replace the considered body with `tasks-axi update --body-file `. - When superseding prior state should remain recoverable, carry that context into the replacement body before updating. + Add `--archive-body` to that update command when superseding prior state should remain recoverable. - Manage dependencies: `tasks-axi block --by ` and `tasks-axi unblock --by `, then `tasks-axi ready` to list queued work with no unresolved blockers. This is a dependency check only; future-dated items still stay queued until their date arrives. - Read an item's full notes: `tasks-axi show --full`. diff --git a/bin/fm-bootstrap.sh b/bin/fm-bootstrap.sh index da140d2c30c..b6bec1e4efd 100755 --- a/bin/fm-bootstrap.sh +++ b/bin/fm-bootstrap.sh @@ -44,8 +44,9 @@ # no-mistakes is also MISSING when its installed version is older than # 1.31.2. # tasks-axi and quota-axi are required bootstrap tools (same class as -# lavish-axi). tasks-axi is also version-gated (0.1.1+); an installed -# but incompatible build reports MISSING like no-mistakes. When +# lavish-axi). tasks-axi is also version and feature gated (0.1.1+ +# with update --archive-body); an installed but incompatible build +# reports MISSING like no-mistakes. When # config/backlog-backend is not manual and tasks-axi is compatible, # bootstrap prints TASKS_AXI: available. quota-axi is required because # crew-dispatch quota-balanced may call it; fm-dispatch-select.sh still diff --git a/bin/fm-tasks-axi-lib.sh b/bin/fm-tasks-axi-lib.sh index 455a406c9a0..2df69dde18b 100644 --- a/bin/fm-tasks-axi-lib.sh +++ b/bin/fm-tasks-axi-lib.sh @@ -2,7 +2,8 @@ # Shared tasks-axi backend selection and compatibility probe for bootstrap and # teardown. # Usage: . bin/fm-tasks-axi-lib.sh -# Compatible means tasks-axi --version reports 0.1.1 or newer. +# Compatible means tasks-axi --version reports 0.1.1 or newer and +# `tasks-axi update --help` exposes --archive-body for recoverable note rewrites. # `config/backlog-backend=manual` opts out of tasks-axi backlog mutations; # absent or any other value keeps the default tasks-axi backend path, falling # back to manual mutation when the tool is not compatible. @@ -25,12 +26,22 @@ fm_tasks_axi_compatible() { minor=${rest%% *} patch=${rest##* } - [ "$major" -gt 0 ] && return 0 - [ "$major" -eq 0 ] && [ "$minor" -gt 1 ] && return 0 - [ "$major" -eq 0 ] && [ "$minor" -eq 1 ] && [ "$patch" -ge 1 ] && return 0 + if [ "$major" -gt 0 ] || + { [ "$major" -eq 0 ] && [ "$minor" -gt 1 ]; } || + { [ "$major" -eq 0 ] && [ "$minor" -eq 1 ] && [ "$patch" -ge 1 ]; }; then + fm_tasks_axi_update_has_archive_body + return $? + fi return 1 } +fm_tasks_axi_update_has_archive_body() { + local output + command -v tasks-axi >/dev/null 2>&1 || return 1 + output=$(tasks-axi update --help 2>&1) || return 1 + printf '%s\n' "$output" | grep -F -- '--archive-body' >/dev/null +} + fm_backlog_backend_value() { local config_dir=$1 backend_file value backend_file="$config_dir/backlog-backend" diff --git a/docs/cmux-backend.md b/docs/cmux-backend.md index d9cc0110ac5..a147319ffeb 100644 --- a/docs/cmux-backend.md +++ b/docs/cmux-backend.md @@ -17,7 +17,7 @@ Prerequisites: - The cmux app itself, installed from [cmux.com](https://cmux.com) or `brew install --cask cmux`, version 0.64.17 or newer. - `jq`, required to parse cmux's JSON output: `brew install jq` (or your platform's package manager). -- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer, and quota-axi); treehouse still provides the worktree, cmux only provides the session. +- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer with `update --archive-body`, and quota-axi); treehouse still provides the worktree, cmux only provides the session. - The cmux CLI binary is not guaranteed to be on `PATH` after a plain app install (see "CLI is not on PATH by default" below) - the adapter falls back to the well-known bundle path automatically, so this is not a blocker, just something to be aware of if you want to run `cmux` yourself from a shell. **One-time socket access setup (required, not optional):** cmux's control socket defaults to `automation.socketControlMode: "cmuxOnly"`, which rejects any CLI process not spawned inside cmux itself - firstmate always drives cmux from an external shell, so this must be changed before `backend=cmux` can work at all. diff --git a/docs/configuration.md b/docs/configuration.md index 45be2cc613b..a8c404c82fd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -10,7 +10,7 @@ The shared orchestrator behavior lives in [`AGENTS.md`](../AGENTS.md) - edit it The tracked `.tasks.toml` pins the default `tasks-axi` markdown backend to `data/backlog.md`, with `done_keep = 10` and an archive at `data/done-archive.md`. When the default backend is selected and compatible `tasks-axi` is on `PATH`, firstmate uses its verbs for routine backlog mutations and keeps secondmate transfers behind `fm-backlog-handoff.sh` validation. -Compatible means the shared bootstrap probe accepts `tasks-axi --version` as 0.1.1 or newer. +Compatible means the shared bootstrap probe accepts `tasks-axi --version` as 0.1.1 or newer and `tasks-axi update --help` exposes `--archive-body`. Bootstrap requires compatible `tasks-axi` on every profile; see "Toolchain" below for missing-tool reporting and `TASKS_AXI: available` behavior. Set the local, gitignored `config/backlog-backend` file to `manual` to force manual backlog editing and suppress `TASKS_AXI: available`, not missing-tool reporting. Absent or `tasks-axi` selects the default tasks-axi backend. @@ -153,7 +153,7 @@ Secondmate homes inherit this file from the primary, so a secondmate's own crewm ## Toolchain -On session start the first mate detects what its required toolchain is missing or too old (tmux, node, gh, treehouse with durable lease support, no-mistakes v1.31.2 or newer, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer, and quota-axi), lists it with the exact install commands, and installs only after you say go. +On session start the first mate detects what its required toolchain is missing or too old (tmux, node, gh, treehouse with durable lease support, no-mistakes v1.31.2 or newer, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer with `update --archive-body`, and quota-axi), lists it with the exact install commands, and installs only after you say go. When bootstrap resolves `backend=orca` from `FM_BACKEND` or `config/backend`, it requires `orca`, keeps the universal `node` requirement, and skips `tmux` and `treehouse`. When `config/crew-dispatch.json` exists, bootstrap also requires `jq` for dispatch profile validation. When X mode is opted in, bootstrap also requires `curl` and `jq` before arming the relay poll shim. diff --git a/docs/herdr-backend.md b/docs/herdr-backend.md index 298e553f352..05b905a2b64 100644 --- a/docs/herdr-backend.md +++ b/docs/herdr-backend.md @@ -20,7 +20,7 @@ Prerequisites: - `herdr` itself, protocol 14 or newer (installed 0.7.1 verified) - see [herdr.dev](https://herdr.dev) for install instructions. - `jq`, required to parse herdr's JSON output: `brew install jq` (or your platform's package manager). -- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer, and quota-axi); treehouse still provides the worktree, herdr only provides the session. +- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer with `update --archive-body`, and quota-axi); treehouse still provides the worktree, herdr only provides the session. Select herdr by putting `herdr` in a local `config/backend` file - the durable way to pick it - or by exporting `FM_BACKEND=herdr` when you launch your harness for a one-off session; telling the first mate in chat to use herdr also works. It can also be auto-detected: when firstmate itself is running natively inside herdr (`HERDR_ENV=1`) and no explicit backend is set, firstmate auto-selects herdr and prints a one-time opt-out notice; running inside tmux nested in herdr always resolves to tmux instead. diff --git a/docs/orca-backend.md b/docs/orca-backend.md index d02e95f8fbb..1be1998fd57 100644 --- a/docs/orca-backend.md +++ b/docs/orca-backend.md @@ -14,7 +14,7 @@ Prerequisites: - The Orca app installed at `/Applications/Orca.app`, and **running**. - The `orca` CLI: `brew install orca`. - `node`, used by firstmate's adapter to parse Orca's JSON output and to gate spawns on runtime readiness. -- `git` with GitHub auth, `no-mistakes`, `gh-axi`, `chrome-devtools-axi`, `lavish-axi`, `tasks-axi` 0.1.1 or newer, and `quota-axi` - the same universal requirements as tmux, minus `tmux` and `treehouse` (Orca replaces both). +- `git` with GitHub auth, `no-mistakes`, `gh-axi`, `chrome-devtools-axi`, `lavish-axi`, `tasks-axi` 0.1.1 or newer with `update --archive-body`, and `quota-axi` - the same universal requirements as tmux, minus `tmux` and `treehouse` (Orca replaces both). Select Orca by putting `orca` in a local `config/backend` file - the durable way to pick it - or by exporting `FM_BACKEND=orca` when you launch your harness for a one-off session; telling the first mate in chat to use Orca also works. It is never auto-detected. diff --git a/docs/tmux-backend.md b/docs/tmux-backend.md index 8ce20decabf..b4b7f6a1b10 100644 --- a/docs/tmux-backend.md +++ b/docs/tmux-backend.md @@ -15,7 +15,7 @@ Pick tmux unless you have a specific reason to try an experimental backend (herd - A verified crew harness: `claude`, `codex`, `opencode`, `pi`, or `grok`. - `git` with GitHub auth (`gh auth login`). - `node`, required by firstmate's universal toolchain. -- `treehouse` for pooling clean worktrees; `no-mistakes` for the validation pipeline; `gh-axi`, `chrome-devtools-axi`, and `lavish-axi` for GitHub, browser, and rich-review operations; `tasks-axi` 0.1.1 or newer and `quota-axi` for bootstrap-managed backlog and dispatch support. +- `treehouse` for pooling clean worktrees; `no-mistakes` for the validation pipeline; `gh-axi`, `chrome-devtools-axi`, and `lavish-axi` for GitHub, browser, and rich-review operations; `tasks-axi` 0.1.1 or newer with `update --archive-body` and `quota-axi` for bootstrap-managed backlog and dispatch support. The first mate detects missing tools at session start and offers to install them after you approve. diff --git a/docs/zellij-backend.md b/docs/zellij-backend.md index f2546174f5f..3ec38439eff 100644 --- a/docs/zellij-backend.md +++ b/docs/zellij-backend.md @@ -15,7 +15,7 @@ Prerequisites: - `zellij` itself, version 0.44 or newer (installed 0.44.0 verified) - see [zellij.dev](https://zellij.dev) for install instructions. - `jq`, required to parse zellij's JSON output: `brew install jq` (or your platform's package manager). -- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer, and quota-axi); treehouse still provides the worktree, zellij only provides the session. +- The same universal requirements as tmux (a verified crew harness, git with GitHub auth, node, treehouse, no-mistakes, gh-axi, chrome-devtools-axi, lavish-axi, tasks-axi 0.1.1 or newer with `update --archive-body`, and quota-axi); treehouse still provides the worktree, zellij only provides the session. Select zellij by putting `zellij` in a local `config/backend` file - the durable way to pick it - or by exporting `FM_BACKEND=zellij` when you launch your harness for a one-off session; telling the first mate in chat to use zellij also works. Unlike tmux and herdr, zellij is **never** auto-detected - it always requires an explicit choice. diff --git a/tests/fm-bootstrap.test.sh b/tests/fm-bootstrap.test.sh index f1d1bf0681f..f828ba7f501 100755 --- a/tests/fm-bootstrap.test.sh +++ b/tests/fm-bootstrap.test.sh @@ -7,8 +7,9 @@ # 'TASKS_AXI: available' lines, so those contracts are pinned verbatim. The cases # are table-driven over the inputs that vary: whether `treehouse get --help` # advertises --lease, which (if any) tasks-axi version is on PATH, whether -# quota-axi is on PATH, whether the local backend config opts out of tasks-axi -# backlog mutations, and which no-mistakes version is on PATH. +# tasks-axi update advertises --archive-body, whether quota-axi is on PATH, +# whether the local backend config opts out of tasks-axi backlog mutations, and +# which no-mistakes version is on PATH. # Dedicated fleet-sync cases pin the computed bootstrap timeout, explicit # override, blank-env defaulting, partial-output relay, and pre-launch timeout # scan. @@ -71,11 +72,20 @@ SH } add_tasks_axi() { - local fakebin=$1 version=$2 + local fakebin=$1 version=$2 archive_body=${3:-yes} archive_line + archive_line="" + [ "$archive_body" = yes ] && archive_line=' --archive-body' cat > "$fakebin/tasks-axi" < [flags]' + printf '%s\n' ' --body-file ' + [ -z '$archive_line' ] || printf '%s\n' '$archive_line' + exit 0 fi exit 0 SH @@ -187,7 +197,7 @@ run_bootstrap_timeout_case() { # mode=exact -> output must equal # mode=grep -> output must contain (fixed string); must not appear test_bootstrap_reporting() { - local label lease tasks quota backend mode expect notcontains case_dir fakebin out n + local label lease tasks quota backend mode expect notcontains case_dir fakebin out n archive_body n=0 while IFS='^' read -r label lease tasks quota backend mode expect notcontains; do [ -n "$label" ] || continue @@ -202,7 +212,14 @@ test_bootstrap_reporting() { if [ "$tasks" = "-" ]; then rm -f "$fakebin/tasks-axi" else - add_tasks_axi "$fakebin" "$tasks" + archive_body=yes + case "$tasks" in + *:noarchive) + archive_body=no + tasks=${tasks%:noarchive} + ;; + esac + add_tasks_axi "$fakebin" "$tasks" "$archive_body" fi if [ "$quota" = "0" ]; then rm -f "$fakebin/quota-axi" @@ -230,6 +247,7 @@ treehouse without --lease reports an upgrade, gh auth is fine^0^0.1.1^1^-^grep^M compatible tasks-axi is reported available by default^1^0.1.1^1^-^exact^TASKS_AXI: available^ missing tasks-axi is required by default^1^-^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^ incompatible tasks-axi is required by default^1^0.1.0^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^ +tasks-axi without archive-body is required by default^1^0.1.2:noarchive^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^ missing quota-axi is required by default^1^0.1.1^0^manual^exact^MISSING: quota-axi (install: npm install -g quota-axi)^ manual backlog backend still requires missing tasks-axi^1^-^1^manual^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^ manual backlog backend suppresses tasks-axi availability^1^0.1.1^1^manual^empty^^ diff --git a/tests/fm-stow-contract.test.sh b/tests/fm-stow-contract.test.sh new file mode 100755 index 00000000000..ee724d008fc --- /dev/null +++ b/tests/fm-stow-contract.test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Behavior tests for /stow's inspect-then-update memory contract. +set -u + +# shellcheck source=tests/lib.sh disable=SC1091 +. "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +test_stow_skill_task_note_contract() { + local stow="$ROOT/.agents/skills/stow/SKILL.md" + + assert_grep 'tasks-axi show --full' "$stow" "stow skill does not require inspecting task notes first" + assert_grep 'tasks-axi update --body-file ' "$stow" "stow skill does not require task body replacement" + assert_grep '--archive-body' "$stow" "stow skill does not document recoverable task body archival" + assert_grep 'Never append.' "$stow" "stow skill does not forbid append-first task notes" + assert_no_grep 'carry that context into the replacement body' "$stow" "stow skill still preserves archive-only context in the replacement body" + pass "stow skill task-note contract includes recoverable body archival" +} + +test_agents_backlog_task_note_contract() { + local agents="$ROOT/AGENTS.md" + + assert_grep 'tasks-axi show --full' "$agents" "AGENTS.md does not require inspecting task notes first" + assert_grep 'tasks-axi update --body-file ' "$agents" "AGENTS.md does not require task body replacement" + assert_grep '--archive-body' "$agents" "AGENTS.md does not document recoverable task body archival" + assert_no_grep 'carry that context into the replacement body' "$agents" "AGENTS.md still preserves archive-only context in the replacement body" + pass "AGENTS.md task-note contract includes recoverable body archival" +} + +test_stow_skill_task_note_contract +test_agents_backlog_task_note_contract diff --git a/tests/fm-teardown.test.sh b/tests/fm-teardown.test.sh index 70d5894878c..67da80f8e22 100755 --- a/tests/fm-teardown.test.sh +++ b/tests/fm-teardown.test.sh @@ -131,6 +131,13 @@ add_compatible_tasks_axi() { #!/usr/bin/env bash if [ "${1:-}" = --version ]; then printf '%s\n' '0.1.1' + exit 0 +fi +if [ "${1:-}" = update ] && [ "${2:-}" = --help ]; then + printf '%s\n' 'usage: tasks-axi update [flags]' + printf '%s\n' ' --body-file ' + printf '%s\n' ' --archive-body' + exit 0 fi exit 0 SH From 2b4068f5c7885f00b1598f4c3cc045979b89e9ac Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 17:27:53 -0700 Subject: [PATCH 5/6] no-mistakes(document): Sync stow memory docs --- AGENTS.md | 8 ++++---- CONTRIBUTING.md | 1 + docs/architecture.md | 2 ++ docs/configuration.md | 3 ++- docs/scripts.md | 4 ++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2f63a768c8d..b2c53d4d2f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,8 +83,8 @@ config/cmux-socket-password optional cmux control-socket password; LOCAL, gitig config/x-mode.env generated X-mode watcher cadence; LOCAL, gitignored; source before arming watcher when present data/ personal fleet records; LOCAL, gitignored as a whole backlog.md task queue, dependencies, history - captain.md captain's curated personal preferences and working style; LOCAL, gitignored, and canonical even if harness memory mirrors it - learnings.md fleet-local operational facts and gotchas; LOCAL, gitignored; dated, evidence-backed, curated - rewrite and prune rather than append forever, the same contract as captain.md; created lazily, absent until this home has a learning to store + captain.md captain's personal preferences and working style; LOCAL, gitignored, canonical even if harness memory mirrors it, and updated with inspect-then-update + learnings.md fleet-local operational facts and gotchas; LOCAL, gitignored; dated, evidence-backed, curated, and updated with inspect-then-update - rewrite and prune rather than append forever, the same contract as captain.md; created lazily, absent until this home has a learning to store projects.md thin fleet navigation registry; firstmate-private, parsed by fm-project-mode.sh (section 6) secondmates.md secondmate routing table; firstmate-private, maintained by fm-home-seed.sh (section 6) /brief.md per-task crewmate brief, or per-secondmate charter brief when kind=secondmate @@ -412,9 +412,9 @@ Route each piece of durable knowledge to its most specific home: | Kind of knowledge | Home | | --- | --- | -| Captain preferences and working style | `data/captain.md` | +| Captain preferences and working style | `data/captain.md`, inspected first and rewritten or pruned in place | | Project-intrinsic knowledge | that project's own `AGENTS.md`, via normal crewmate delivery, never hand-written by firstmate | -| Fleet-local operational facts and gotchas | `data/learnings.md` | +| Fleet-local operational facts and gotchas | `data/learnings.md`, inspected first and rewritten or pruned in place | | Knowledge generalizable to every firstmate user | the shared `AGENTS.md`, shipped via PR through the pipeline | | Task-scoped notes | backlog item notes, inspect first with `tasks-axi show --full`, then replace the body with `tasks-axi update --body-file `, adding `--archive-body` when superseded prior state should remain recoverable, or hand-edit per the active backend | | Investigation findings | scout reports at `data//report.md` | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c877b394297..6e13b94efdf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,7 @@ See the [no-mistakes quick start](https://kunchenguid.github.io/no-mistakes/star `.agents/skills/` holds agent-loaded skills that assume a live firstmate home and carry `metadata.internal: true` so installers such as [skills.sh](https://skills.sh) hide them from discovery; `skills/` holds standalone, installer-facing public skills with no firstmate dependency (see the README's "Two-tier skill layout"). Everything personal to one captain's fleet (`.env`, `data/`, `state/`, `config/`, `projects/`, `.no-mistakes/`) is gitignored; never commit it. The root `.tasks.toml` is tracked `tasks-axi` config for `data/backlog.md`; compatible `tasks-axi` is the default backend for routine backlog mutations. + Compatible means version 0.1.1 or newer and `tasks-axi update --help` exposing `--archive-body`. A local `config/backlog-backend=manual` opt-out forces hand-editing and stays gitignored. A local `config/backend` file explicitly overrides runtime auto-detection for new task endpoints and stays gitignored; spawn-supported values are `tmux` plus experimental `herdr`, `zellij`, `orca`, and `cmux`, while `codex-app` is documented only in `docs/codex-app-backend.md`. It does not make `data/` tracked. diff --git a/docs/architecture.md b/docs/architecture.md index 6f448e7ac5b..bea24cc3913 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -196,6 +196,8 @@ The full ownership rule - what is project-intrinsic versus fleet-private, and ho `/stow` sweeps the current session for durable knowledge that only exists in conversation and routes each finding to the most specific disk home. Captain preferences go to `data/captain.md`, fleet-local operational facts and gotchas go to `data/learnings.md`, project-intrinsic knowledge goes through normal crewmate delivery into that project's committed `AGENTS.md`, and task-scoped notes or undone next steps go to the backlog. +Memory writes use inspect-then-update: read the current destination first, then rewrite or prune matching bullets or notes in place instead of appending by default. +Task-scoped notes use `tasks-axi show --full` followed by `tasks-axi update --body-file `, adding `--archive-body` when the prior body should remain recoverable. Generalizable firstmate knowledge goes to shared tracked docs through the normal PR pipeline; the firstmate-internal `/stow` deliberately never stores findings in either skill directory. ## Local clones stay fresh diff --git a/docs/configuration.md b/docs/configuration.md index a8c404c82fd..3cc09ca68b5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -79,11 +79,12 @@ It intentionally mirrors the behavior-test baseline in [`.github/workflows/ci.ym ## Captain preferences (data/captain.md) Personal preferences for one captain's fleet live locally in `data/captain.md`; it is gitignored and printed in the session-start context digest after `data/projects.md` and optional `data/secondmates.md`. +Before changing it, inspect the current file and rewrite or prune the matching bullet in place; add a new bullet only for a genuinely new durable preference. ## Operational learnings (data/learnings.md) Fleet-local operational facts and gotchas live locally in `data/learnings.md`; it is gitignored and printed right after `data/captain.md` in the session-start context digest. -The file is created lazily on first learning and follows the same dated, evidence-backed, curated style as `data/captain.md`: rewrite or prune stale entries instead of appending forever. +The file is created lazily on first learning and follows the same dated, evidence-backed, curated style as `data/captain.md`: inspect the current file first, then rewrite or prune stale entries instead of appending forever. ## Secondmate routes (data/secondmates.md) diff --git a/docs/scripts.md b/docs/scripts.md index 1b3596e9f10..c5e54bd471c 100644 --- a/docs/scripts.md +++ b/docs/scripts.md @@ -7,7 +7,7 @@ If you have changed away from the firstmate home in an interactive shell, invoke | Script | Description | | ------------------------ | ------------------------------------------------------------------------------------------------------------------- | | `fm-session-start.sh` | The one command AGENTS.md sections 3 and 5 run at every session start: composes `fm-lock.sh`, `fm-bootstrap.sh` (its four mutating sweeps gated on holding the lock via `FM_BOOTSTRAP_DETECT_ONLY`), and `fm-wake-drain.sh`, emits exactly one primary-harness supervision operating block, then prints a full context digest (`data/projects.md`, `data/secondmates.md`, `data/captain.md`, `data/learnings.md`, each `ABSENT`-marked when missing) and fleet-state digest (`data/backlog.md`, every `state/*.meta`, a bounded `state/*.status` tail, `state/.afk`, and a cheap per-task endpoint-liveness read); prints a loud read-only banner and skips every mutating step when the lock is held elsewhere; refreshes Pi's generated primary watcher extension before reporting whether both Pi primary extensions are loaded; never starts supervision itself | -| `fm-bootstrap.sh` | Detect required toolchain and version problems (including `tasks-axi`, `quota-axi`, and the other bootstrap AXI tools), dispatch profile JSON errors or active-rule blocks, default backlog-backend status, primary-checkout `TANGLE:` problems, and actionable clone refresh outcomes or timeout summaries; refresh project clones best-effort under the configured bootstrap timeout; locally sync live secondmate homes and propagate declared inheritable config; run the secondmate agent-liveness sweep; set up opt-in X mode; install tools only after consent; `FM_BOOTSTRAP_DETECT_ONLY=1` skips the four mutating sweeps and prints advisory-only `TANGLE:` wording without a checkout command | +| `fm-bootstrap.sh` | Detect required toolchain and version problems (including `tasks-axi` version/archive-body compatibility, `quota-axi`, and the other bootstrap AXI tools), dispatch profile JSON errors or active-rule blocks, default backlog-backend status, primary-checkout `TANGLE:` problems, and actionable clone refresh outcomes or timeout summaries; refresh project clones best-effort under the configured bootstrap timeout; locally sync live secondmate homes and propagate declared inheritable config; run the secondmate agent-liveness sweep; set up opt-in X mode; install tools only after consent; `FM_BOOTSTRAP_DETECT_ONLY=1` skips the four mutating sweeps and prints advisory-only `TANGLE:` wording without a checkout command | | `fm-fleet-sync.sh` | Fetch all clones, or one clone selected by absolute path, relative path, bare project name, or `projects/` resolved against this home's projects dir; fast-forward safe default-branch states, self-heal clean detached ancestor drift, report unsafe drift as `STUCK:`, and safely prune branches whose remote is gone | | `fm-fleet-snapshot.sh` | Print the read-only structured fleet snapshot JSON contract, schema `fm-fleet-snapshot.v1`, used by bearings and human fleet views; preserves current state from `fm-crew-state.sh` separately from historical status-log event data | | `fm-fleet-view.sh` | Render a human Markdown fleet view from `fm-fleet-snapshot.sh --json`, or print that underlying snapshot with `--json`, without reparsing raw fleet state | @@ -45,7 +45,7 @@ If you have changed away from the firstmate home in an interactive shell, invoke | `fm-supervision-lib.sh` | Shared grace-based "in-flight work exists but no watcher has a fresh beacon" status and predicate used by `fm-guard.sh`; `fm-turnend-guard.sh` uses it for banner fields and relies on `fm-wake-lib.sh` for live watcher lock health | | `fm-ff-lib.sh` | Shared guarded fast-forward helper for `/updatefirstmate` origin pulls and no-fetch local secondmate syncs | | `fm-config-inherit-lib.sh` | Shared primary->secondmate inheritable-config propagation (a declared, extensible item list - currently `config/crew-dispatch.json`, `config/crew-harness`, and `config/backlog-backend`) sourced by spawn, bootstrap, and config push | -| `fm-tasks-axi-lib.sh` | Shared backlog-backend selector and `tasks-axi` compatibility probe sourced by bootstrap and teardown | +| `fm-tasks-axi-lib.sh` | Shared backlog-backend selector and `tasks-axi` compatibility probe (0.1.1+ plus `update --archive-body`) sourced by bootstrap and teardown | | `fm-wake-drain.sh` | Atomically drain queued watcher wakes before handling supervision work, then run the watcher-liveness guard | | `fm-wake-lib.sh` | Shared durable wake queue, portable lock helpers, path-age helpers, and watcher identity/health helpers sourced by the watcher, drain, arm, guard, turn-end guard, daemon, and teardown | | `fm-classify-lib.sh` | Shared captain-relevant wake classifier sourced by the watcher and daemon, plus the watcher's provably-working predicate | From 911c59cabeb4c803dddc3ba9fe89865ee072923b Mon Sep 17 00:00:00 2001 From: kunchenguid Date: Wed, 8 Jul 2026 17:29:55 -0700 Subject: [PATCH 6/6] no-mistakes(lint): Silence ShellCheck source warning --- tests/fm-teardown.test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fm-teardown.test.sh b/tests/fm-teardown.test.sh index 67da80f8e22..b9cae1251e4 100755 --- a/tests/fm-teardown.test.sh +++ b/tests/fm-teardown.test.sh @@ -48,7 +48,7 @@ # (w) index.lock mtime read failure -> lock kept, REFUSE set -u -# shellcheck source=tests/lib.sh +# shellcheck source=tests/lib.sh disable=SC1091 . "$(dirname "${BASH_SOURCE[0]}")/lib.sh" fm_git_identity fmtest fmtest@example.invalid