0082 — Each seat publishes a heartbeat to a shared row and checks its sibling's; a stale or behind sibling is reminded once and never handed work

1. The problem

Smith runs on the mini and Trinity runs the same agent-harness code on Fede's machine. On 2026-11-01, three cross-seat protocol changes landed in one day:

Trinity follows each one only after Fede pulls main and restarts her. The mini had no record of her running commit. Each worker writes its boot commit (deploy_state.record_boot) only into its own state dir. So fleet_deploy reads her as UNKNOWN, and the only way to learn her version was for her to say it in Slack.

2. The decision

  1. Every seat publishes a heartbeat to one shared row. The row lives in the DynamoDB table the Construct's AutomationRuns already use (propflow-prod, via automation_run._table_name), at PK = SEAT_HEARTBEAT#<registry slug>, SK = #LATEST. It is overwritten in place, at boot and every 5 minutes, from a daemon thread in the worker.
    • Contents: seat, the boot commit the worker declared, boot time, last-turn time, host, and a protocol number.
    • Why this table: both machines already write it. Fede's machine records granola-transcripts-poll runs there every ~5 minutes (host Federicos-Mac-mini.local). So this adds no credential, no daemon and no Slack traffic.
    • Why not the alternatives:
      • The relay is reachable only from the mini.
      • Slack messages are a store nobody can query cheaply.
      • A Temporal query needs the other seat's worker alive to answer, so a dead seat would read as an error rather than as "silent since T".
  2. A protocol number, bumped by any PR that changes how the seats talk. seat_heartbeat.PROTOCOL_CHANGES maps each version to one line saying what it adds. The version is its newest key, so the two cannot drift. A seat on a lower number is behind even when it is alive. Version 3, the first published, covers the three changes above plus the heartbeat itself (agent-harness #1810). Amended 2026-11-02: the sibling check and its reminders are version 4 (agent-harness #1813), so a seat on #1810 alone reads behind and is reminded to pull them.
  3. Each seat checks its sibling before handing it work or asking it for a cross-review, and every ~15 minutes. A sibling is ok only when its row was written in the last 15 minutes AND its protocol is at least the checker's. Missing, stale, behind and unreadable are all not ok (ADR-0011: absence never reads as health).
  4. One reminder per episode, then people, then stop. An episode opens when the sibling stops being ok, and closes when it is ok again.
    • At once: one line on the engineering bridge, tagging the sibling's bot. It names the sibling's commit and protocol, the checker's commit and protocol, and what the newer protocol adds, and asks it to pull and restart.
    • About 30 minutes later, if still open: one message in the checker's own home channel, tagging the person who runs the sibling's machine.
    • About 3 hours later, if still open: one message tagging the checker's own person.
    • Then nothing more until the episode closes. For Smith checking Trinity that ladder is Trinity → Fede → Gera. For Trinity checking Smith it is Smith → Gera → Fede.
    • Why the people go to the home channel, not the bridge: the bridge never pages a person (agent-harness #937 rewrites every human mention there to a plain name), so a mention of Fede there would silently not reach him. Each seat can always post to its own home channel, and fleet health belongs in #agent-smith (ADR-0056 agent-smith-channel, fleet-down).
  5. It is symmetric. Trinity runs the same code, so she checks Smith the same way, under her registry slug (trinity, not her boot slug clara).
  6. No work is handed to a seat that isn't ok. A cross-review request or a handoff to a sibling that is not ok is not sent. The seat keeps the work and says so in the original thread. A PR that would have waited on the sibling's risk label waits on a person instead, which is where the merge gate already sends a PR with no reviewer label.

3. What this does not do

4. Signals

The SYSTEMS.md seat-heartbeat entry matches SEAT-HEARTBEAT published in the mini's worker log. The line is written on every successful publish, so it goes stale only when publishing stops. smith doctor shows both seats' rows ("Seat heartbeats"), so either machine can see the other's commit and protocol.