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
8 changes: 4 additions & 4 deletions .agents/skills/harness-adapters/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,17 +301,17 @@ The model arms through `fm_watch_arm_pi`, never a foreground bash arm; the watch
`bin/fm-session-start.sh` reports when the live Pi-family session has not loaded both the turn-end guard and watcher extensions, and points at the selected executable after project trust as the fix, with `-e` as a trust-free fallback.
When a secondmate is launched on Pi or pi-signed, `fm-spawn.sh --secondmate` launches the selected executable with both `-e .pi/extensions/fm-primary-turnend-guard.ts` and `-e .pi/extensions/fm-primary-pi-watch.ts`, both already present in the secondmate home's git worktree.

## grok (VERIFIED 2026-06-29, grok 0.2.73; slash-submit re-verified 2026-07-03 on 0.2.82; reasoning-effort ceiling re-verified 2026-07-13 on 0.2.99; exit paths re-verified 2026-07-19 on grok 0.2.103)
## grok (VERIFIED 2026-06-29, grok 0.2.73; slash-submit re-verified 2026-07-03 on 0.2.82; reasoning-effort ceiling re-verified 2026-07-13 on 0.2.99; exit paths re-verified 2026-07-19 on grok 0.2.103; busy-footer and interrupt re-verified 2026-08-02 on Grok 4.5 / Build 0.2.118)

Grok Build TUI (`grok`), a Claude-Code-compatible CLI from xAI.
Launch with a positional prompt: `grok --always-approve "$(cat <brief>)"`.
For Grok's supported reasoning-effort values and omission behavior, see the [launch-profile-axes table](#launch-profile-axes).

| Fact | Value |
|---|---|
| Busy state | The one remaining rendered-tail fallback, isolated to Grok until its structured lifecycle is live-verified: `Ctrl+c:cancel`, the mid-turn cancel hint shown in grok's keybind bar iff a turn is running. The idle bar shows only `Shift+Tab:mode │ Ctrl+.:shortcuts`. ASCII is matched rather than the braille spinner to avoid locale fragility. |
| Exit command | `/exit` typed into the composer exits the TUI cleanly and prints `Resume this session with: grok --resume <session-id>`; `Ctrl+Q` double-press within 1000ms remains a fallback; `Ctrl+D` is the quit key in VS Code family terminals; `Ctrl+C` is the interrupt, not the exit. |
| Interrupt | single `Ctrl+C` (cancels the current turn; the footer shows `Ctrl+c:cancel` mid-turn). `Esc` only moves focus to the scrollback, it does NOT interrupt. |
| Busy state | The one remaining rendered-tail fallback, isolated to Grok until its structured lifecycle is live-verified. Match the mid-turn cancel keybind in the keybind bar (present only while a turn is running). Verified forms are an alternation in `FM_TMUX_GROK_BUSY_REGEX_DEFAULT` (`bin/fm-tmux-lib.sh`, same fallback in `bin/fm-busy-lib.sh`): older `Ctrl+c:cancel`, and Grok 4.5 / Build 0.2.118 `Esc:cancel` (live busy footer `Shift+Tab:mode │ Esc:cancel │ Ctrl+x:shortcuts`). Idle bar has no `:cancel` token (`Shift+Tab:mode │ Ctrl+x:shortcuts` on 0.2.118). ASCII keybind text is matched rather than the braille spinner to avoid locale fragility. Add a new form when the TUI renames the busy footer; do not replace one form with another. |
| Exit command | `/exit` typed into the composer exits the TUI cleanly and prints `Resume this session with: grok --resume <session-id>`; `Ctrl+Q` double-press within 1000ms remains a fallback; `Ctrl+D` is the quit key in VS Code family terminals; a double `Ctrl+C` while idle is quit (`Ctrl+c:press again to quit`), not the mid-turn cancel. |
| Interrupt | single Escape cancels the running turn on Grok 4.5 (footer shows `Esc:cancel` mid-turn; verified 2026-08-02 on Build 0.2.118). A single `Ctrl+C` also cancels a running turn on 4.5. Prefer Escape when the live footer shows `Esc:cancel`. Escape is the cancel key mid-turn, not a scrollback-only focus move. |
| Skill invocation | `/<skill>` (e.g. `/no-mistakes`), same as claude. Opens a slash-autocomplete popup, so a too-fast Enter selects the popup entry instead of sending. For an argument-taking command that first Enter does not submit at all - it expands the selection into an argument-hint placeholder in the composer (e.g. `/compact` -> `/compact compaction instructions`, live-verified), leaving real text still sitting there unsubmitted; a genuine second Enter is required. `fm-send`'s retried Enter lands it on BOTH backends, but only because each backend's own submit-verification correctly recognizes that placeholder-filled text as still-pending - see the incident below. |
| Autonomy | `--always-approve` (footer shows `· always-approve`); auto-approves every tool execution, verified to run fully unattended. `--permission-mode bypassPermissions` is the stronger equivalent. |
| Env marker | `GROK_AGENT=1`, set for child/tool processes. grok does NOT set `CLAUDECODE` despite Claude compatibility, so the marker is unambiguous. |
Expand Down
7 changes: 5 additions & 2 deletions bin/fm-busy-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,15 @@ fm_busy_record_read() { # <state-dir> <id>
}

# fm_busy_grok_tail_busy: the Grok-only temporary rendered-tail fallback.
# Consumes the tail on stdin; 0 when Grok's verified busy signature matches.
# Consumes the tail on stdin; 0 when any verified Grok mid-turn cancel
# keybind form matches. Forms are an ERE alternation owned with
# FM_TMUX_GROK_BUSY_REGEX_DEFAULT in bin/fm-tmux-lib.sh (keep both in sync):
# Ctrl+c:cancel (older) and Esc:cancel (Grok 4.5 / Build 0.2.118+).
# FM_BUSY_REGEX still globally overrides the signature, mirroring the
# historical operator escape hatch.
fm_busy_grok_tail_busy() {
grep -v '^[[:space:]]*$' | tail -12 \
| grep -qiE "${FM_BUSY_REGEX:-${FM_TMUX_GROK_BUSY_REGEX_DEFAULT:-Ctrl\\+c:cancel}}"
| grep -qiE "${FM_BUSY_REGEX:-${FM_TMUX_GROK_BUSY_REGEX_DEFAULT:-(Ctrl\\+c|Esc):cancel}}"
}

# fm_busy_classify: semantic classification for a task whose endpoint the
Expand Down
15 changes: 12 additions & 3 deletions bin/fm-tmux-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
. "$(dirname -- "${BASH_SOURCE[0]}")/fm-composer-lib.sh"

# Delivery-only rendered busy footers per harness. claude/codex: "esc to
# interrupt"; opencode: "esc interrupt"; pi: "Working..."; grok: "Ctrl+c:cancel".
# interrupt"; opencode: "esc interrupt"; pi: "Working..."; grok: mid-turn
# cancel keybind (see FM_TMUX_GROK_BUSY_REGEX_DEFAULT).
# Claude's current spinner has a rotating glyph and word, but every active-turn
# line has an ellipsis followed by a parenthesized elapsed duration. Keep this
# signature separate from the shared default because that shape is not generic
Expand All @@ -82,12 +83,20 @@
# busy signals on their own.
# The full moon-phase set remains locale- and emoji-font-sensitive because Kimi
# exposes no stable ASCII busy token.
FM_TMUX_BUSY_REGEX_DEFAULT='esc (to )?interrupt|Working\.\.\.|Ctrl\+c:cancel'
#
# Grok mid-turn cancel keybind forms are an ERE alternation, not one literal.
# Add a new form when the TUI renames the busy footer; keep prior forms so a
# mixed fleet keeps working. Live-verified 2026-08-02 on Grok Build 0.2.118
# (Grok 4.5): busy footer is "Shift+Tab:mode | Esc:cancel | Ctrl+x:shortcuts",
# idle is the same without Esc:cancel. Older verified form: Ctrl+c:cancel.
# Keep this string identical to the fallback in bin/fm-busy-lib.sh
# fm_busy_grok_tail_busy (semantic task state uses the same forms).
FM_TMUX_GROK_BUSY_REGEX_DEFAULT='(Ctrl\+c|Esc):cancel'
FM_TMUX_BUSY_REGEX_DEFAULT="esc (to )?interrupt|Working\\.\\.\\.|${FM_TMUX_GROK_BUSY_REGEX_DEFAULT}"
FM_TMUX_CLAUDE_BUSY_REGEX_DEFAULT='esc to interrupt|…[[:space:]]+\([0-9]+[smh]'
FM_TMUX_CODEX_BUSY_REGEX_DEFAULT='esc to interrupt'
FM_TMUX_OPENCODE_BUSY_REGEX_DEFAULT='esc interrupt'
FM_TMUX_PI_BUSY_REGEX_DEFAULT='Working\.\.\.'
FM_TMUX_GROK_BUSY_REGEX_DEFAULT='Ctrl\+c:cancel'
FM_TMUX_KIMI_BUSY_REGEX_DEFAULT='^[[:space:]]*(🌑|🌒|🌓|🌔|🌕|🌖|🌗|🌘)[[:space:]]+·[[:space:]]+'

fm_busy_lines_match() { # [harness]
Expand Down
6 changes: 5 additions & 1 deletion tests/fm-busy-adapter-wiring.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,11 @@ test_kimi_and_grok_install_no_unverified_wiring() {
out=$(fm_busy_classify tmux fake:w kimi gate-k "$state" '🌒 · thinking')
[ "$out" = "unknown kimi-unverified" ] || fail "kimi must classify unknown, not from its spinner, got '$out'"
out=$(fm_busy_classify tmux fake:w grok gate-g "$state" 'Ctrl+c:cancel')
[ "$out" = "busy grok-regex" ] || fail "grok must classify through its isolated fallback, got '$out'"
[ "$out" = "busy grok-regex" ] || fail "grok older footer must classify through its isolated fallback, got '$out'"
out=$(fm_busy_classify tmux fake:w grok gate-g45 "$state" 'Esc:cancel')
[ "$out" = "busy grok-regex" ] || fail "grok 4.5 Esc:cancel must classify through its isolated fallback, got '$out'"
out=$(fm_busy_classify tmux fake:w grok gate-idle "$state" 'Shift+Tab:mode │ Ctrl+x:shortcuts')
[ "$out" = "idle grok-regex" ] || fail "grok idle footer without :cancel must be idle, got '$out'"
pass "kimi and grok install no unverified semantic wiring and classify through their own gates"
}

Expand Down
14 changes: 12 additions & 2 deletions tests/fm-busy-state.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,26 @@ Ctrl+c:cancel'
test_grok_regex_isolated() {
local state out
state=$(new_state_dir grok-arm)
# Older footer form still classifies busy (mixed-fleet compatibility).
out=$(fm_busy_classify tmux w1 grok t1 "$state" 'thinking hard
Ctrl+c:cancel')
[ "$out" = "busy grok-regex" ] || fail "grok busy tail must classify 'busy grok-regex', got '$out'"
[ "$out" = "busy grok-regex" ] || fail "older Ctrl+c:cancel busy tail must classify 'busy grok-regex', got '$out'"
# Grok 4.5 / Build 0.2.118 live busy footer (2026-08-02): Esc:cancel.
out=$(fm_busy_classify tmux w1 grok t1 "$state" 'Shift+Tab:mode │ Esc:cancel │ Ctrl+x:shortcuts')
[ "$out" = "busy grok-regex" ] || fail "Grok 4.5 Esc:cancel busy footer must classify 'busy grok-regex', got '$out'"
out=$(fm_busy_classify tmux w1 grok t1 "$state" 'Waiting for response…
Esc:cancel')
[ "$out" = "busy grok-regex" ] || fail "bare Esc:cancel busy token must classify 'busy grok-regex', got '$out'"
# Idle directions: bare prompt, and the live 4.5 idle keybind bar (no :cancel).
out=$(fm_busy_classify tmux w1 grok t1 "$state" 'done.
> ')
[ "$out" = "idle grok-regex" ] || fail "grok idle tail must classify 'idle grok-regex', got '$out'"
out=$(fm_busy_classify tmux w1 grok t1 "$state" 'Shift+Tab:mode │ Ctrl+x:shortcuts')
[ "$out" = "idle grok-regex" ] || fail "Grok 4.5 idle footer without :cancel must classify 'idle grok-regex', got '$out'"
# Another adapter's footer never makes grok busy either.
out=$(fm_busy_classify tmux w1 grok t1 "$state" '• Working (6s • esc to interrupt)')
[ "$out" = "idle grok-regex" ] || fail "a claude footer must not classify grok busy, got '$out'"
pass "the grok fallback is regex-scoped to grok and classifies only grok tasks"
pass "the grok fallback matches both cancel-footer forms and classifies idle without them"
}

# --- kimi verification gate -----------------------------------------------------
Expand Down
23 changes: 18 additions & 5 deletions tests/fm-crew-state.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -827,22 +827,35 @@ test_no_run_footer_text_alone_is_not_working() {

# Grok keeps its isolated temporary rendered-tail fallback until its structured
# lifecycle is live-verified, so a grok crew still reads working from its own
# verified signature.
# verified signature (both older and Grok 4.5 cancel-footer forms).
test_no_run_grok_uses_isolated_fallback() {
reset_fakes
local d; d=$(new_case busy-grok)
local d out
d=$(new_case busy-grok)
make_repo_on_branch "$d/wt" fm/feat-h3
make_fakebin "$d" >/dev/null
fm_write_meta "$d/state/feat-h3.meta" "window=fm:fm-feat-h3" "worktree=$d/wt" "kind=ship" "harness=grok"
FM_FAKE_AXI_STATUS=""
FM_FAKE_RUNS_LIST=""
FM_FAKE_BUSY=1
# Older form.
FM_FAKE_BUSY_TEXT='Ctrl+c:cancel'
export FM_FAKE_BUSY_TEXT
local out; out=$(run_crew_state "$d" feat-h3)
assert_contains "$out" "state: working" "grok busy tail -> working"
out=$(run_crew_state "$d" feat-h3)
assert_contains "$out" "state: working" "grok older Ctrl+c:cancel busy tail -> working"
assert_contains "$out" "grok-regex" "the grok verdict names its isolated fallback source"
pass "grok still reads working through its isolated rendered-tail fallback"
# Grok 4.5 form (live 2026-08-02).
FM_FAKE_BUSY_TEXT='Shift+Tab:mode │ Esc:cancel │ Ctrl+x:shortcuts'
export FM_FAKE_BUSY_TEXT
out=$(run_crew_state "$d" feat-h3)
assert_contains "$out" "state: working" "grok 4.5 Esc:cancel busy footer -> working"
assert_contains "$out" "grok-regex" "the grok 4.5 verdict names its isolated fallback source"
# Idle 4.5 footer must not report working from the rendered tail alone.
FM_FAKE_BUSY_TEXT='Shift+Tab:mode │ Ctrl+x:shortcuts'
export FM_FAKE_BUSY_TEXT
out=$(run_crew_state "$d" feat-h3)
assert_not_contains "$out" "state: working" "grok 4.5 idle footer must not read working"
pass "grok still reads working through both verified cancel-footer forms"
}

test_no_run_herdr_unknown_uses_backend_capture() {
Expand Down
10 changes: 9 additions & 1 deletion tests/fm-tmux-submit-busy.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,16 @@ test_claude_busy_signature_uses_real_capture_shapes() {
printf 'Working...\n' > "$composer"
pane_busy pi pi || fail "Pi Working footer should be busy"
pane_busy pi-signed pi-signed || fail "pi-signed should share Pi's exact Working footer"
# Grok: both verified mid-turn cancel forms classify busy; idle bar does not.
printf 'Ctrl+c:cancel\n' > "$composer"
pane_busy grok grok || fail "Grok cancel footer should be busy"
pane_busy grok-old grok || fail "older Grok Ctrl+c:cancel footer should be busy"
printf 'Shift+Tab:mode │ Esc:cancel │ Ctrl+x:shortcuts\n' > "$composer"
pane_busy grok-45 grok || fail "Grok 4.5 Esc:cancel busy footer should be busy"
printf 'Shift+Tab:mode │ Ctrl+x:shortcuts\n' > "$composer"
pane_busy grok-idle grok && fail "Grok 4.5 idle footer without :cancel must not be busy"
# Shared no-harness fallback still matches both Grok forms.
printf 'Esc:cancel\n' > "$composer"
pane_busy fallback-esc || fail "no-harness fallback should retain Grok 4.5 Esc:cancel"
pass "fm_pane_is_busy: Claude spinner is scoped, multi-frame, and backward-compatible"
}

Expand Down
Loading