From d39307dc3f1ac2a318a9f1c5271f8659f3a4f2a6 Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 00:53:33 +0100 Subject: [PATCH 1/9] fix(relay): harden autonomous relay prompts and monitor behavior --- docs/ai/PROMPT_TEMPLATE.md | 2 + docs/ai/RELAY_PROMPT_TEMPLATE.md | 4 +- docs/ai/RELAY_QUICKSTART.md | 34 +++++---- docs/ai/RELAY_RUNBOOK.md | 3 +- docs/ai/WORKFLOW.md | 2 +- scripts/relay-monitor.sh | 126 ++++++++++++++++++++++++++++++- scripts/relay-prompts.sh | 24 +++++- scripts/relay-run.sh | 2 +- scripts/setup_relay_runtime.sh | 2 +- 9 files changed, 173 insertions(+), 26 deletions(-) diff --git a/docs/ai/PROMPT_TEMPLATE.md b/docs/ai/PROMPT_TEMPLATE.md index b87555c4..b36bcea6 100644 --- a/docs/ai/PROMPT_TEMPLATE.md +++ b/docs/ai/PROMPT_TEMPLATE.md @@ -92,4 +92,6 @@ Operator UX contract (mandatory): - If `ACTION NEEDED` is `none`, keep the rest of the update to at most five concise lines. - If `ACTION NEEDED` is not `none`, print that line before any other content. - Do not ask the maintainer to extract machine/runtime internals from logs; provide exact values directly. +- If relay prompt artifacts are needed, do not ask maintainer for source-path discovery; provide one exact command: + `scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id ` - On completion, proactively emit final delivery package (summary, verification evidence, commit-ready status, and exact approval text when needed) without waiting for maintainer prodding. diff --git a/docs/ai/RELAY_PROMPT_TEMPLATE.md b/docs/ai/RELAY_PROMPT_TEMPLATE.md index f74e2650..3007310a 100644 --- a/docs/ai/RELAY_PROMPT_TEMPLATE.md +++ b/docs/ai/RELAY_PROMPT_TEMPLATE.md @@ -67,7 +67,7 @@ Prompt/report artifact rules: - Do not emit “will do X” updates unless the prior event already emitted “did X” evidence for the previous action. - Runtime event labels should prefer explicit completion facts (`worker_command_started`, `worker_command_completed`, `worker_command_failed`, `unit_completed`, `unit_failed`, `retry_scheduled`, `workflow_completed`, `workflow_failed`). - Before requesting maintainer approval to dispatch `developer_run`, you MUST stage both relay prompt artifacts for this run_id: - - `scripts/relay-prompts.sh stage --run-id --developer --auditor ` + - `scripts/relay-prompts.sh stage --run-id --auto` - Then verify with `scripts/relay-prompts.sh verify --run-id ` - Do not claim dispatch/start for `developer_run` without runtime evidence (`unit_queued`, `lease_acquired`, or `worker_command_started`) and its `history_seq`. @@ -134,7 +134,7 @@ Non-negotiable operator UX contract: - On run start/resume, provide exactly one copy-paste command line for runtime launch: - `scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2` - If prompt artifacts are required, provide exactly one copy-paste command line for staging+verify: - - `scripts/relay-prompts.sh stage --run-id --developer --auditor ;scripts/relay-prompts.sh verify --run-id ` + - `scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id ` - Do not require maintainer to query runtime internals (run_id lookup, idempotency lookup, history parsing); architect must provide exact values. - On `workflow_completed`, immediately emit final delivery package without maintainer prodding: summary, pass/fail, commit-ready status, commit-message approval line (or `none`), PR/CI status, and cleanup commands. ``` diff --git a/docs/ai/RELAY_QUICKSTART.md b/docs/ai/RELAY_QUICKSTART.md index e2376870..4e6c3d09 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -1,20 +1,20 @@ # Relay Quickstart (Minimal) -## One-time (or after relay config/unit changes) +## [BASH] One-time (or after relay config/unit changes) ```bash cd ~/ytree scripts/setup_relay_runtime.sh ``` -## Each working session +## [BASH] Each working session ```bash cd ~/ytree scripts/relay-workers.sh start ``` -## Prompt source (AI chat/IDE) +## [IDE] Prompt source (AI chat/IDE) - Non-trivial work: `docs/ai/RELAY_PROMPT_TEMPLATE.md` - Single quick unit: `docs/ai/PROMPT_TEMPLATE.md` @@ -22,7 +22,7 @@ scripts/relay-workers.sh start In IDE, open a fresh AI thread and paste the task-customized template. Do not run prompt text in bash. -## Start a durable run +## [BASH] Start a durable run ```bash cd ~/ytree @@ -34,15 +34,15 @@ Expected output: - `RUN STARTED: ` (or `RUN RESUMED: `) - `PROMPT ARTIFACTS READY: ` **or** `PROMPT ARTIFACTS PENDING: ` -If prompt artifacts are pending, stage them: +If prompt artifacts are pending, stage them in bash: ```bash cd ~/ytree -scripts/relay-prompts.sh stage --run-id --developer --auditor +scripts/relay-prompts.sh stage --run-id --auto scripts/relay-prompts.sh verify --run-id ``` -## Optional monitoring (single terminal) +## [BASH] Optional monitoring (single terminal) ```bash cd ~/ytree @@ -59,10 +59,11 @@ scripts/relay-monitor.sh --run --view verbose - `quiet` = status + `ACTION NEEDED` only - `normal` = key transitions - `verbose` = full stream (includes heartbeats) +- on terminal completion/failure, monitor prints report handles and an exact IDE fallback line if IDE goes silent Stop monitor with `Ctrl+C`. -## What to look for in AI updates +## [IDE] What to look for in AI updates Every update should include exactly one explicit line: @@ -71,7 +72,7 @@ Every update should include exactly one explicit line: (“maintainer” means you.) -## Finish / shutdown +## [BASH] Finish / shutdown ```bash cd ~/ytree @@ -80,10 +81,11 @@ scripts/relay-workers.sh stop ## Numbered flow -1. Run setup once: `scripts/setup_relay_runtime.sh` -2. Start workers for the session: `scripts/relay-workers.sh start` -3. Paste task prompt in IDE (not bash) -4. Run the exact `scripts/relay-run.sh ...` line -5. If pending, run `scripts/relay-prompts.sh stage ...` then `verify` -6. Optional monitor: `scripts/relay-monitor.sh --run --view quiet` -7. When done: `scripts/relay-workers.sh stop` +1. [BASH] Run setup once: `scripts/setup_relay_runtime.sh` +2. [BASH] Start workers for the session: `scripts/relay-workers.sh start` +3. [IDE] Paste task prompt in IDE (not bash) +4. [BASH] Run the exact `scripts/relay-run.sh ...` line +5. [BASH] If prompt artifacts are pending, stage prompts: `scripts/relay-prompts.sh stage --run-id --auto` +6. [BASH] Verify prompts: `scripts/relay-prompts.sh verify --run-id ` +7. [BASH] Optional monitor: `scripts/relay-monitor.sh --run --view quiet` +8. [BASH] When done: `scripts/relay-workers.sh stop` diff --git a/docs/ai/RELAY_RUNBOOK.md b/docs/ai/RELAY_RUNBOOK.md index c7760ae3..502ac334 100644 --- a/docs/ai/RELAY_RUNBOOK.md +++ b/docs/ai/RELAY_RUNBOOK.md @@ -31,7 +31,7 @@ If prompt artifacts are still pending, stage them immediately: ```bash cd ~/ytree -scripts/relay-prompts.sh stage --run-id --developer --auditor +scripts/relay-prompts.sh stage --run-id --auto scripts/relay-prompts.sh verify --run-id ``` @@ -46,6 +46,7 @@ Detail levels: - `quiet`: status + `ACTION NEEDED (maintainer)` only - `normal`: key transitions (heartbeat noise filtered) - `verbose`: full event stream including heartbeats +- on terminal completion/failure, monitor includes report handles plus an exact IDE fallback line if IDE goes silent Convenience: - omit `--run` to monitor the latest run in durable relay state. diff --git a/docs/ai/WORKFLOW.md b/docs/ai/WORKFLOW.md index 2b353dcf..6a873f2f 100644 --- a/docs/ai/WORKFLOW.md +++ b/docs/ai/WORKFLOW.md @@ -273,7 +273,7 @@ The run wrapper auto-loads relay env and prints `RUN STARTED: ` (or `RUN If pending, stage them with: ```bash -scripts/relay-prompts.sh stage --run-id --developer --auditor +scripts/relay-prompts.sh stage --run-id --auto scripts/relay-prompts.sh verify --run-id ``` diff --git a/scripts/relay-monitor.sh b/scripts/relay-monitor.sh index ee4a8a2b..ca55fa9e 100644 --- a/scripts/relay-monitor.sh +++ b/scripts/relay-monitor.sh @@ -20,11 +20,23 @@ INTERVAL=3 LIMIT=20 FOLLOW=1 AUTO_SELECT_RUN=0 +SOUND=0 +SOUND_INPUT="${RELAY_MONITOR_SOUND_INPUT:-}" +SOUND_FAIL="${RELAY_MONITOR_SOUND_FAIL:-}" +SOUND_SUCCESS="${RELAY_MONITOR_SOUND_SUCCESS:-}" +LAST_NOTIFIED_SEQ="" +LAST_NOTIFIED_ACTION="" usage() { cat <<'USAGE' Usage: scripts/relay-monitor.sh [--run ] [--view quiet|normal|verbose] [--interval ] [--limit ] [--once] +Sound options: + --sound enable sound notifications + --sound-input sound when maintainer input is needed + --sound-fail sound when workflow fails/errors + --sound-success sound when workflow completes + Defaults: --run latest run in relay DB --view normal @@ -55,6 +67,25 @@ while [[ $# -gt 0 ]]; do FOLLOW=0 shift ;; + --sound) + SOUND=1 + shift + ;; + --sound-input) + SOUND=1 + SOUND_INPUT="${2:-}" + shift 2 + ;; + --sound-fail) + SOUND=1 + SOUND_FAIL="${2:-}" + shift 2 + ;; + --sound-success) + SOUND=1 + SOUND_SUCCESS="${2:-}" + shift 2 + ;; -h|--help) usage exit 0 @@ -82,6 +113,10 @@ if [[ -f "$HOME/.config/ytree/relay.env" ]]; then set +a fi +if [[ "$SOUND" -eq 0 && (-n "${SOUND_INPUT:-}" || -n "${SOUND_FAIL:-}" || -n "${SOUND_SUCCESS:-}") ]]; then + SOUND=1 +fi + cd "$REPO_ROOT" if [[ -z "$RUN_ID" ]]; then @@ -165,8 +200,27 @@ unit = last.get('unit_id', '-') ts = last.get('ts_utc_iso', '-') print(f"LATEST: seq={seq} event={event} status={status} unit={unit} at={ts}") +def find_report_handle(unit_id: str) -> str: + for row in reversed(rows): + if row.get("event_type") != "unit_completed": + continue + if row.get("unit_id") != unit_id: + continue + msg = str(row.get("message", "")) + marker = "report_handle=" + idx = msg.find(marker) + if idx < 0: + continue + remainder = msg[idx + len(marker):] + return remainder.split(";", 1)[0].strip() + return "-" + action = "none" -if last.get('event_type') not in ('workflow_completed', 'workflow_failed'): +if event == "workflow_completed": + action = f'if IDE is silent, reply "provide final delivery package now for run_id {run_id} from history_seq {seq}"' +elif event == "workflow_failed": + action = f'if IDE is silent, reply "explain workflow failure for run_id {run_id} from history_seq {seq}"' +else: for row in reversed(rows): msg = str(row.get('message', '')) msg_l = msg.lower() @@ -179,6 +233,11 @@ if last.get('event_type') not in ('workflow_completed', 'workflow_failed'): break print(f"ACTION NEEDED (maintainer): {action}") +if event in ("workflow_completed", "workflow_failed"): + dev_report = find_report_handle("developer_run") + auditor_report = find_report_handle("auditor_run") + print(f"REPORTS: developer={dev_report} auditor={auditor_report}") + if view == 'quiet': sys.exit(0) @@ -202,15 +261,76 @@ PY )" "$RUN_ID" "$VIEW" "$LIMIT" } +play_sound_file() { + local sound_file="$1" + if [[ -z "$sound_file" || ! -f "$sound_file" ]]; then + printf '\a' + return 0 + fi + if command -v ffplay >/dev/null 2>&1; then + (ffplay -nodisp -autoexit -loglevel quiet "$sound_file" >/dev/null 2>&1 &) + elif command -v mpv >/dev/null 2>&1; then + (mpv --really-quiet --no-video "$sound_file" >/dev/null 2>&1 &) + elif command -v mpg123 >/dev/null 2>&1; then + (mpg123 -q "$sound_file" >/dev/null 2>&1 &) + elif command -v play >/dev/null 2>&1; then + (play -q "$sound_file" >/dev/null 2>&1 &) + else + printf '\a' + fi +} + +notify_from_rendered() { + local rendered="$1" + [[ "$SOUND" -eq 1 ]] || return 0 + + local latest_line action_line seq event action + latest_line="$(printf '%s\n' "$rendered" | sed -n 's/^LATEST: /LATEST: /p' | head -n1)" + action_line="$(printf '%s\n' "$rendered" | sed -n 's/^ACTION NEEDED (maintainer): /ACTION NEEDED (maintainer): /p' | head -n1)" + seq="$(printf '%s\n' "$latest_line" | sed -n 's/^LATEST: seq=\([^ ]*\) .*/\1/p')" + event="$(printf '%s\n' "$latest_line" | sed -n 's/^LATEST: seq=[^ ]* event=\([^ ]*\) .*/\1/p')" + action="${action_line#ACTION NEEDED (maintainer): }" + + if [[ -n "$seq" && "$seq" != "$LAST_NOTIFIED_SEQ" ]]; then + case "$event" in + workflow_completed) + play_sound_file "$SOUND_SUCCESS" + LAST_NOTIFIED_SEQ="$seq" + LAST_NOTIFIED_ACTION="$action" + return 0 + ;; + workflow_failed|unit_failed|worker_command_failed) + play_sound_file "$SOUND_FAIL" + LAST_NOTIFIED_SEQ="$seq" + LAST_NOTIFIED_ACTION="$action" + return 0 + ;; + esac + fi + + if [[ "${action:-none}" != "none" && "${action:-}" != "$LAST_NOTIFIED_ACTION" ]]; then + play_sound_file "$SOUND_INPUT" + fi + + if [[ -n "$seq" ]]; then + LAST_NOTIFIED_SEQ="$seq" + fi + LAST_NOTIFIED_ACTION="${action:-none}" +} + if [[ "$FOLLOW" -eq 0 ]]; then refresh_auto_selected_run - render_once + rendered_once="$(render_once)" + printf '%s\n' "$rendered_once" + notify_from_rendered "$rendered_once" exit $? fi while true; do refresh_auto_selected_run clear - render_once || true + rendered_loop="$(render_once || true)" + printf '%s\n' "$rendered_loop" + notify_from_rendered "$rendered_loop" sleep "$INTERVAL" done diff --git a/scripts/relay-prompts.sh b/scripts/relay-prompts.sh index 47363a39..949fac75 100644 --- a/scripts/relay-prompts.sh +++ b/scripts/relay-prompts.sh @@ -3,6 +3,7 @@ # # Usage: # scripts/relay-prompts.sh stage --run-id --developer --auditor +# scripts/relay-prompts.sh stage --run-id --auto # scripts/relay-prompts.sh verify --run-id set -euo pipefail @@ -13,11 +14,13 @@ MODE="" RUN_ID="" DEVELOPER_SRC="" AUDITOR_SRC="" +AUTO_STAGE=0 usage() { cat <<'USAGE' Usage: scripts/relay-prompts.sh stage --run-id --developer --auditor + scripts/relay-prompts.sh stage --run-id --auto scripts/relay-prompts.sh verify --run-id USAGE } @@ -49,6 +52,10 @@ while [[ $# -gt 0 ]]; do AUDITOR_SRC="${2:-}" shift 2 ;; + --auto) + AUTO_STAGE=1 + shift + ;; -h|--help) usage exit 0 @@ -82,8 +89,23 @@ cd "$REPO_ROOT" case "$MODE" in stage) + if [[ "$AUTO_STAGE" -eq 1 ]]; then + auto_src_dir="$HOME/.local/state/ytree/prompt-sources/$RUN_ID" + if [[ -d "$auto_src_dir" ]]; then + DEVELOPER_SRC="$(ls -1t "$auto_src_dir"/developer*.txt 2>/dev/null | head -n1 || true)" + AUDITOR_SRC="$(ls -1t "$auto_src_dir"/auditor*.txt 2>/dev/null | head -n1 || true)" + fi + if [[ -z "$DEVELOPER_SRC" || -z "$AUDITOR_SRC" ]]; then + echo "ERROR: auto stage could not find prompt sources for run_id=$RUN_ID" >&2 + echo " looked in: $auto_src_dir" >&2 + echo " expected files like: developer*.txt and auditor*.txt" >&2 + exit 1 + fi + echo "AUTO SOURCE: developer=$DEVELOPER_SRC" + echo "AUTO SOURCE: auditor=$AUDITOR_SRC" + fi if [[ -z "$DEVELOPER_SRC" || -z "$AUDITOR_SRC" ]]; then - echo "ERROR: stage requires --developer and --auditor" >&2 + echo "ERROR: stage requires either --auto or both --developer and --auditor" >&2 exit 2 fi if [[ ! -f "$DEVELOPER_SRC" ]]; then diff --git a/scripts/relay-run.sh b/scripts/relay-run.sh index acb0de03..86cfb93d 100644 --- a/scripts/relay-run.sh +++ b/scripts/relay-run.sh @@ -62,6 +62,6 @@ if [[ -n "$run_id" ]]; then echo "PROMPT ARTIFACTS READY: $run_id" else echo "PROMPT ARTIFACTS PENDING: $run_id" - echo "NEXT: scripts/relay-prompts.sh stage --run-id $run_id --developer --auditor " + echo "NEXT: scripts/relay-prompts.sh stage --run-id $run_id --auto;scripts/relay-prompts.sh verify --run-id $run_id" fi fi diff --git a/scripts/setup_relay_runtime.sh b/scripts/setup_relay_runtime.sh index c8365632..1865ffe1 100644 --- a/scripts/setup_relay_runtime.sh +++ b/scripts/setup_relay_runtime.sh @@ -150,5 +150,5 @@ else fi echo "[setup-relay] 2) IDE: paste a task-customized relay prompt template" echo "[setup-relay] 3) run: scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2" -echo "[setup-relay] 4) if pending prompts: scripts/relay-prompts.sh stage --run-id --developer --auditor ;scripts/relay-prompts.sh verify --run-id " +echo "[setup-relay] 4) if pending prompts: scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id " echo "[setup-relay] 5) optional monitor: scripts/relay-monitor.sh --view quiet" From 62adca451d67da8c2807dd28471922afa92f0fd3 Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 01:09:21 +0100 Subject: [PATCH 2/9] fix(relay): harden autonomous flow and monitor notifications --- scripts/assets/sounds/all.mp3 | Bin 0 -> 16280 bytes scripts/assets/sounds/gen.mp3 | Bin 0 -> 24090 bytes scripts/assets/sounds/hey.mp3 | Bin 0 -> 5071 bytes scripts/relay-monitor.sh | 28 +++++++++++++++++++++++++--- 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 scripts/assets/sounds/all.mp3 create mode 100644 scripts/assets/sounds/gen.mp3 create mode 100644 scripts/assets/sounds/hey.mp3 diff --git a/scripts/assets/sounds/all.mp3 b/scripts/assets/sounds/all.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..935d7bcace5df6fe7a488538141a7d25b8cf8ba0 GIT binary patch literal 16280 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 W8tBA;<`_~rL@H$% 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 literal 0 HcmV?d00001 diff --git a/scripts/relay-monitor.sh b/scripts/relay-monitor.sh index ca55fa9e..1fcd83d9 100644 --- a/scripts/relay-monitor.sh +++ b/scripts/relay-monitor.sh @@ -21,9 +21,10 @@ LIMIT=20 FOLLOW=1 AUTO_SELECT_RUN=0 SOUND=0 -SOUND_INPUT="${RELAY_MONITOR_SOUND_INPUT:-}" -SOUND_FAIL="${RELAY_MONITOR_SOUND_FAIL:-}" -SOUND_SUCCESS="${RELAY_MONITOR_SOUND_SUCCESS:-}" +DEFAULT_SOUND_DIR="$REPO_ROOT/scripts/assets/sounds" +SOUND_INPUT="${RELAY_MONITOR_SOUND_INPUT:-$DEFAULT_SOUND_DIR/hey.mp3}" +SOUND_FAIL="${RELAY_MONITOR_SOUND_FAIL:-$DEFAULT_SOUND_DIR/gen.mp3}" +SOUND_SUCCESS="${RELAY_MONITOR_SOUND_SUCCESS:-$DEFAULT_SOUND_DIR/all.mp3}" LAST_NOTIFIED_SEQ="" LAST_NOTIFIED_ACTION="" @@ -119,6 +120,27 @@ fi cd "$REPO_ROOT" +resolve_sound_path() { + local sound_file="$1" + if [[ -z "$sound_file" ]]; then + printf '%s' "" + return 0 + fi + if [[ -f "$sound_file" ]]; then + printf '%s' "$sound_file" + return 0 + fi + if [[ "$sound_file" != */* && -f "$DEFAULT_SOUND_DIR/$sound_file" ]]; then + printf '%s' "$DEFAULT_SOUND_DIR/$sound_file" + return 0 + fi + printf '%s' "$sound_file" +} + +SOUND_INPUT="$(resolve_sound_path "$SOUND_INPUT")" +SOUND_FAIL="$(resolve_sound_path "$SOUND_FAIL")" +SOUND_SUCCESS="$(resolve_sound_path "$SOUND_SUCCESS")" + if [[ -z "$RUN_ID" ]]; then AUTO_SELECT_RUN=1 fi From dc973950351768b3c783db92ed9193bb66d75cd3 Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 01:37:58 +0100 Subject: [PATCH 3/9] docs(relay): tighten maintainer update formatting contract --- docs/ai/PROMPT_TEMPLATE.md | 13 ++++++++++++- docs/ai/RELAY_PROMPT_TEMPLATE.md | 23 +++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/ai/PROMPT_TEMPLATE.md b/docs/ai/PROMPT_TEMPLATE.md index b36bcea6..74c993d5 100644 --- a/docs/ai/PROMPT_TEMPLATE.md +++ b/docs/ai/PROMPT_TEMPLATE.md @@ -89,9 +89,20 @@ Operator UX contract (mandatory): - First line of every update MUST be exactly one of: - `ACTION NEEDED (maintainer): none` - `ACTION NEEDED (maintainer): reply ""` -- If `ACTION NEEDED` is `none`, keep the rest of the update to at most five concise lines. +- If `ACTION NEEDED` is `none`, keep the rest of the update to at most six concise lines. - If `ACTION NEEDED` is not `none`, print that line before any other content. +- Required section order (no extra sections): + 1) `ACTION NEEDED` + 2) `COMPLETED` + 3) `RUN` (only when relay runtime is involved) + 4) `NEXT` + 5) `REPRO` (only when maintainer-run repro is needed) + 6) `LATEST EVENT` (only when relay runtime is involved) +- Repro instructions must be numbered with one step per line (no single-line paragraph lists). +- Do not emit `Model:` / `Reasoning level:` banners in routine updates. - Do not ask the maintainer to extract machine/runtime internals from logs; provide exact values directly. - If relay prompt artifacts are needed, do not ask maintainer for source-path discovery; provide one exact command: `scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id ` +- If relay run start/resume is reported in the current update, include one exact launch command with concrete values: + `scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2` - On completion, proactively emit final delivery package (summary, verification evidence, commit-ready status, and exact approval text when needed) without waiting for maintainer prodding. diff --git a/docs/ai/RELAY_PROMPT_TEMPLATE.md b/docs/ai/RELAY_PROMPT_TEMPLATE.md index 3007310a..ca53837b 100644 --- a/docs/ai/RELAY_PROMPT_TEMPLATE.md +++ b/docs/ai/RELAY_PROMPT_TEMPLATE.md @@ -118,21 +118,28 @@ Response format to maintainer: - If no maintainer input is required, include: - `ACTION NEEDED (maintainer): none` - Use delta-only updates: include only net-new state, next action, and new/changed handles unless maintainer asks for a full recap. -- Include `Latest relay event` in each update with direction + unit + handle. +- Include `LATEST EVENT` in each update with direction + unit + handle. - Do not repeat full historical handle inventories unless the maintainer explicitly requests a full recap. -- Include handoff block for relay role-to-role traceability only (maintainer does not need to copy/paste this): - Model: - Reasoning level: - Handoff line: : Execute $WORK_KIND $TASK from handle exactly as written. +- Do not emit `Model:` / `Reasoning level:` banners in routine updates. +- Handoff is one optional short line only, and only when changed: + `HANDOFF: : Execute $WORK_KIND $TASK from handle exactly as written.` Non-negotiable operator UX contract: - First line of every update MUST be exactly one of: - `ACTION NEEDED (maintainer): none` - `ACTION NEEDED (maintainer): reply ""` - If `ACTION NEEDED` is not `none`, emit that line before any other content. -- If `ACTION NEEDED` is `none`, keep update body to at most five concise lines. -- On run start/resume, provide exactly one copy-paste command line for runtime launch: - - `scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2` +- If `ACTION NEEDED` is `none`, keep update body to at most six concise lines. +- Required section order (no extra sections): + 1) `ACTION NEEDED` + 2) `COMPLETED` + 3) `RUN` + 4) `NEXT` + 5) `REPRO` (only when maintainer-run repro is needed) + 6) `LATEST EVENT` +- Repro instructions must be numbered with one step per line (no single-line paragraph lists). +- On run start/resume in the current update, provide exactly one copy-paste runtime command line with concrete values (not placeholders): + - `scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2` - If prompt artifacts are required, provide exactly one copy-paste command line for staging+verify: - `scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id ` - Do not require maintainer to query runtime internals (run_id lookup, idempotency lookup, history parsing); architect must provide exact values. From 7b76d1dcfa592b3526b336c8dc1f2ca19e1b657c Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 01:46:17 +0100 Subject: [PATCH 4/9] fix(relay): make monitor executable and warn when no audio player --- scripts/relay-monitor.sh | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/scripts/relay-monitor.sh b/scripts/relay-monitor.sh index 1fcd83d9..49f91f60 100644 --- a/scripts/relay-monitor.sh +++ b/scripts/relay-monitor.sh @@ -27,6 +27,7 @@ SOUND_FAIL="${RELAY_MONITOR_SOUND_FAIL:-$DEFAULT_SOUND_DIR/gen.mp3}" SOUND_SUCCESS="${RELAY_MONITOR_SOUND_SUCCESS:-$DEFAULT_SOUND_DIR/all.mp3}" LAST_NOTIFIED_SEQ="" LAST_NOTIFIED_ACTION="" +SOUND_PLAYER="" usage() { cat <<'USAGE' @@ -141,6 +142,20 @@ SOUND_INPUT="$(resolve_sound_path "$SOUND_INPUT")" SOUND_FAIL="$(resolve_sound_path "$SOUND_FAIL")" SOUND_SUCCESS="$(resolve_sound_path "$SOUND_SUCCESS")" +if [[ "$SOUND" -eq 1 ]]; then + if command -v ffplay >/dev/null 2>&1; then + SOUND_PLAYER="ffplay" + elif command -v mpv >/dev/null 2>&1; then + SOUND_PLAYER="mpv" + elif command -v mpg123 >/dev/null 2>&1; then + SOUND_PLAYER="mpg123" + elif command -v play >/dev/null 2>&1; then + SOUND_PLAYER="play" + else + echo "WARN: no audio player found for --sound (ffplay/mpv/mpg123/play). Falling back to terminal bell." >&2 + fi +fi + if [[ -z "$RUN_ID" ]]; then AUTO_SELECT_RUN=1 fi @@ -285,17 +300,21 @@ PY play_sound_file() { local sound_file="$1" + if [[ -z "$SOUND_PLAYER" ]]; then + printf '\a' + return 0 + fi if [[ -z "$sound_file" || ! -f "$sound_file" ]]; then printf '\a' return 0 fi - if command -v ffplay >/dev/null 2>&1; then + if [[ "$SOUND_PLAYER" == "ffplay" ]]; then (ffplay -nodisp -autoexit -loglevel quiet "$sound_file" >/dev/null 2>&1 &) - elif command -v mpv >/dev/null 2>&1; then + elif [[ "$SOUND_PLAYER" == "mpv" ]]; then (mpv --really-quiet --no-video "$sound_file" >/dev/null 2>&1 &) - elif command -v mpg123 >/dev/null 2>&1; then + elif [[ "$SOUND_PLAYER" == "mpg123" ]]; then (mpg123 -q "$sound_file" >/dev/null 2>&1 &) - elif command -v play >/dev/null 2>&1; then + elif [[ "$SOUND_PLAYER" == "play" ]]; then (play -q "$sound_file" >/dev/null 2>&1 &) else printf '\a' From 98d592ce080722910ab6ab03f77f33d847ae8fdc Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 11:00:08 +0100 Subject: [PATCH 5/9] docs(relay): remove redundant checks approval prompts --- docs/ai/PROMPT_TEMPLATE.md | 7 +++++-- docs/ai/RELAY_PROMPT_TEMPLATE.md | 11 +++++++---- docs/ai/RELAY_QUICKSTART.md | 1 + docs/ai/RELAY_RUNBOOK.md | 1 + 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/ai/PROMPT_TEMPLATE.md b/docs/ai/PROMPT_TEMPLATE.md index 74c993d5..e9e4a8d2 100644 --- a/docs/ai/PROMPT_TEMPLATE.md +++ b/docs/ai/PROMPT_TEMPLATE.md @@ -50,10 +50,10 @@ Process requirements: - keep scope tight and avoid unrelated edits 5) Validation: - - do not run checks or QA until I explicitly agree + - run required checks/QA autonomously; do not stop for checks approval - before first push, run a quick local gate (build + targeted smoke/tests) - run targeted tests as needed - - if I approve full QA for a unit state, run `make qa-all` at most once for that exact accepted state; rerun only after code changes + - run `make qa-all` at most once per accepted code state; rerun only after code changes - after task completion, run full gate: source .venv/bin/activate make qa-all @@ -84,6 +84,9 @@ Important guardrails: - Status updates must be facts-first: report what was completed (with evidence handle) before stating next action. - If uncertain, ask before choosing behavior-changing options. - If anything is ambiguous, ask me to clarify instead of guessing. +- Maintainer interruption is reserved for: + - `true_blocker_decision` + - `commit_message_approval` Operator UX contract (mandatory): - First line of every update MUST be exactly one of: diff --git a/docs/ai/RELAY_PROMPT_TEMPLATE.md b/docs/ai/RELAY_PROMPT_TEMPLATE.md index ca53837b..26a44b1b 100644 --- a/docs/ai/RELAY_PROMPT_TEMPLATE.md +++ b/docs/ai/RELAY_PROMPT_TEMPLATE.md @@ -74,10 +74,13 @@ Prompt/report artifact rules: Stall/escalation policy: - If a unit exceeds timeout or misses heartbeat, watchdog MUST emit a stall event. - Watchdog then retries/reassigns within retry policy; if retries are exhausted, mark terminal failure and escalate. -- Do not run checks or QA until maintainer explicitly agrees. -- When maintainer approves checks/QA for a unit state, avoid duplicate full-gate churn: - - `make qa-all` runs at most once per accepted unit state. +- Run required checks/QA autonomously; do not ask maintainer approval for checks. +- Avoid duplicate full-gate churn: + - `make qa-all` runs at most once per accepted code state. - Re-run full gate only if code changed after the last full-gate evidence. +- Maintainer interruption is reserved only for: + - `true_blocker_decision` + - `commit_message_approval` Developer prompt requirements (for each unit): - Strict scope and explicit non-goals @@ -114,7 +117,7 @@ Response format to maintainer: - Include handles only when they changed or are newly created. - For any required maintainer decision/approval, include one standalone explicit line: - `ACTION NEEDED (maintainer): reply ""` - - Example: `ACTION NEEDED (maintainer): reply "approve checks for BUG-11.2"` + - Example: `ACTION NEEDED (maintainer): reply "approve commit message: fix(ui): ..."` - If no maintainer input is required, include: - `ACTION NEEDED (maintainer): none` - Use delta-only updates: include only net-new state, next action, and new/changed handles unless maintainer asks for a full recap. diff --git a/docs/ai/RELAY_QUICKSTART.md b/docs/ai/RELAY_QUICKSTART.md index 4e6c3d09..c25028bb 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -69,6 +69,7 @@ Every update should include exactly one explicit line: - `ACTION NEEDED (maintainer): none` - or `ACTION NEEDED (maintainer): reply ""` +- checks/QA should run autonomously; maintainer replies should normally be only blocker clarification or commit-message approval. (“maintainer” means you.) diff --git a/docs/ai/RELAY_RUNBOOK.md b/docs/ai/RELAY_RUNBOOK.md index 502ac334..a26c642e 100644 --- a/docs/ai/RELAY_RUNBOOK.md +++ b/docs/ai/RELAY_RUNBOOK.md @@ -62,6 +62,7 @@ If no input is required: - `ACTION NEEDED (maintainer): none` In relay messages, **maintainer** means you (operator/user). +Checks/QA run autonomously; maintainer interruption is for blocker clarification or commit-message approval. ## Stop workers when done From 5d9a13aa389c05e04bd695c27e2b43635a44f73c Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 11:07:21 +0100 Subject: [PATCH 6/9] docs(relay): document sound notification setup --- docs/ai/RELAY_QUICKSTART.md | 10 ++++++++++ docs/ai/RELAY_RUNBOOK.md | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/docs/ai/RELAY_QUICKSTART.md b/docs/ai/RELAY_QUICKSTART.md index c25028bb..dc297c1f 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -49,6 +49,14 @@ cd ~/ytree scripts/relay-monitor.sh --run --view quiet ``` +Sound notifications (optional): + +```bash +sudo apt-get update +sudo apt-get install -y ffmpeg +scripts/relay-monitor.sh --run --view quiet --sound +``` + Other views: ```bash @@ -60,6 +68,8 @@ scripts/relay-monitor.sh --run --view verbose - `normal` = key transitions - `verbose` = full stream (includes heartbeats) - on terminal completion/failure, monitor prints report handles and an exact IDE fallback line if IDE goes silent +- `--sound` plays notifications for maintainer input needed, workflow failure, and workflow completion +- if no supported player is available, monitor warns and falls back to terminal bell Stop monitor with `Ctrl+C`. diff --git a/docs/ai/RELAY_RUNBOOK.md b/docs/ai/RELAY_RUNBOOK.md index a26c642e..de3e414f 100644 --- a/docs/ai/RELAY_RUNBOOK.md +++ b/docs/ai/RELAY_RUNBOOK.md @@ -42,11 +42,21 @@ cd ~/ytree scripts/relay-monitor.sh --run --view quiet ``` +Sound notifications (optional): + +```bash +sudo apt-get update +sudo apt-get install -y ffmpeg +scripts/relay-monitor.sh --run --view quiet --sound +``` + Detail levels: - `quiet`: status + `ACTION NEEDED (maintainer)` only - `normal`: key transitions (heartbeat noise filtered) - `verbose`: full event stream including heartbeats - on terminal completion/failure, monitor includes report handles plus an exact IDE fallback line if IDE goes silent +- `--sound` plays notifications for maintainer input needed, workflow failure, and workflow completion +- if no supported player is available, monitor warns and falls back to terminal bell Convenience: - omit `--run` to monitor the latest run in durable relay state. From cbd6c5e238b11e76ddecd6911048518db37415ec Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 11:25:43 +0100 Subject: [PATCH 7/9] docs(relay): keep quickstart minimal for sound usage --- docs/ai/RELAY_QUICKSTART.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ai/RELAY_QUICKSTART.md b/docs/ai/RELAY_QUICKSTART.md index dc297c1f..aab9d537 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -52,11 +52,11 @@ scripts/relay-monitor.sh --run --view quiet Sound notifications (optional): ```bash -sudo apt-get update -sudo apt-get install -y ffmpeg scripts/relay-monitor.sh --run --view quiet --sound ``` +If sound does not play on this machine yet, do one-time audio setup from `docs/ai/RELAY_RUNBOOK.md`. + Other views: ```bash @@ -69,7 +69,7 @@ scripts/relay-monitor.sh --run --view verbose - `verbose` = full stream (includes heartbeats) - on terminal completion/failure, monitor prints report handles and an exact IDE fallback line if IDE goes silent - `--sound` plays notifications for maintainer input needed, workflow failure, and workflow completion -- if no supported player is available, monitor warns and falls back to terminal bell +- if no supported player is available, monitor falls back to terminal bell Stop monitor with `Ctrl+C`. From 6cbd4a2e47b26fdcd00174ed39056904d383085a Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 11:30:03 +0100 Subject: [PATCH 8/9] docs(relay): streamline quickstart and copy-paste setup output --- docs/ai/RELAY_QUICKSTART.md | 74 +++++----------------------------- scripts/setup_relay_runtime.sh | 9 +++-- 2 files changed, 15 insertions(+), 68 deletions(-) diff --git a/docs/ai/RELAY_QUICKSTART.md b/docs/ai/RELAY_QUICKSTART.md index aab9d537..63335bf3 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -1,102 +1,46 @@ # Relay Quickstart (Minimal) -## [BASH] One-time (or after relay config/unit changes) +## 1) [BASH] One-time (or after relay config/unit changes) ```bash cd ~/ytree scripts/setup_relay_runtime.sh ``` -## [BASH] Each working session +## 2) [BASH] Each session ```bash cd ~/ytree scripts/relay-workers.sh start ``` -## [IDE] Prompt source (AI chat/IDE) +## 3) [IDE] Paste the task prompt - Non-trivial work: `docs/ai/RELAY_PROMPT_TEMPLATE.md` - Single quick unit: `docs/ai/PROMPT_TEMPLATE.md` -In IDE, open a fresh AI thread and paste the task-customized template. -Do not run prompt text in bash. - -## [BASH] Start a durable run +## 4) [BASH] Run the exact start/resume command the IDE gives ```bash -cd ~/ytree scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2 ``` -Expected output: - -- `RUN STARTED: ` (or `RUN RESUMED: `) -- `PROMPT ARTIFACTS READY: ` **or** `PROMPT ARTIFACTS PENDING: ` - -If prompt artifacts are pending, stage them in bash: - -```bash -cd ~/ytree -scripts/relay-prompts.sh stage --run-id --auto -scripts/relay-prompts.sh verify --run-id -``` - -## [BASH] Optional monitoring (single terminal) +## 5) [BASH] If prompts are pending ```bash -cd ~/ytree -scripts/relay-monitor.sh --run --view quiet +scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id ``` -Sound notifications (optional): +## 6) [BASH] Optional monitor ```bash scripts/relay-monitor.sh --run --view quiet --sound ``` -If sound does not play on this machine yet, do one-time audio setup from `docs/ai/RELAY_RUNBOOK.md`. - -Other views: +## 7) [BASH] Done ```bash -scripts/relay-monitor.sh --run --view normal -scripts/relay-monitor.sh --run --view verbose -``` - -- `quiet` = status + `ACTION NEEDED` only -- `normal` = key transitions -- `verbose` = full stream (includes heartbeats) -- on terminal completion/failure, monitor prints report handles and an exact IDE fallback line if IDE goes silent -- `--sound` plays notifications for maintainer input needed, workflow failure, and workflow completion -- if no supported player is available, monitor falls back to terminal bell - -Stop monitor with `Ctrl+C`. - -## [IDE] What to look for in AI updates - -Every update should include exactly one explicit line: - -- `ACTION NEEDED (maintainer): none` -- or `ACTION NEEDED (maintainer): reply ""` -- checks/QA should run autonomously; maintainer replies should normally be only blocker clarification or commit-message approval. - -(“maintainer” means you.) - -## [BASH] Finish / shutdown - -```bash -cd ~/ytree scripts/relay-workers.sh stop ``` -## Numbered flow - -1. [BASH] Run setup once: `scripts/setup_relay_runtime.sh` -2. [BASH] Start workers for the session: `scripts/relay-workers.sh start` -3. [IDE] Paste task prompt in IDE (not bash) -4. [BASH] Run the exact `scripts/relay-run.sh ...` line -5. [BASH] If prompt artifacts are pending, stage prompts: `scripts/relay-prompts.sh stage --run-id --auto` -6. [BASH] Verify prompts: `scripts/relay-prompts.sh verify --run-id ` -7. [BASH] Optional monitor: `scripts/relay-monitor.sh --run --view quiet` -8. [BASH] When done: `scripts/relay-workers.sh stop` +For setup/details/troubleshooting, use `docs/ai/RELAY_RUNBOOK.md`. diff --git a/scripts/setup_relay_runtime.sh b/scripts/setup_relay_runtime.sh index 1865ffe1..bfec08d9 100644 --- a/scripts/setup_relay_runtime.sh +++ b/scripts/setup_relay_runtime.sh @@ -149,6 +149,9 @@ else echo "[setup-relay] 1) workers: run scripts/relay-workers.sh start" fi echo "[setup-relay] 2) IDE: paste a task-customized relay prompt template" -echo "[setup-relay] 3) run: scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2" -echo "[setup-relay] 4) if pending prompts: scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id " -echo "[setup-relay] 5) optional monitor: scripts/relay-monitor.sh --view quiet" +echo "[setup-relay] 3) copy/paste when IDE gives run_id:" +echo " scripts/relay-run.sh --run-id --idempotency-key --activity-timeout 900 --retry-limit 2" +echo "[setup-relay] 4) if pending prompts, copy/paste:" +echo " scripts/relay-prompts.sh stage --run-id --auto;scripts/relay-prompts.sh verify --run-id " +echo "[setup-relay] 5) optional monitor:" +echo " scripts/relay-monitor.sh --run --view quiet --sound" From 5f99ba89a3748a7acae348ed71956a94ce55150a Mon Sep 17 00:00:00 2001 From: robkam Date: Tue, 12 May 2026 12:17:10 +0100 Subject: [PATCH 9/9] docs(readme): add top-level project structure overview --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 2652b1fa..0c1c8bf0 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,25 @@ The project documentation is split into several focused files. | **[ROADMAP.md](docs/ROADMAP.md)** | **Future Plans**: Pending milestones and prioritized delivery backlog. | | **[CHANGES.md](docs/CHANGES.md)** | **Changelog**: Detailed history of v3.0 feature delivery, architecture work, and updates. | +## Project Structure + +```text +ytree/ +├── src/ C source files +├── include/ C headers +├── tests/ pytest/pexpect test suite +├── scripts/ helper scripts (build, relay, tooling) +├── docs/ user, developer, and process documentation +├── infra/ service and infrastructure templates +├── etc/ manpage source and related assets +├── build/ build artifacts +├── obj/ object files +├── coverage/ coverage outputs +├── Makefile primary build/test targets +├── README.md project overview +└── AGENTS.md Codex/agent discovery stub +``` + --- ## Reporting Issues