Doctrine
Idle-heartbeat loop in feed-based (`local_external`) mode — sleep, don't stop
Idle-heartbeat loop in feed-based (local_external) mode — sleep, don't stop
The rule
When feed-bracket close-read shows no claimable work for your lane + warm context + capacity, DO NOT END THE TURN. Enter an idle-heartbeat loop:
1. sleep 120 (platform equivalent wait; 120 seconds default). 2. Re-read the fleet feed (fleet-active-queue + grep for recent DONE transitions that may have unblocked downstream tickets). 3. If claimable work now exists → CLAIM and work (loop back into ORA-2026-0015 turn discipline). 4. If feed is still empty → repeat from step 1.
Exit conditions:
- A new claimable ticket appears (→ claim + work).
- Your project backlog is completely empty (no AWAITING_CLAIM items
- Explicit operator retirement signal (
tram_retire-equivalent or feed post
remaining across your project, no unblocked BLOCKED items under your warm context, no parent dependencies about to close).
from Chad/STRAT naming you for retire).
Why this doctrine is needed
Boot Protocol §5a already documents this loop pattern, but the vocabulary is TRAM-based (tram_unread, session_heartbeat, session_list). Current fleet runs local_external mode (FLEET_FEED.md). Seats reading AGENTS.md / GEMINI.md / CLAUDE.md see the feed-bracket rule (ORA-2026-0015) which says "loop back to work IF claimable," but have no adapted rule for the "sleep-and-retry if not claimable" half of the pattern. They default to end-of-turn on an empty read, which is a doctrine violation equivalent to the CAMBER-10 failure (FAL ORA-2026-0020).
Symptoms of violation
- Seat finishes a ticket, posts DONE, closing feed-bracket read shows empty
- Operator observes "no one is working" despite multiple CLAIMED tickets,
- STRAT has to pressure or re-paste to wake seats for work that should have
queue, seat stops — even though their project backlog has BLOCKED items that will unblock within minutes as other seats finish upstream work.
because seats are idle between turns instead of resting-and-resuming.
self-absorbed.
Correct behavior
- After DONE posting, run feed-bracket close read.
- If claimable → claim + work (ORA-2026-0015).
- If nothing claimable but project backlog has BLOCKED items →
sleep 120, - If nothing claimable AND project backlog exhausted → post a
IDLE_HOLDING - Only terminate session on explicit retirement signal or platform
re-read, look for unblocks; repeat.
or equivalent liveness note, then sleep 120 loop (seat is reachable for fresh dispatches, not dead).
constraint (e.g., session timeout).
Platform notes
- Codex CLI / Claude CLI: bash
sleep 120in the turn-loop framing; use - Codex Desktop / Claude Desktop: these sessions do not auto-loop in the
- Gemini CLI: same as Codex CLI pattern.
fleet-active-queue --since-minutes 5 on re-check to catch recent DONE transitions that may have unblocked work.
same way; they typically need an operator prompt to continue. In Desktop mode, the wake packet should explicitly include this idle-heartbeat instruction, or the seat should be asked to run a loop within a single long-lived turn (using ScheduleWakeup-equivalent or self-spawned heartbeat subprocess).
Verification
Seat has internalized the rule when:
- Between CLAIMED and next-CLAIMED posts, there's either active work mtime
- Zero "end-of-turn-on-empty-queue" failures when BLOCKED items in the
activity OR a visible sleep/wait gap that's explained by this loop.
seat's project are about to unblock.
Related
- ORA-2026-0015 — Feed-bracket every turn. This doctrine extends the
- ORA-2026-0019 — Wake-work pairing. Wake packets in feed-based mode
- Boot Protocol §5a — Original TRAM-based formulation of this loop.
claim-trigger rule with a sleep-and-retry clause for the empty-queue case.
should include the idle-heartbeat rule explicitly if the seat is expected to work through a multi-ticket backlog autonomously.