Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
53ecbc7
docs(stow): generalize read-before-write in the public stow skill (#2…
kunchenguid Aug 10, 2026
f9b9d43
fix: resurface durable supervision work after re-arm (#2065)
kunchenguid Aug 10, 2026
9068958
ci: measure Herdr automation on Windows runners (#2100)
kunchenguid Aug 10, 2026
f74d9e4
feat(ahoy): guide captains through open decisions (#2099)
kunchenguid Aug 10, 2026
7f828ea
fix(stow): enforce startup-memory budget decisions (#2110)
kunchenguid Aug 11, 2026
fc5f164
fix(spawn): refresh pooled worktrees from origin before launch (#2116)
kunchenguid Aug 11, 2026
7f05100
fix(composer): unify safe classification across backends (#2102)
kunchenguid Aug 11, 2026
e836a7f
fix(spawn): gate Pi TUI mode by CLI capability (#2117)
kunchenguid Aug 11, 2026
76355e2
docs(vision): elevate experience, pain narrative, and distro virtues …
kunchenguid Aug 11, 2026
2d550fe
feat(bin): reconcile inactive terminal crew outcomes (#2167)
kunchenguid Aug 11, 2026
07450b9
ci: raise Herdr test timeout (#2191)
kunchenguid Aug 11, 2026
e8c7645
fix: refresh stale Pi instructions after compaction (#2163)
kunchenguid Aug 11, 2026
81ce6dc
feat: add deterministic condition-to-action watcher (#2200)
kunchenguid Aug 11, 2026
614fae6
fix(bin): honor a decision key stated after the verb colon (#2202)
kunchenguid Aug 11, 2026
c42cfe0
fix(bin): prevent watcher recovery acknowledgement livelock (#2212)
kunchenguid Aug 12, 2026
b5d430d
feat(fmx-respond): consume Relay conversation chains (#2206)
kunchenguid Aug 12, 2026
b91016f
fix: parse decision verbs before status metadata tags (#2280)
kunchenguid Aug 12, 2026
b0ad61e
fix(bin): collapse duplicate supervision wakes (#2287)
kunchenguid Aug 13, 2026
4930d2c
feat: add Cursor CLI crew harness (#2238)
kunchenguid Aug 13, 2026
96876db
fix(bin): require quota-axi 0.1.25 (#2300)
kunchenguid Aug 13, 2026
85cefa9
fix(bin): prevent false Pi watcher alarms during hand-offs (#2304)
kunchenguid Aug 13, 2026
81f7020
feat: support Cursor Agent CLI as a primary harness (#2305)
kunchenguid Aug 13, 2026
5521323
feat(bin): add decline and repair paths for decision holds (#2330)
kunchenguid Aug 13, 2026
db0280f
fix(bin): surface buried wake status lines once (#2331)
kunchenguid Aug 13, 2026
88d0f2e
feat: add max Calm presentation level (#2334)
kunchenguid Aug 13, 2026
9823ff8
feat(calm): hide mid-turn working notes by default (#2339)
kunchenguid Aug 13, 2026
1238402
chore: store no-mistakes test evidence in the repo (#2355)
kunchenguid Aug 14, 2026
6789876
chore: ignore scratchpad/ at the repo root (#2359)
kunchenguid Aug 14, 2026
48a6ca7
fix(spawn): stop passing the removed --tui-mode flag on Pi launches (#2)
tiago-peixoto Aug 11, 2026
94309a5
fix(supervision): align Claude guard with session ownership
tiago-peixoto Aug 14, 2026
d8ddd21
no-mistakes(review): Fix guard escalation and evidence fingerprinting
tiago-peixoto Aug 14, 2026
b22986b
no-mistakes(review): Refresh guard evidence and escalation documentation
tiago-peixoto Aug 14, 2026
ceb8eae
no-mistakes(review): Keep queued wake delivery under supervision
tiago-peixoto Aug 14, 2026
753a51e
no-mistakes(review): Use one supervision snapshot for queue warnings
tiago-peixoto Aug 14, 2026
c47f190
no-mistakes(document): Align supervision documentation with acknowled…
tiago-peixoto Aug 14, 2026
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
41 changes: 20 additions & 21 deletions .agents/skills/afk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ No `/back` is needed. The first genuine message is the return signal:

- A message **without** the current operational prefix or a legacy bare marker, and **not** starting with `/afk` -> the captain is back.
Run `bin/fm-afk-return.sh` before acting on the message that brought the captain back.
That script owns correct-ordered daemon shutdown, durable wake draining, escalation and wedge evidence, and the return-catch-up gate.
That script owns correct-ordered daemon shutdown, durable wake presentation and post-handling acknowledgement, escalation and wedge evidence, and the return-catch-up gate.
If it reports a firstmate-actionable `blocked:` event, remediate it immediately through the normal lifecycle, or explicitly reclassify it with a durable reason and close its decision key with `resolved [key=...]`, then run `bin/fm-afk-return.sh check`.
Once the daemon stops, resume full per-wake responsiveness through the emitted primary-harness supervision protocol while blocker handling proceeds, so the gate never creates a blind wait.
Do not answer a Bearings request or perform any other ordinary captain work until the check exits successfully.
Expand Down Expand Up @@ -94,11 +94,11 @@ backend (tmux or herdr; see "Auto-discovered supervisor pane" below):

- **Primary-pane busy guard** - `pane_is_busy` trusts Herdr native `busy` when available, otherwise matches rendered output against only the detected primary harness's signature.
This narrow delivery guard never classifies a recorded worker task and never uses a global union of vendor patterns.
- **Composer-state guard** - `inject_msg` reads the full `empty`/`pending`/`unknown` verdict from `fm_backend_composer_state` and injects only when it is affirmatively `empty`.
`pending` means real unsubmitted text, while `unknown` includes an unreadable pane and a bare shell prompt left after the agent exits, so both defer.
The shared `bin/fm-composer-lib.sh` owns the content decision after each backend captures and structurally identifies its own composer row.
It preserves idle bordered composers such as claude's `│ > … │` and bare agent glyphs as empty, but a bare shell glyph is unknown unless inside a genuine bordered composer box; see `docs/herdr-backend.md` "Composer and injection safety" for the complete contract.
`pane_input_pending` remains the tested predicate for callers that only need to know whether real unsubmitted text is present, but it is insufficient for an injection-safety decision because it cannot distinguish `empty` from `unknown`.
- **Composer-state guard** - `inject_msg` reads the full `empty`/`pending`/`pending-unproven`/`unknown` verdict from `fm_backend_composer_state` and injects only when it is affirmatively `empty`.
Every other or future verdict defers, including an unreadable pane, ambiguous geometry, a blank unidentified row, and a bare shell prompt left after the agent exits.
Each adapter contributes only capture and capability facts to the fleet-wide screen classifier in `bin/fm-composer-lib.sh`, which owns every shape and verdict.
It preserves proven idle composers as empty but requires a genuine container around shell glyphs; see `docs/herdr-backend.md` "Composer and injection safety" for the operator contract.
`pane_input_pending` is the tested fail-closed predicate for callers that need to know whether the composer is unsafe: it treats every result except exact `empty` as pending.

A busy primary pane, or any composer verdict other than `empty`, defers the injection; the buffered escalation survives in `state/.subsuper-escalations` and is retried on the next housekeeping tick.
In afk mode the composer guard is belt-and-suspenders (no human is typing), but it protects against the race window between the captain returning and their message landing, a dead shell, and the daemon's own previous injection sitting unsent.
Expand All @@ -121,9 +121,9 @@ herdr - both literal, non-submitting sends), then submitted with Enter and
**verified** through the selected backend's submit primitive.
Enter is retried (Enter only, never a retype) until the backend confirms the
submit landed.
For tmux that confirmation is a cleared composer, using the same corrected,
border-aware detector as the composer guard.
For herdr, normal idle-baseline submits are confirmed by native agent-state showing a real turn started; the ANSI-aware composer classifier remains the affirmative-empty pre-injection guard and conservative fallback for non-idle or unreadable baselines.
For tmux that confirmation is normally a proven cleared composer from the shared classifier; an idle baseline transitioning to busy across this submit's own Enter also confirms that the turn started when a working harness hides its composer.
Without that baseline, busy state never converts an `unknown` composer into confirmation.
For herdr, normal idle-baseline submits are confirmed by native agent-state showing a real turn started; the shared classifier remains the affirmative-empty pre-injection guard and conservative fallback for non-idle or unreadable baselines.
A bordered-empty or ghost-only composer is recognized as empty where that backend uses composer confirmation, rather than mistaken for a swallowed Enter.
`fm-send.sh` uses the same primitive and exits non-zero
when a steer's Enter is positively swallowed, so firstmate learns an instruction
Expand All @@ -146,9 +146,8 @@ behavior but needs a separate fix; the gap is recorded in

## Classification policy

The daemon wraps `fm-watch.sh`, runs the watcher as a child, classifies each
wake reason in bash, and self-handles the routine majority without consuming a
firstmate turn.
The daemon wraps `fm-watch.sh`, runs the watcher as a child, presents every durable wake after each actionable watcher close, classifies each presented record in bash, and acknowledges the presented generation only after routing completes.
It self-handles the routine majority without consuming a firstmate turn.
Captain-relevant events, plus a bounded recheck of a declared external wait that remains idle, escalate to firstmate's context as one pre-read, single-line, batched digest.
The classification predicates (the captain-relevant verb set, declared-pause vocabulary, signal/stale tests, and fleet-scan) live in the shared `bin/fm-classify-lib.sh`, the same library the always-on watcher uses for its own triage when afk is off, so the two modes apply one identical policy.
While `state/.afk` exists the daemon owns the watcher, so the watcher reverts to one-shot and lets the daemon do the triage - the two never run their triage at the same time.
Expand Down Expand Up @@ -185,11 +184,12 @@ the operational prefix lets firstmate distinguish it from a real captain message
- **Busy and composer guards on the supervisor pane** - before injecting, the daemon runs the detected-primary-harness rendered busy guard and reads `fm_backend_composer_state` directly.
Only `empty` permits injection; `pending` protects half-typed or swallowed input, and `unknown` protects unreadable panes and bare dead-shell prompts.
Every other result preserves the buffer for retry, so the daemon never merges its digest into the captain's half-typed line or types it into a shell.
- The shared composer classifier receives a candidate row only after the active backend performs its own capture and structural row recognition.
tmux and herdr route their raw styled candidate rows through the shared `fm_composer_strip_ghost` extractor, which removes dim/faint and dark-TRUECOLOR ghost/placeholder text before classification.
They read the composer shape from a separately ANSI-stripped plain row because a dark TRUECOLOR border can be stripped with ghost content.
- The active backend passes its capture plus declarative styled, cursor, identity, and row capabilities to the shared screen classifier; all structural recognition and verdict logic remains in `bin/fm-composer-lib.sh`.
Styled captures let that owner remove dim/faint and dark-TRUECOLOR ghost or placeholder text while shape detection uses the ANSI-stripped screen, so a dark border is not lost with ghost content.
A ghost-only or idle bordered composer such as claude's `│ > ... │` therefore reads empty without allowing an unbordered shell prompt to do the same.
`FM_COMPOSER_IDLE_RE` still overrides tmux empty-composer matching after shared ghost and border stripping, and `FM_BUSY_REGEX` overrides the rendered delivery guards plus Grok's isolated task-state fallback.
`FM_COMPOSER_IDLE_RE` overrides the shared idle-placeholder regex, but a match alone never bypasses the classifier's shape-specific position and ANSI de-emphasis safety gates.
`FM_BUSY_REGEX` overrides the rendered delivery guards plus Grok's isolated task-state fallback.
A blank or otherwise unidentified input row carries no positive container proof and defers injection, so a modal dialog or a mid-redraw pane is never an injection target.
- **Max-defer escape** - the daemon must never silently wedge. If anything stays
buffered past `FM_MAX_DEFER_SECS` (default 300s), the daemon attempts one
normal flush, which still requires an idle pane and an affirmatively empty composer. If that
Expand All @@ -202,9 +202,8 @@ the operational prefix lets firstmate distinguish it from a real captain message
on tmux, `pane send-text` on herdr), then submitted with Enter and verified.
Enter is retried, Enter only and never a retype, until the backend submit
primitive reports `empty` as its caller-facing success verdict.
For tmux that verdict means the shared-ghost-aware and border-aware composer
cleared.
For herdr's normal idle-baseline path it means native agent-state observed a real turn start; herdr uses the ANSI-aware structural classifier for the pre-injection composer guard and fallback paths.
For tmux that verdict normally means the shared classifier proved the composer cleared; a baseline-gated idle-to-busy transition may instead prove this Enter started the turn.
For herdr's normal idle-baseline path it means native agent-state observed a real turn start; herdr uses the shared classifier for the pre-injection composer guard and fallback paths.
This lets ghost-only or bordered-empty composers count as empty where a composer read is the active confirmation signal.
- **Marker strip** - `strip_injection_marker` removes the current operational
prefix or legacy bare marker before classification or relay, so the digest
Expand Down Expand Up @@ -239,8 +238,8 @@ Always exit through `bin/fm-afk-launch.sh stop`, which keeps `state/.afk` presen

These properties must hold:

- Nothing is lost. The durable queue plus `fm-wake-drain.sh` recover any missed
or crashed injection.
- Nothing is lost after queue publication.
The daemon leaves every presented wake durable until routing completes and post-handling acknowledgement succeeds, so interruption replays the same work to the daemon or its successor.
- Wedge detection is bounded-latency, not lossy.
- Declared external waits are rechecked on a separate, bounded cadence rather than being mislabeled as wedges.
- The catch-all scan backs up the keyword classifier.
Expand Down
8 changes: 7 additions & 1 deletion .agents/skills/ahoy/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: ahoy
description: Recap visible session events since the prior real captain message plus visibly unanswered captain decisions when the captain explicitly invokes /ahoy, with a Bearings fallback when /ahoy is the session's first real captain message.
description: Recap visible session events and guide the captain through visibly unanswered decisions when the captain explicitly invokes /ahoy, with a Bearings fallback when /ahoy is the session's first real captain message.
user-invocable: true
metadata:
internal: true
Expand Down Expand Up @@ -43,6 +43,12 @@ Give the captain a concise session-only recap without gathering fresh state.
7. If no ordinary events occurred after the previous captain message but an older visibly open decision exists, report that decision instead of claiming nothing happened.
If neither ordinary events nor visibly open decisions exist, say directly in one sentence that nothing happened after the previous captain message.

8. After the normal recap, when the existing visibly open decision inventory contains decisions, begin a guided decision-clearing flow by presenting only the single open decision judged most impactful by the first mate.
Make clear that impact ordering is the first mate's judgment rather than a mechanical score.
Give enough escalation-quality context to decide easily: the decision, why it matters, the options, and a recommendation.
9. When the captain answers the presented decision, present the next highest-impact decision from that existing inventory in the same form.
Continue one decision at a time until none remain, without starting this flow when the inventory is empty.

The current `/ahoy` message is outside the recap interval.
A previous `/ahoy` is a real captain message and may be the next interval boundary.
If context compaction makes the prior boundary unavailable, state that the exact session boundary is unavailable and summarize only visibly supported events.
Expand Down
10 changes: 6 additions & 4 deletions .agents/skills/decision-hold-lifecycle/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ After inventorying the whole report and review surface, run `bin/fm-decision-hol
A completed investigation and an ended visual review use this same owner and completion command; a visual tool, including Lavish, never owns a parallel completion policy.
Run the command in the originating work's authoritative `FM_HOME`; main-home work creates main-home holds, and secondmate-owned work creates holds in that secondmate home's backlog rather than copying them into the main backlog.
Do not close a hold merely because the originating investigation completed, its report was archived, its visual review ended, or its task was torn down.
The hold remains the authoritative Captain's Call item until the captain's answer is durably recorded, dependent work is created in the same backlog and blocked by that hold, and `bin/fm-decision-hold.sh resolve` routes the answer by clearing those dependency edges before closing the hold.
When the captain's answer authorizes follow-up work, the hold remains the authoritative Captain's Call item until that answer is durably recorded, dependent work is created in the same backlog and blocked by the hold, and `bin/fm-decision-hold.sh resolve` routes the answer by clearing those dependency edges before closing the hold.
When the captain's answer routes no follow-up work at all, such as a declined proposal, `bin/fm-decision-hold.sh decline` records that answer and closes the hold; it never substitutes for routing work the captain did authorize.
A hold closed outside this owner leaves no durable answer, so the completion gate keeps failing until `bin/fm-decision-hold.sh repair` records the decision the captain actually gave; neither unrouted path may stand in for an answer the captain has not given.
Resolved findings, recommendations that need no captain choice, and prose that merely sounds decision-like do not create holds.
Bearings reads the resulting structured state and must never compensate by scraping historical reports, visual-review artifacts, terminal output, chat, or other prose.

Expand All @@ -32,9 +34,9 @@ Bearings reads the resulting structured state and must never compensate by scrap
3. For each choice, choose a stable key and use the script's `hold` command with a concise title, reason, and repository.
4. Run the script's `complete` command with the full unresolved-key inventory for that review pass.
5. Relay the choices to the captain as decisions from Bearings' Captain's Call section under `AGENTS.md` section 9; do not use the word hold in captain chat.
6. After the captain decides, record dependent work with normal tasks-axi commands and block it by the hold identity.
7. Put the captain's exact durable decision in a file and use the script's `resolve` command with every routed task.
8. Confirm Bearings no longer shows the closed hold and that routed work remains in structured backlog state.
6. If the captain authorizes dependent work, record it with normal tasks-axi commands and block it by the hold identity.
7. Put the captain's exact durable decision in a file and close the hold with the script's `resolve` command and every routed task, its `decline` command when the answer routes no work, or its `repair` command when the hold was already closed outside the script.
8. Confirm Bearings no longer shows the closed hold and that any routed work remains in structured backlog state.

`bin/fm-decision-hold.sh --help` owns command syntax, identity construction, completion attestation, retry behavior, and close ordering.
`docs/decision-hold-lifecycle.md` records the mechanism and regression evidence without restating this policy.
Loading