Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ba885d0
refactor(composer): one shape owner behind thin capture adapters, who…
kunchenguid Aug 10, 2026
c69e0e9
no-mistakes(review): Fix Pi glyph ambiguity and complete profile matrix
kunchenguid Aug 10, 2026
9b6917f
no-mistakes(review): Preserve bare verdict when Pi identity probe is …
kunchenguid Aug 10, 2026
f8eb6f7
no-mistakes(review): Harden composer structure and titled-border geom…
kunchenguid Aug 10, 2026
9adb9bb
no-mistakes(review): Require proven idle baseline and strict Zellij g…
kunchenguid Aug 10, 2026
29b0844
no-mistakes(review): Reject box bottom borders as composer input rows
kunchenguid Aug 10, 2026
612ba2e
no-mistakes(review): Prove Zellij probe typing before classifier retries
kunchenguid Aug 10, 2026
e06a12d
no-mistakes(review): Preserve Pi identity uncertainty and scan full l…
kunchenguid Aug 10, 2026
2c2678d
no-mistakes(review): Verify Zellij text lands before submitting
kunchenguid Aug 10, 2026
11fc737
no-mistakes(review): Scope Zellij typing verification to selected com…
kunchenguid Aug 10, 2026
0b1d28c
no-mistakes(review): Verify Zellij pastes through composer-scoped con…
kunchenguid Aug 10, 2026
e1a9072
no-mistakes(review): Prove wrapped bare Zellij pastes through compose…
kunchenguid Aug 10, 2026
0b83c7f
no-mistakes(review): Invalidate stale cursorless composers below dead…
kunchenguid Aug 10, 2026
8b68139
no-mistakes(review): Handle shell prompt placeholders in composer ext…
kunchenguid Aug 10, 2026
cf63973
no-mistakes(review): Classify cursorless bare continuation regions sa…
kunchenguid Aug 10, 2026
9b44a85
no-mistakes(review): Reject stale cursorless containers below live ac…
kunchenguid Aug 10, 2026
d1b2a29
no-mistakes(review): Preserve prompt glyphs in wrapped Zellij pastes
kunchenguid Aug 10, 2026
dd32ccf
no-mistakes(review): Reject live shell rows during composer extraction
kunchenguid Aug 10, 2026
3b7822e
no-mistakes(review): Preserve wrapped glyph continuations through sub…
kunchenguid Aug 10, 2026
174c3b7
no-mistakes(review): Scope idle placeholders to proven positions
kunchenguid Aug 10, 2026
75dd1d4
no-mistakes(review): Restore boxed placeholders and live prompt reanc…
kunchenguid Aug 10, 2026
d25a77b
no-mistakes(review): Fix Zellij placeholder and wrapped glyph paste p…
kunchenguid Aug 10, 2026
3cea3ae
no-mistakes(document): Align composer architecture documentation
kunchenguid Aug 10, 2026
81cb5df
no-mistakes(lint): Fix ShellCheck warnings in composer refactor
kunchenguid Aug 10, 2026
74d0ede
no-mistakes: apply CI fixes
kunchenguid Aug 10, 2026
59eb8f4
docs(verification): record the trusted-checkout live matrix rerun
kunchenguid Aug 10, 2026
568d205
no-mistakes(document): Align composer verification evidence
kunchenguid Aug 10, 2026
5da7d88
no-mistakes: apply CI fixes
kunchenguid Aug 10, 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
29 changes: 14 additions & 15 deletions .agents/skills/afk/SKILL.md
Original file line number Diff line number Diff line change
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 Down Expand Up @@ -185,11 +185,11 @@ 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 fleet-wide empty-composer placeholder set after shared ghost and border stripping on every backend, and `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
Loading
Loading