Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/bootstrap-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The inline rules in `AGENTS.md` section 3 still bind: detect, then consent, then
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 that fails the compatibility probe (`docs/configuration.md` "Backlog backend" owns the definition); `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 and direct account-directory selection uses it; `bin/fm-dispatch-select.sh` still degrades at runtime when quota data is unavailable.
Direct account-directory selection also requires Agent Fleet's read-only profile registry to enforce crew-pool eligibility.
Direct account-directory selection also requires Agent Fleet's read-only profile registry to enforce worker-pool eligibility.
- `MISSING_MANUAL: <tool> (instructions: <url>)` - tell the captain why the tool is required and give them the printed instructions URL, but do not pass the tool to `bin/fm-bootstrap.sh install`; wait for the captain to complete the manual installation, then rerun session start to confirm the dependency is present.
- `BACKEND_INVALID: <name> (known: <names>)` - the resolved runtime backend has no verified dependency or lifecycle contract, so do not dispatch work until the invalid `FM_BACKEND` or `config/backend` value is corrected to one of the listed backends.
- `ACCOUNT_ROUTING: invalid routing policy - <reason>` - the environment or `config/account-routing-mode` cannot resolve to exactly one of `off`, `observe`, or `enforce`; fix the reported source before dispatch because managed spawns will fail closed on the same policy error.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/harness-adapters/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Secondmates have their own harness knob, so a secondmate can run on a different
`config/secondmate-harness` is the harness the primary uses to launch SECONDMATE agents, resolved through the fallback chain `config/secondmate-harness` -> `config/crew-harness` -> firstmate's own.
An absent or `default` `config/secondmate-harness` therefore behaves exactly as the crewmate harness did before this knob existed (secondmates launched on the crewmate harness); setting it splits the two.
The complete inheritable-config list is owned by the `secondmate-provisioning` skill.
This skill owns only the harness-relevant consequence: a secondmate's own crewmates use the primary's dispatch profiles, static harness value, and Claude crew model anchor, while `config/secondmate-harness` is the primary's own setting and is never inherited - secondmates do not spawn secondmates.
This skill owns only the harness-relevant consequence: a secondmate's own crewmates use the primary's dispatch profiles, static harness value, and Claude crewmate model anchor, while `config/secondmate-harness` is the primary's own setting and is never inherited - secondmates do not spawn secondmates.
Inheritance copies the literal `config/crew-harness` file, so for a secondmate's own crewmates to run on the primary's crewmate harness the captain must set `config/crew-harness` to a concrete adapter name, such as `codex`.
If `config/crew-harness` is unset or `default`, there is no concrete value to inherit, so the secondmate's own crewmates fall back to the secondmate's own/detected harness rather than the primary's effective crewmate harness.
Inheritance also copies the literal `config/crew-dispatch.json` file, so secondmates apply the same best-fit profile rules for their own crewmates.
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ Secondmate launches are exempt because they resolve through `fm-harness.sh secon

`quota-balanced` selection is deterministic and owned by `bin/fm-dispatch-select.sh`; every real new ship/scout launch uses direct account-directory selection.
When candidates carry account pools, the selector chooses the ordered first profile and passes its pool to spawn only as a compatibility activation input, never as an account choice or lease request.
Direct selection reads Agent Fleet's profile registry only for pool membership, excludes profiles outside the provider's crew pool, and requires Claude's non-secret per-directory quota-axi Keychain approval marker before any fallback or rotation.
When no usable Claude crew account survives those checks, selection fails closed and names the reserved or approval-required reasons.
Direct selection reads Agent Fleet's profile registry only for pool membership, excludes profiles outside the provider's worker pool, and requires Claude's non-secret per-directory quota-axi Keychain approval marker before any fallback or rotation.
When no usable Claude account for crewmates survives those checks, selection fails closed and names the reserved or approval-required reasons.
An explicitly declared `claude-crew-last-resort` pool is consulted only after `claude-crew` has no usable account; manual-only profiles remain excluded, and selector code never guesses account ownership from unreadable identity data.
Fresh Codex quota is an opportunistic ranking signal.
Exact best-score Codex ties and an all-unavailable quota result rotate across eligible accounts under a serialized persistent cursor; Claude always uses that rotation because its per-directory quota signal is unreadable.
Expand Down
6 changes: 3 additions & 3 deletions bin/fm-account-directory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
# Account homes are discovered under the current passwd user's
# .local/share/agent-fleet/accounts/<vendor>/ tree without fixed counts.
# Agent Fleet's read-only profile list is the source of pool eligibility:
# direct crew selection considers only enabled worker profiles with real homes
# direct crewmate selection considers only enabled worker profiles with real homes
# registered in the fixed <vendor>-crew pool, so a disabled, manual-only, or
# non-worker profile is never a crew candidate and no caller-supplied
# non-worker profile is never a crewmate candidate and no caller-supplied
# compatibility alias can weaken that boundary.
# Claude may also declare a separate claude-crew-last-resort pool.
# It is consulted only when no usable claude-crew profile remains; Firstmate
# never guesses that membership from credentials or identity metadata.
# Claude eligibility also requires quota-axi's exact, non-secret per-directory
# Keychain access marker.
# That check happens after pool filtering and before rotation, so a reserved
# profile cannot become a fallback and an unapproved crew profile fails honestly.
# profile cannot become a fallback and an unapproved crewmate profile fails honestly.
# Codex selection removes that account's quota-axi window cache immediately
# before every read, sets CODEX_HOME plus the account-isolated XDG_CACHE_HOME,
# accepts only a fresh result with at least one numeric five_hour or weekly
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
# bootstrap prints TASKS_AXI: available. quota-axi is required because
# crew-dispatch quota-balanced may call it; fm-dispatch-select.sh still
# degrades at runtime when quota data is unavailable.
# Routed direct crew selection also requires Agent Fleet's read-only
# Routed direct crewmate selection also requires Agent Fleet's read-only
# profile registry so pool-reserved accounts can fail closed.
# X mode is OPTIONAL and inert unless FM_HOME/.env has a non-empty
# FMX_PAIRING_TOKEN. When opted in, bootstrap requires curl+jq, writes
Expand Down
8 changes: 4 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Repeated unchanged wedge or permission-stall escalations add an escalation count
Those actionable wakes are written to a durable local queue (`state/.wake-queue`) before detector state advances, so a missed process exit can be recovered by draining the queue.
No-verb wakes, such as `working:` notes and bare turn-ended signals, are benign only when `bin/fm-crew-state.sh` reports positive evidence that the crewmate is still working: an actively running no-mistakes step for that crewmate's branch or a backend busy signature.
A crewmate that declares `paused:` for a known external wait is separately absorbed while idle and re-surfaced only on the longer pause cadence, rather than being treated as a possible wedge.
For stale-pane triage only, that durable declaration also explains the expected idle pane after a matching no-mistakes run has reached terminal `done` (including passed or checks-passed outcomes); active and failed runs retain normal run-step precedence, and stopped crew without a declared pause surface immediately.
For stale-pane triage only, that durable declaration also explains the expected idle pane after a matching no-mistakes run has reached terminal `done` (including passed or checks-passed outcomes); active and failed runs retain normal run-step precedence, and stopped crewmates without a declared pause surface immediately.
Pause cadence markers remain in force while the latest durable status still declares the pause and are cleared only after that status resumes, so every continuously declared pause still re-surfaces on the bounded long cadence.
Its initial normal-mode status signal still surfaces through the no-verb path, while away mode self-handles that routine signal and owns the later recheck.
Fresh stale panes use the same current-state read before trusting the status log, so an active run or busy pane outranks an old captain-relevant status-log line left behind before validation.
Expand Down Expand Up @@ -139,8 +139,8 @@ That keeps spawn launch compatible across claude, codex, grok, pi, and opencode
New ship/scout observe and enforce account routing is backend-neutral and uses the direct profile-directory contract owned by [configuration.md](configuration.md#agent-fleet-account-routing).
Routing remains default-off and adds no account field or provider environment override until an explicit account flag or observe/enforce policy enables it.
The direct selector reads Agent Fleet only for pool membership and enabled worker eligibility, excludes disabled, non-worker, and manual-only profiles, requires Claude's fully validated non-secret per-directory Keychain approval marker, then uses fresh Codex quota opportunistically.
If no usable Claude crew account survives eligibility, the launch fails closed before fallback selection.
Claude can declare a separate last-resort crew pool, consulted only after its primary crew pool is empty; no identity inference participates in either tier.
If no usable Claude account for crewmates survives eligibility, the launch fails closed before fallback selection.
Claude can declare a separate last-resort worker pool, consulted only after its primary worker pool is empty; no identity inference participates in either tier.
It serializes round-robin fallback and exact-score tie-breaking so unavailable usage and concurrent launches cannot collapse onto the stable first account.
For quota-balanced account-pool candidates, new dispatch deterministically uses the ordered first profile and passes its compatibility alias to spawn only as the activation signal for direct per-account selection.
The legacy Agent Fleet pool-summary selector is inactive deferred code tracked by `remove-fleet-routing-deadcode`; it is not available to new dispatches.
Expand Down Expand Up @@ -177,7 +177,7 @@ When the harness token is unset or `default`, launch falls back to `config/crew-
Those optional tokens are re-read on every secondmate spawn or respawn and are overridden by explicit per-spawn `--model` or `--effort` flags.
An explicit per-spawn harness or raw launch command does not inherit model or effort tokens from `config/secondmate-harness`.
`config/crew-harness` remains the crewmate harness and is inherited into secondmate homes.
`config/claude-crew-model` is inherited too, so Claude crews in every home validate the same exact `claude-opus-5` model anchor.
`config/claude-crew-model` is inherited too, so Claude crewmates in every home validate the same exact `claude-opus-5` model anchor.
`config/crew-dispatch.json` is inherited too; secondmates use the same natural-language dispatch profiles when spawning their own crewmates.
`config/backlog-backend` is inherited too; absent or `tasks-axi` selects the default tasks-axi backlog backend, while `manual` forces routine backlog updates to hand-editing across the fleet without disabling validated handoff delegation.

Expand Down
6 changes: 3 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Claude and Grok use background-notify cycles, Codex uses bounded foreground chec
When it is absent or contains `default`, crewmates mirror the firstmate's own harness.
Claude crewmate and scout launches additionally require the exact installed Opus 5 model before endpoint creation.
`fm-harness.sh claude-crew-model` resolves the one-value local `config/claude-crew-model` anchor, whose absent-file default and only accepted value is `claude-opus-5`; an explicit `--model` must equal that resolved anchor.
The anchor is inherited into secondmate homes so the same rule governs their Claude crews.
The anchor is inherited into secondmate homes so the same rule governs their Claude crewmates.
An empty, invalid, mismatched, or `default` anchor, a mismatched explicit model, and a raw Claude launch fail closed rather than inheriting or selecting another Claude model.
The resolved model is both passed through Claude's `--model` flag and recorded as `model=` in task metadata.
Fresh launches and recovery share this final resolution guard: recovery preserves an explicit recorded model, while legacy `model=default` metadata resolves through the anchor before relaunch and is rewritten to the value actually used.
Expand All @@ -178,7 +178,7 @@ The exact discovery, fresh-usage, health, fallback, and Herdr-hook mechanics are
The selected provider command receives `CLAUDE_CONFIG_DIR=<home>` or `CODEX_HOME=<home>`, and task metadata records the non-secret `account_home=<home>` for observability.
New ship/scout launches never ask Agent Fleet to enable a profile, establish identity, install a bundle, or acquire a lease.
They use Agent Fleet's read-only profile listing as the authoritative pool-membership registry.
Only enabled worker profiles with real account homes registered in the provider's crew pool are eligible, so disabled, non-worker, and manual-only profiles cannot be selected for crew work.
Only enabled worker profiles with real account homes registered in the provider's worker pool are eligible, so disabled, non-worker, and manual-only profiles cannot be selected for crewmate work.
Claude candidates must additionally carry quota-axi's exact non-secret per-directory `claude-keychain-access-granted` marker with the exact `granted` newline payload, mode 0600, current-user ownership, one link, non-symlink path components, and stable metadata.
Pool filtering and the marker check happen before fallback selection; if no Claude candidate survives, launch fails closed with the reserved and captain-approval-required reasons instead of borrowing a manual profile.
The optional declared `claude-crew-last-resort` pool is checked only after no usable `claude-crew` profile remains and is announced loudly when used.
Expand Down Expand Up @@ -215,7 +215,7 @@ It never creates `account_pool=`, `account_profile=`, `account_task=`, `account_
Existing ship/scout tasks that already carry `account_profile=` metadata remain legacy Agent Fleet managed generations.
That compatibility path is recovery-only for ordinary crewmates and is not used for any new ship/scout task.
Secondmate launches continue to create and recover legacy Agent Fleet managed generations until their dedicated direct-account integration is designed.
Bootstrap requires Agent Fleet for direct crew-pool eligibility, for enforced secondmate routing, and when legacy `account_profile=` or pending rollback metadata exists.
Bootstrap requires Agent Fleet for direct worker-pool eligibility, for enforced secondmate routing, and when legacy `account_profile=` or pending rollback metadata exists.
New direct ship/scout routing also requires `jq`, `quota-axi`, and Herdr's integration installer.
Same-profile recovery is sticky and fail-closed: `bin/fm-spawn.sh <id> --resume-account` validates existing task metadata and Agent Fleet's session mapping, uses `lease recover` rather than new-task quota selection, resumes the recorded provider session without replaying the brief as a new prompt, and requires a higher monotonic `session_event_seq` from a SessionStart accepted after its local launch gate before committing the recovered lease.
Wall-clock `updated_at` remains diagnostic only and never decides launch freshness.
Expand Down
2 changes: 1 addition & 1 deletion tests/fm-account-directory.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ make_spawn_case() {
touch "$home/state/.last-watcher-beat"
# A leased Treehouse worktree is handed over at detached HEAD on a clean
# default branch, and fm-spawn refuses one that is still attached to a branch
# (the crew creates its own). Reproduce that shape, not a generic worktree.
# (the crewmate creates its own). Reproduce that shape, not a generic worktree.
fm_git_worktree "$project" "$worktree" "wt-$name"
git -C "$worktree" checkout --quiet --detach HEAD
git -C "$project" branch --quiet -D "wt-$name"
Expand Down
2 changes: 1 addition & 1 deletion tests/fm-account-routing.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ test_off_is_byte_compatible_and_never_calls_agent_fleet() {
assert_grep '# Completion report' "$HOME_DIR/data/$id/brief.md" "post-cutover spawn did not upgrade a legacy unspawned brief"
# Assert each required section by the exact heading publication demands, not by
# the sentence that happens to list them. The prose was reworded once already
# (to spell out the level-two requirement crews kept getting wrong) and this
# (to spell out the level-two requirement crewmates kept getting wrong) and this
# assertion broke even though the contract still named all six - it was pinned
# to phrasing rather than to the guarantee.
for section in '## Summary' '## What changed' '## Verification' \
Expand Down
2 changes: 1 addition & 1 deletion tests/fm-crew-state.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ TMP_ROOT=$(fm_test_tmproot fm-crew-state)
fm_git_identity fmtest [email protected]

# A real git repo checked out on <branch>, so the helper's branch attribution
# (git symbolic-ref) resolves like it would for a live crew worktree.
# (git symbolic-ref) resolves like it would for a live crewmate worktree.
make_repo_on_branch() { # <dir> <branch>
local dir=$1 branch=$2
mkdir -p "$dir"
Expand Down
2 changes: 1 addition & 1 deletion tests/fm-gate-refuse.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ make_gate_worktree() {
}

# make_normal_repo <dir> -> echoes a plain (non-gate) git repo to stand in for a
# normal primary/crew checkout: its git-common-dir is <dir>/.git, never a gate.
# normal primary/crewmate checkout: its git-common-dir is <dir>/.git, never a gate.
make_normal_repo() {
local dir=$1
git init -q -b main "$dir"
Expand Down
Loading