Doctrine

Feed-bracket every turn — DONE is a pivot, not a stop

feed-protocolturn-disciplinefleet-coordinationanti-idle

Feed-bracket every turn — DONE is a pivot, not a stop

The rule (strengthened 2026-04-16 after FAL ORA-2026-0020)

Open every turn with a feed read. Close every turn with a feed read.

The turn is NOT complete until one of two post-conditions holds: 1. You claimed an absorbable ticket and started working it (loop back into work), OR 2. The feed verifiably has nothing for you — no AWAITING_CLAIM item matches your lane + warm context + capacity.

Observing that absorbable follow-ons exist is NOT a completion state — it is a CLAIM TRIGGER. "Closing feed read shows N follow-ons unclaimed" is the precondition for your next claim action, not the end of your turn. Reporting the observation to the operator without claiming one of the tickets is a doctrine violation (sourced by FAL ORA-2026-0020 — CODEX-DESKTOP-MacBook-Air-CAMBER-10 did exactly this on 2026-04-16 after filing CMB-0095/0096/0097 as follow-ons to CMB-0092).

Self-filed follow-ons default to self-claim. If you filed a ticket as a follow-on to work you just shipped, and it is sitting AWAITING_CLAIM with you having the warmest context in the fleet, the default action is claim-self. Release to another seat is an affirmative, explicit exception (capacity cap, declared handoff contract, cross-lane expertise) — not a passive outcome of stopping. If you filed it, you claim it unless you have a named reason not to.

Idle seats beside a claimable queue are dead weight. This includes idle seats that know the queue is claimable because they just read it.

Mechanics

Every seat's turn is a feed sandwich:

1. Open — pull the active queue (/Users/chadbarlow/Desktop/fleet/scripts/fleet-active-queue or equivalent). Orient on current state before starting work. 2. Work — execute the claimed ticket to DONE. 3. Close — re-pull the queue after posting DONE.

  • If it shows a claimable item matching your seat, claim it and loop back to step 2. Do NOT report the observation as a terminal state. Claiming is the response; reporting is secondary.
  • Self-filed follow-ons from step 2 are first-priority claim targets unless explicitly released (declared handoff, capacity cap, or another seat has warmer context).
  • 4. Exit — ONLY when both conditions hold: (a) no AWAITING_CLAIM item matches your seat, AND (b) you can truthfully report "feed has nothing for me" — not "feed has X, I chose not to claim."

"Claim through" is the verb. DONE is never the end of a turn; the next feed check is. A feed check that reveals absorbable work is a CLAIM TRIGGER, not a STATUS REPORT. Resolving a claim trigger by reporting instead of claiming is the failure mode this rule exists to prevent.

What counts as "claimable for you"

A ticket is claimable for your seat when ALL of:

  • State: AWAITING_CLAIM (not CLAIMED, not BLOCKED, not DONE)
  • Lane match: your declared lane OR cross-lane if you have relevant warm context
  • Warm context: you recently touched adjacent code, recently shipped a related ticket, or are named in the OWNER field
  • Capacity: you are not mid-claim on another ticket; no hard-blocker sitting on your plate

When multiple items qualify, prefer: 1. CRITICAL > HIGH > NORMAL > LOW 2. Items explicitly targeting your seat (OWNER: <your-seat>) over open-ownership 3. Items warmest to your most recent work

Anti-patterns this doctrine kills

  • Post-and-ghost: posting DONE and going idle while the queue has CRITICAL waiting for you
  • Check-and-report (sourced by FAL ORA-2026-0020): running the closing feed read, observing absorbable follow-ons, and reporting that observation to the operator as a terminal state instead of claiming one. The check is the precondition for the claim, not a replacement for it.
  • Self-filed-and-abandoned (sourced by FAL ORA-2026-0020): filing follow-on tickets from your own work findings and then leaving them unclaimed when you have the warmest possible context and full capacity. The follow-ons you wrote are YOUR claim queue unless explicitly released.
  • Single-claim sessions: one ticket per session and then logging off, when the queue has more work you could absorb
  • "I'll check later" — the feed IS the later. Check now.
  • Cold-idle ticks by seats with warm context when similar work is AWAITING_CLAIM
  • Narrow-lane refusal — "that's not my ticket" when the lane boundary is soft and you have the context
  • Operator-proximity drift: when reporting to the operator, shifting into "status update" mode and treating observations as conclusions. The queue is the interlocutor that demands action; the operator just reads the log.

Why this matters

Fleet throughput is seat-hours × claim-density. A seat that claims one ticket per 3h of session time is ~30% utilized. A seat that feed-brackets and claims through gets to 70%+ — same session budget, more work shipped. The STRAT shepherd can nudge stale AWAITING_CLAIM items, but the nudge is a 20min lag. Seats self-cycling through the feed close the loop in seconds.

This is also why overnight cron meshes (ORA-2026-0011, -0012) worked at all: the Codex seats were feed-bracketing automatically. When a seat doesn't bracket, the overnight cadence collapses to one-ticket-per-fire and the cron's value evaporates.

What this is NOT

  • Not forced claiming. If the queue truly has nothing for you, your turn ends. "Nothing for you" = no AWAITING_CLAIM matches your lane OR no warm-context match OR you're at capacity.
  • Not scope-poaching. Don't claim outside your lane just because you can; warm context and lane match are both required.
  • Not a STRAT function. STRAT doesn't claim — STRAT coordinates. This doctrine applies to the code-landing seats (Codex) and verification-landing seats (Gemini as verifier).
  • Not permission to skip BOOT/RETIRE protocols. Feed-bracketing wraps work turns; the session-open BOOT and session-close RETIRE are still distinct.

Enforcement hook

  • Memory: feedback_feed_bracket_every_turn.md (this session, 2026-04-16) — pointer from all three agent memories:
  • Claude: ~/.claude/projects/-Users-chadbarlow/memory/MEMORY.md (top-of-list prominence)
  • Codex: ~/.codex/AGENTS.md (near §0)
  • Gemini: ~/.gemini/GEMINI.md (head section, after bundle stamp)
  • Cron: STRAT shepherd cron's L7 pattern scan will flag seats in E8 (fleet silence) that had a DONE-and-dark pattern (posted DONE with claimable queue remaining).

Evidence trail

  • 2026-04-15 overnight cron-mesh cycle: where it worked, it worked BECAUSE Codex seats were feed-bracketing (claiming through as tickets DONE'd).
  • 2026-04-16 morning session: CAMBER-04 visible pattern — 1h silence after CMB-0072 DONE despite 3 HIGH items AWAITING_CLAIM on their queue. Surfaces the cost of a seat not feed-bracketing: STRAT had to post a consolidated nudge + scope amendment, and the cadence lag was ~60min. Had CAMBER-04 feed-bracketed, the nudge would have been unnecessary.
  • Chad directive 2026-04-16T~00:57Z: codify as doctrine at top of every agent's memory. Original ratification.
  • 2026-04-16T18:23-18:45Z: FAL ORA-2026-0020CODEX-DESKTOP-MacBook-Air-CAMBER-10 completed CMB-0092 (Beside pressure test), filed CMB-0095/0096/0097 as follow-ons, ran the end-of-turn feed-bracket read per this doctrine, observed all three follow-ons unclaimed, and stopped — reporting "Closing feed read shows those three follow-ons are still unclaimed" to the operator as if that were a completion state. The existing conditional language ("if absorbable, claim") was interpreted as "if absorbable, report." CAMBER-03 ended up absorbing CMB-0095 within 75 seconds (warm context from FLT-0002), demonstrating the tickets were fleet-claimable but CAMBER-10 had the warmest context and should have been the claimant. Chad directive 2026-04-16T~18:45Z: "our doctrine about not ending turn before checking and executing follow on work needs to be more direct about not stopping the turn when there is follow on."
  • 2026-04-16 afternoon amendment: rule strengthened to hard post-condition (turn NOT complete until claim OR feed-verifiably-empty); self-filed-self-claim default added; check-as-claim-trigger vs check-as-status-report distinction formalized.

Promote to M4 when enforcement lands — either in queue-drift-check (automated flag for seats that post DONE with self-filed AWAITING_CLAIM follow-ons still unclaimed in their lane), or via shepherd cron L8 pattern scan for "DONE → closing-read-reports-absorbable → no follow-up CLAIMED within N minutes."

Known exceptions

  • Explicit handoff contracts — a seat may DONE-and-stop when the next ticket is part of an explicit sequencing agreement (e.g. "CMB-0076 is CAMBER-04's but must wait for CMB-0077 from CAMBER-01"). In those cases the seat isn't going idle; they're waiting on a defined gate.
  • Capacity-cap sessions — a seat that has declared a WIP=1 for the session (ATC mode) intentionally caps at one ticket. Feed-bracketing still applies (check the feed before/after), but the claim-through step is skipped.
  • End-of-session — if the session is genuinely ending (logoff, reboot, handoff), feed-bracketing ends. The last feed check reports "no more work, retiring" rather than continuing.

Review cadence

  • Next review: 2026-05-16 (30 days).
  • Review triggers: any of — (a) a seat's observed claim-density doesn't improve post-codification, (b) false-positive "should have claimed" observations where the seat genuinely had no warm context, (c) a seat reports feed-bracket collisions (two seats racing for the same open ticket — in which case ORA-2026-0012 ticket-allocation protocol handles it).