Skip to content

Handle Kiro-primary wakes without typing drain - #5

Merged
shishiv merged 5 commits into
mainfrom
fm/fm-kiro-supervisor-desenho
Sep 29, 2026
Merged

shishiv merged 5 commits into
mainfrom
fm/fm-kiro-supervisor-desenho

Conversation

@shishiv

@shishiv shishiv commented Sep 29, 2026

Copy link
Copy Markdown
Owner

Pilha: sobre #4

Summary

When Kiro is the primary, wakes are now handled without anyone typing "drain". Two parts, following the supervisor design in the 28/09 architecture review:

  • Doorbell owner. New bin/fm-primary-doorbell.sh keeps the watcher running. It runs as one detached process per home, kept alive by the primary's SessionStart, UserPromptSubmit, and Stop hooks. Before the old Stop hook, only one loose watcher cycle was started per turn, the watcher rang only for a new queue row, and a recovery close with no row left the home unwatched. The owner starts the next watcher cycle before it rings, and it's the only component that types into the primary pane. It retries a refused ring until main has nothing left to present. The watcher no longer rings, and the model is no longer told to arm. The PreToolUse hook blocks a model-run arm or checkpoint while the owner can run (fm-arm-command-policy gains --owner-held).
  • Supervision host beside Kiro. This puts Kiro on the separate-supervisor model that Claude and Cursor already use. When a home opts into the host, the doorbell owner runs bin/fm-supervision-host.sh park in place of the arm. A headless engine session then takes the wakes the supervision branch may take, and main is rung only for what the host hands back. The dialog mirror gains a Kiro writer: the captain's prompt comes from UserPromptSubmit, and main's reply comes from the session transcript, because Kiro's Stop payload carries no reply text.
  • Host fix. When the first watcher cycle closed before the host's first poll, its status line was never streamed. That made owners waiting for readiness retire the host mid-turn. The host now streams that line as soon as it sees the finished output.

Docs updated: docs/supervision-protocols/kiro-cli.md, docs/supervision-protocols/supervision-host.md, docs/supervision-host.md, docs/turnend-guard.md, docs/watcher-continuity.md, docs/configuration.md, docs/sessionstart-nudge.md, docs/verification/supervision.md, and the Kiro harness reference.

Tests

  • Live on kiro-cli 2.24.1: two separately timed wakes each rang an idle pane and were acknowledged with nobody typing. The first close started its successor with no re-announced downtime.
  • Live with the host: the Kiro mirror recorded both prompts and replies in order. With a real Claude engine, an away wake and an attended routine wake were each handled while the Kiro pane was never rung.
  • Portable suites on this head, all passing (LC_ALL=C bin/fm-test-run.sh ..., failed=0): fm-primary-doorbell, fm-primary-endpoint, fm-supervision-host, fm-host-mirror, fm-supervision-instructions, fm-afk-launch, and fm-arm-pretool-check.
  • bin/fm-lint.sh passes, bin/fm-doc-audience-check.sh reports ok, and LC_ALL=C bin/fm-test-run.sh --check-coverage reports FM_TEST_COVERAGE ok total=244.
  • New opt-in live guards: tests/fm-kiro-primary-live-e2e.test.sh, tests/fm-kiro-host-mirror-live-e2e.test.sh, and tests/fm-kiro-supervision-host-live-e2e.test.sh.

Stack

This PR is stacked on #4, which removes the fork's GitHub Actions. The removal was merged into this branch, so this PR's own diff shows only the supervisor change. After #4 merges, retarget this PR to main.

Delivered without the no-mistakes pipeline, as asked.

…running

A Kiro primary had no component that owned watcher continuity: the Stop hook
forked one loose arm, the watcher rang the doorbell only for a new queue row,
and a recovery close with no row left the home unwatched until the captain
typed. bin/fm-primary-doorbell.sh is that owner: one detached process per home,
kept alive by the primary's SessionStart, UserPromptSubmit, and Stop hooks. It
starts the handling successor before it rings, is the only component that
types into the primary pane, and retries a refused ring until main has
nothing left to present. The watcher no longer rings, the model is no longer
told to arm, and the PreToolUse hook blocks a model-run arm or checkpoint
while the owner can run (the arm policy gains --owner-held).

Verified live on kiro-cli 2.24.1: two separately timed wakes each rang an
idle pane and were acknowledged with nobody typing, and the first close
started its successor with no re-announced downtime.
A first watcher cycle that closed before the host's first poll never had its
status line streamed: await_close only streamed inside its poll loop. A close
the engine then takes keeps the host running silently, so an owner waiting for
readiness (the OpenCode plugin, the omp extension, the Kiro doorbell owner)
retired the host mid-turn and paid for a killed engine turn on every early
wake. The host now streams that line as soon as it sees the finished output.
The Kiro doorbell owner now runs bin/fm-supervision-host.sh park in the arm's
place when a home opts into the host, so a headless engine session takes the
wakes the supervision branch may take and main is rung only for what the host
hands back; the host's lines reach the doorbell turn as UserPromptSubmit
context. Because the owner runs outside the primary's process tree, the host
proves ownership through the endpoint record naming the served pid.

The dialog mirror gains a Kiro writer: the captain's prompt from
UserPromptSubmit, and main's reply from the session transcript, since Kiro's
Stop payload carries no reply text. With the writer proven live from the first
prompt, Kiro joins Claude and Cursor in the attended posture.

Verified live: the Kiro mirror recorded both prompts and replies in order, and
with a real Claude engine an away wake and an attended routine wake were each
handled while the Kiro pane was never rung.
Delete ci.yml, no-mistakes-required.yml, and windows-herdr-spike.yml so
the fork runs no GitHub Actions on pull requests or main pushes.

Keep the local gates working without them: bin/fm-lint.sh skips workflow
lint with a note when the checkout has no .github/workflows directory
(an empty directory still fails), and .no-mistakes.yaml declares
no_ci: true so the pipeline's CI step does not wait for checks that can
never register.

Remove the tests whose only subject was a deleted workflow
(fm-ci-workflow, fm-no-mistakes-required, the Herdr CI step-timeout
case, and the current-workflows parse case), and correct the docs and
the layout skill that linked to or described the deleted CI.
@shishiv
shishiv changed the base branch from fm/fork-remove-actions to main September 29, 2026 12:46
@shishiv
shishiv merged commit 2dee13b into main Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant