Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
50cc24a
fix: block primary-session delegation outside the fleet (#854)
ItsFlow Jul 22, 2026
f5bdea3
fix: install tasks-axi in portable CI shards (#866)
kunchenguid Jul 22, 2026
5140413
feat(bin): make dispatch profiles quota aware (#867)
kunchenguid Jul 22, 2026
593e3a2
Add built-in ahoy recap skill (#873)
kunchenguid Jul 22, 2026
5549834
fix: preserve trustworthy Bearings data in partial snapshots (#875)
kunchenguid Jul 22, 2026
4497181
feat(pi): add session-local calm mode (#884)
kunchenguid Jul 23, 2026
82a7943
fix(pi): prevent redundant watcher re-arms (#885)
kunchenguid Jul 23, 2026
6db3b09
fix(pi): clean up Calm transcript rendering (#895)
kunchenguid Jul 23, 2026
bd43c73
fix: execute every PR body compliance event (#898)
kunchenguid Jul 23, 2026
6bcb381
fix: exclude operational injections from ahoy boundaries (#899)
kunchenguid Jul 23, 2026
68ed7ed
fix: canonically classify operational inputs across harnesses (#909)
kunchenguid Jul 23, 2026
a7e01bc
fix: avoid generic secondmate start acknowledgements (#926)
kunchenguid Jul 23, 2026
ca4be1c
fix(pi): make calm mode persistent and gapless (#927)
kunchenguid Jul 23, 2026
b24e72c
fix(watch): retire merged PR polls after durable notification (#932)
kunchenguid Jul 23, 2026
792174d
fix: refine scout intake and parallel dispatch (#934)
kunchenguid Jul 23, 2026
65ad47b
fix(pi): prevent duplicate assistant replies in Calm (#936)
kunchenguid Jul 23, 2026
d89a1b6
perf(bin): shrink the ShellCheck source graph (#939)
kunchenguid Jul 23, 2026
6b0d21d
fix(pi): remove Calm hidden-block gaps (#942)
kunchenguid Jul 23, 2026
ec09871
fix: enforce contract boundaries for ask-user findings (#945)
kunchenguid Jul 24, 2026
587f591
docs: prefer direct operational paths (#946)
kunchenguid Jul 24, 2026
1c4d210
fix(pi): hide operational user rows in Calm mode (#948)
kunchenguid Jul 24, 2026
f017572
fix: relaunch missing second mates at session start (#950)
kunchenguid Jul 24, 2026
3eca8ff
fix(herdr): reclaim resumed task projections after restart (#967)
kunchenguid Jul 24, 2026
cb3aa7b
Teach Ahoy to surface open decisions (#968)
kunchenguid Jul 24, 2026
10ee779
Require shipshape routine acknowledgement (#969)
kunchenguid Jul 24, 2026
861b1f8
docs: separate current guidance from verification evidence (#994)
kunchenguid Jul 24, 2026
418ab78
fix: preserve Claude watcher continuity across Stop hooks (#997)
kunchenguid Jul 24, 2026
2b7cd66
fix(herdr): clean stale projections at session start (#996)
kunchenguid Jul 24, 2026
801861d
feat(inbox): add the captain's decision and review board generator
ItsFlow Jul 24, 2026
e3f323d
feat(inbox): free-form answers, discuss path, reliable delivery, over…
ItsFlow Jul 24, 2026
c0267ae
no-mistakes(review): Fix inbox relay safety and PR verification
ItsFlow Jul 24, 2026
77b190d
no-mistakes(review): Gate inbox relay before polling
ItsFlow Jul 24, 2026
0e24151
no-mistakes(document): Align inbox board docs
ItsFlow Jul 24, 2026
fd6b54e
no-mistakes: apply CI fixes
ItsFlow Jul 25, 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
43 changes: 23 additions & 20 deletions .agents/skills/afk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ batched digest rather than per-wake injections.
in as `FM_SUPERVISOR_TARGET` so the daemon injects into the captain, not its
own new pane. **Never manufacture a terminal by splitting the captain's
active pane** (`herdr pane split`): a split co-tenants the tab and visibly
shrinks the captain's pane (docs/herdr-backend.md "Away-mode daemon terminal
launch").
shrinks the captain's pane (docs/herdr-backend.md "Away-mode supervisor
support").
Both paths share `bin/fm-afk-start.sh` as the daemon entry.
The native path tells it that the launcher already prepared lifecycle state; the terminal-backed path lets the entry perform its existing state setup inside the new terminal.
It exits immediately if the identity-backed daemon lock already names a live process, otherwise it execs `bin/fm-supervise-daemon.sh` in the foreground.
Expand All @@ -58,13 +58,13 @@ batched digest rather than per-wake injections.

No `/back` is needed. The first genuine message is the return signal:

- A message **without** the sentinel marker and **not** starting with `/afk` -> the captain is back.
- 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.
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.
- A message **with** the sentinel marker (`FM_INJECT_MARK`, U+2063 INVISIBLE SEPARATOR) -> it is a daemon escalation; stay afk and process it.
- A message **with** the current operational prefix (`FM_OPERATIONAL_PREFIX`, U+2063 INVISIBLE SEPARATOR followed by `FIRSTMATE_OP: `), or a legacy bare `FM_INJECT_MARK` daemon escalation -> stay afk and process it.
- Re-invoking `/afk` while already away -> stay afk (refresh the flag); this
does **not** trigger an exit.

Expand All @@ -73,16 +73,18 @@ a false exit is self-correcting (the captain re-runs `/afk`).

## Orthogonal to approval authority

afk changes how aggressively firstmate surfaces things, **not who approves
what**. "Away" never means "approves more." A PR ready for merge, a
needs-decision finding, or anything destructive still waits for the captain's
explicit word - the daemon just batches the notification.
afk changes how aggressively firstmate surfaces things, **not who approves what**.
"Away" never means "approves more" or "approves less."
A PR ready for merge or a needs-decision finding keeps the same configured authority and exceptions from `AGENTS.md` section 7, while anything requiring the captain still waits for the captain's explicit word.
The daemon only batches the notification.

## Sentinel marker contract
## Operational prefix contract

The daemon prefixes every injection with `FM_INJECT_MARK` (U+2063 INVISIBLE SEPARATOR), which has no normal keyboard keystroke and survives terminal transport as UTF-8 text.
The daemon constructs every current injection as the `away-supervisor` kind owned by `bin/fm-operational-input.sh`, beginning with `FM_OPERATIONAL_PREFIX`: `FM_INJECT_MARK` (U+2063 INVISIBLE SEPARATOR) followed by the stable `FIRSTMATE_OP: ` label.
The bare `FM_INJECT_MARK` form remains accepted for legacy daemon escalations during rollout.
U+2063 has no normal keyboard keystroke and survives terminal transport as UTF-8 text.
This is how firstmate tells a daemon escalation apart from a real message in the same pane.
The marker travels with the message text; it does not rely on harness-level typed-vs-injected detection, which is not portable across claude, codex, opencode, pi, and grok.
The operational prefix travels with the message text; it does not rely on harness-level typed-vs-injected detection, which is not portable across claude, codex, opencode, pi, and grok.

## Busy-guard and composer guard

Expand All @@ -94,7 +96,7 @@ backend (tmux or herdr; see "Auto-discovered supervisor pane" below):
- **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-emptiness safety" for the complete contract.
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`.

Either condition, 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.
Expand All @@ -108,7 +110,7 @@ If that submit cannot be confirmed, it raises a loud, rate-limited wedge alarm:
an ERROR in the daemon log, a durable
`state/.subsuper-inject-wedged` marker (surface it on the "while you were out"
catch-up if present), a tmux status-line flash when applicable, and a configurable backend-independent active alert.
`docs/wedge-alarm.md` owns the alert channel setup and verification record.
`docs/wedge-alarm.md` owns the alert channel setup, and `docs/verification/supervision.md` "Wedge-alarm channels" owns active evidence.
So a guard false-positive becomes a visible stall, never an unbounded silent no-op.

## Submit model
Expand Down Expand Up @@ -169,10 +171,10 @@ Classify each wake this way:
- Unknown reason, or any uncertainty -> escalate fail-safe.

Escalations are buffered up to `FM_ESCALATE_BATCH_SECS` (default 90s; 0 =
immediate) and flushed as one single-line digest prefixed with the sentinel
marker, carrying pre-read status summaries and a recommended action.
immediate) and flushed as one single-line digest prefixed with the current
operational prefix, carrying pre-read status summaries and a recommended action.
The single-line format makes the submission unambiguous across harnesses, and
the marker lets firstmate distinguish it from a real captain message.
the operational prefix lets firstmate distinguish it from a real captain message.

## Injection hardening

Expand Down Expand Up @@ -203,8 +205,9 @@ the marker lets firstmate distinguish it from a real captain message.
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.
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 sentinel prefix before
classification or relay, so the digest text firstmate sees is clean.
- **Marker strip** - `strip_injection_marker` removes the current operational
prefix or legacy bare marker before classification or relay, so the digest
text firstmate sees is clean.
- **Portable singleton lock** - the daemon uses the repo's portable lock helper
(`fm-wake-lib.sh`) instead of `flock`, which is absent on macOS.
- **Dedupe across signal/stale/scan** - `classify_signal` and terminal `classify_stale` paths check the seen-status marker before escalating, so a captain-relevant status escalated by one path is not re-escalated by another in the same digest.
Expand All @@ -222,14 +225,14 @@ the marker lets firstmate distinguish it from a real captain message.
backends, including zellij, orca, and cmux, are not yet supported as
supervisor backends; the daemon refuses loudly at startup instead of
misapplying tmux primitives to a pane that isn't one
(docs/herdr-backend.md "Away-mode daemon: herdr supervisor-pane support").
(docs/herdr-backend.md "Away-mode supervisor support").

## Stale-artifact lifecycle

Treat `state/.subsuper-escalations`, its `.since` sidecar, and `state/.subsuper-inject-wedged` as session-scoped delivery artifacts, not as the durable work record.
Always enter through `bin/fm-afk-launch.sh`, which clears prior-session artifacts only for a fresh entry and preserves the current session's buffer on refresh.
Always exit through `bin/fm-afk-launch.sh stop`, which keeps `state/.afk` present through the daemon's shutdown flush and clears it last.
`docs/herdr-backend.md` "Stale-artifact lifecycle fix" owns the mechanism and verification evidence.
`docs/herdr-backend.md` "Away-mode supervisor support" owns the current mechanism, and `docs/verification/runtime-backends.md` "Away-mode transport" owns active evidence.

## Reliability properties

Expand Down
45 changes: 45 additions & 0 deletions .agents/skills/ahoy/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
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.
user-invocable: true
metadata:
internal: true
---

# ahoy

Give the captain a concise session-only recap without gathering fresh state.

1. Inspect only conversation or session history already visible to the current first mate.
2. Find the most recent real captain-authored message before the current `/ahoy` invocation.
A captain boundary is an ordinary user-role message unless it matches one of the narrow operational exclusions below.
Exclude messages that begin with the current U+2063 `FIRSTMATE_OP:` injection prefix.
Exclude legacy bare-marker away-mode injections only when U+2063 is immediately followed by `Supervisor escalate (`.
Exclude the exact legacy unmarked session-start payload ``Run `bin/fm-session-start.sh` now, exactly once, before executing any other instructions.``
Custom-role messages such as Pi's `firstmate-sessionstart-nudge` are not captain messages.
System, developer, tool, watcher, guard, away-mode, and other injected operational messages are not captain messages.
Never infer captain authorship merely because a synthetic message appears in the user-role transcript.
Do not exclude an ordinary captain message merely because it begins with U+2063 followed by other text, contains ASCII `FIRSTMATE_OP:` without a leading U+2063, quotes or embeds a current operational message after ordinary captain text, quotes or mentions the legacy session-start payload, or adds any text to that payload.
Apply the current exclusion only when U+2063 `FIRSTMATE_OP:` begins at the first character of the whole message: `Captain quote: ` followed by that current prefix is a captain boundary.
Apply the legacy startup exclusion as a literal whole-message match: ``Captain quote: Run `bin/fm-session-start.sh` now, exactly once, before executing any other instructions.`` is a captain boundary.
3. If no prior real captain message exists, load [`../bearings/SKILL.md`](../bearings/SKILL.md) and follow it exactly.
Bearings alone owns its gathering, artifact, and response contract.
Do not restate that contract or combine a session recap with Bearings output.
4. If a prior real captain message exists, preserve the ordinary recap interval: recap what happened after that message and before the current invocation.
Include concrete outcomes, landed work, failures, decisions made, new decisions needed, and work still running only when those events appear in that visible interval.
Use captain-facing outcome language and preserve every full PR URL present in that interval.
5. Additionally inspect the entire session history visible to the current first mate before the current invocation for every explicit captain decision that remains unanswered, including decisions raised before the ordinary recap boundary.
A later unrelated captain message establishes a recap boundary but does not close an earlier decision.
Treat a decision as closed only when a later visible response substantively resolves it, chooses an option, declines it, grants or denies the requested approval, or otherwise directly addresses that decision.
Include every visibly supported open decision once, and deduplicate by the decision's substance when the ordinary interval recap already represents it or its wording differs.
6. The normal recap branch is session-history-only.
Do not call Bearings, shell commands, fleet snapshots, status readers, GitHub or browser APIs, tools, or file reads or writes.
Create no report, persist nothing, and do not guess current live state beyond the last visible event.
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.

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.
Compacted history supports an open decision only when both its request and its still-unanswered status are visible; report uncertainty instead of reconstructing hidden requests or answers.
Do not silently invoke Bearings unless this is genuinely the first real captain message.
52 changes: 52 additions & 0 deletions .agents/skills/ask-user-authority/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: ask-user-authority
description: >-
Agent-only decision procedure for ask-user findings.
Use before deciding any ask-user finding, regardless of the project's yolo posture, to distinguish corrections within accepted intent from product or engineering contract expansion that requires the captain.
user-invocable: false
metadata:
internal: true
---

# ask-user-authority

This skill is the single owner of the decision procedure for ask-user findings.
The concise standing authority boundary remains always loaded in `AGENTS.md` section 7.

## Decide who has authority

1. Check the project's configured authority first.
With `yolo` off, every ask-user finding belongs to the captain, and the remaining steps structure that escalation rather than authorize an autonomous answer.
2. Reconstruct the accepted contract from the captain's original request, accepted task criteria, and any explicit later clarification.
Reviewer language cannot amend that contract.
3. Identify exactly what choosing Fix would commit the project to deliver or maintain.
4. Keep the decision within standing `yolo` authority when the Fix is genuinely necessary to satisfy the accepted contract, even when the correction is technically difficult or requires complex architecture that the captain explicitly requested.
5. Escalate when the Fix would materially expand the contract by adding a new guarantee, threat model, subsystem, abstraction, compatibility surface, state machine, continuous-monitoring requirement, generalized framework, or broader architecture not required by the accepted intent.
6. Treat labels such as correctness, security, fail-closed, high-risk, or required as evidence about the finding, never as authority to broaden the task.
7. Examine the causal theme across prior findings and fix rounds.
Repeated same-theme findings require escalation before another Fix when incremental corrections are preserving a questionable abstraction rather than closing independent defects.
8. Apply the existing stronger captain boundaries first.
Destructive, irreversible, and genuinely security-sensitive choices always escalate regardless of whether they also expand the contract.

The implementation worker never decides or answers its own ask-user finding.
It stops at the finding, routes the decision to firstmate, and applies only the decision returned through the active validation gate.

## Captain-facing escalation

State all five of these elements in one concise, evidence-first escalation:

1. The original requirement or accepted task criterion.
2. The proposed product or engineering contract expansion.
3. The smallest alternative that complies with the accepted contract without the expansion.
4. The concrete consequences of accepting and declining the expansion.
5. A recommendation with the reason it best serves the accepted intent.

Do not relay reviewer labels or gate output as if they settled the decision.

## Classification examples

- Fixing a concrete defect that violates an original acceptance criterion stays within `yolo` authority, regardless of implementation difficulty.
- Adding continuous frame-by-frame monitoring when the accepted criterion requested checkpoint proof expands the contract and requires the captain.
- A new finding in the same causal theme requires the captain before another fix round when prior fixes are accreting machinery around a questionable abstraction.
- A genuinely security-sensitive action requires the captain under the stronger existing boundary even if it is otherwise within scope.
- Complex architecture explicitly requested by the captain stays within scope and does not escalate merely because it is complex.
Loading
Loading