# RX agent handoff protocol ## Purpose The RX laptop watches `origin/video-development` with ordinary local Python code. Git polling does not call an LLM, ChatGPT or an agent. A detected task is never executed until the user approves it. The first implementation uses only `ManualExecutor`: it prepares a local task outbox and does not start a model itself. ## Task header `docs/handoff/NEXT_RX_TASK.md` starts with these plain key-value fields: ```text TASK_ID = RF001A_CLKIN_001 TASK_TITLE = Verify RF-001A slave CLKIN SOURCE_BRANCH = video-development EXPECTED_HEAD = REQUIRES_APPROVAL = YES HARDWARE_ACCESS = YES RF_RX = YES RF_TX = NO ALLOW_COMMIT = NO ALLOW_PUSH = RX_RESULT_ONLY RISK_LEVEL = YELLOW ``` `TASK_ID` is immutable and unique. A revised task receives a new ID. `EXPECTED_HEAD` names the known base commit against which the task was written; the watcher requires it to be an ancestor of the fetched task commit. This avoids an impossible self-reference to the commit that contains the header. `ALLOW_PUSH = RX_RESULT_ONLY` permits only a small result publication to `rx-node`; it never permits a push to `video-development`. `ALLOW_COMMIT` governs task changes in the source checkout; a separately authorized result-only commit on `rx-node` follows `ALLOW_PUSH`. `RISK_LEVEL` is an optional plain field used to declare RED actions other than RF TX. The watcher rejects missing, duplicate or invalid fields. It reads the file with `git show origin/video-development:docs/handoff/NEXT_RX_TASK.md`, so it does not need to change the local checkout. ## Safety levels and approval | Level | Operations | Approval rule | |---|---|---| | GREEN | Software-only checks, parsing and local result preparation | The task's normal approval applies | | YELLOW | Hardware inventory, clock, gain or bounded RF RX | The user must choose `[A] Approve` | | RED | RF TX, firmware, CPLD, Zadig, USB driver changes, destructive Git or filesystem operations | A normal approval is insufficient; an executor must require the exact separate action `AUTHORIZE RF TX` or an equivalent task-specific explicit confirmation | The watcher derives RED whenever `RF_TX = YES` and rejects a lower declared level. Firmware, CPLD, Zadig, USB driver changes and destructive actions must declare `RISK_LEVEL = RED`. A non-RF RED approval requires `AUTHORIZE `; RF TX requires exactly `AUTHORIZE RF TX`. The watcher cannot authorize RED itself, and the executor must reject a body/header conflict. At the prompt the minimum choices are: ```text [A] Approve [D] Defer [S] Stop watcher ``` The watcher records the task ID before leaving the prompt. Deferred and already seen IDs are not shown again on every polling cycle. A changed task must have a new `TASK_ID`. ## Local state and executor boundary Runtime state is stored in `.local/rx_agent_state.json`; prepared prompts and result skeletons are stored in `.local/rx_agent_outbox//`. `.local/` is ignored by Git. State includes `last_seen_task_id`, `last_completed_task_id`, `deferred_task_id`, `running` and the watcher PID. Writes use a temporary file followed by an atomic replace. `TaskExecutor` is the backend interface. Version 1 provides `ManualExecutor`, which writes `TASK.md`, `RX_RESULT.md` and `RX_RESULT.json` after approval. It refuses to overwrite an existing task outbox. It does not invoke an agent, access SDR hardware, commit or push. A `CodexExecutor` may be added only after the Codex CLI is found and tested on the RX laptop. Availability on the main PC is not sufficient evidence. After a human or separately authorized executor finishes the task, mark the local ID complete: ```powershell python -B tools/rx_agent_watcher.py --mark-completed ``` ## Result and `rx-node` branch The result follows [RX_RESULT_TEMPLATE.md](RX_RESULT_TEMPLATE.md) and has matching Markdown and JSON. The proposed publication branch is `rx-node`. The RX laptop must never commit or push to `video-development`. A separately approved publication step may place only these items on `rx-node`: - handoff result Markdown; - result JSON; - small log metadata needed to interpret the result. Raw IQ, video, waveform files, large logs, secrets and machine-local state remain outside Git. The watcher does not create `rx-node`, switch branches, commit or push. This keeps branch creation and publication subject to the repository's current instructions and a separate explicit authorization. The main PC later uses `git fetch origin rx-node` and reads the result without merging it blindly. ## Token and model behavior - Git polling itself does not call an LLM. - When no new task exists, no model runs. - A detected but unapproved or deferred task does not run a model. - After approval, `ManualExecutor` only prepares an outbox. A separately selected executor may invoke one agent. - The executor stops after task completion. The watcher returns to Git polling and consumes no model tokens. ## Dry-run contract Dry-run requires a local `--task-file`; it does not fetch Git, access SDR, invoke an agent, perform RF RX/TX, commit or push. The tracked fake task is `tests/fixtures/watcher_test_task.md` with `TASK_ID = WATCHER_TEST_001` and `RF_TX = NO`.