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 diff --git a/docs/ai/PROMPT_TEMPLATE.md b/docs/ai/PROMPT_TEMPLATE.md index b87555c4..30bf2a0f 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,12 +84,31 @@ 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: - `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 ` +- Do not ask maintainer to run `relay-prompts.sh` with `--developer` / `--auditor` source-path arguments. +- 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` +- If that run-start/resume command line is missing, immediately send a correction update with only the missing command line. +- Do not stop/pause relay workers for routine process gating; only stop on explicit maintainer stop/cancel or terminal runtime failure recovery. - 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..dd9bdd0e 100644 --- a/docs/ai/RELAY_PROMPT_TEMPLATE.md +++ b/docs/ai/RELAY_PROMPT_TEMPLATE.md @@ -49,12 +49,12 @@ Execution model (auto-relay runtime): 9) Architect updates $WORK_DOC 10) After starting the durable run, print exactly one explicit line: `RUN STARTED: ` +11) Do not stop/pause relay workers for routine gating. Keep workers running unless: + - maintainer explicitly requests stop/cancel, or + - terminal runtime failure requires controlled recovery. Prompt/report artifact rules: - Use run_id + unit_id + event seq in all status updates; include handles only when new/changed. -- Before each prompt message you generate, print exactly: - Model: - Reasoning level: - Use unit IDs derived from the base work item only: $TASK.1, $TASK.2, ... - Load startup instruction files once per session unless files changed or maintainer explicitly requests reload. - Stream relay visibility live: post a maintainer update immediately after each runtime event. @@ -66,18 +66,22 @@ Prompt/report artifact rules: - Heartbeat-only updates are liveness pings and MUST include elapsed runtime + current executing action. - 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 ` +- Before dispatching `developer_run`, you MUST stage both relay prompt artifacts for this run_id: + - `scripts/relay-prompts.sh stage --run-id --auto` - Then verify with `scripts/relay-prompts.sh verify --run-id ` +- Do not ask maintainer to run `relay-prompts.sh` with `--developer` / `--auditor` source paths. - Do not claim dispatch/start for `developer_run` without runtime evidence (`unit_queued`, `lease_acquired`, or `worker_command_started`) and its `history_seq`. 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,27 +118,35 @@ 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. -- 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 a run-start/resume update is emitted without that exact command line, immediately emit a correction update containing the missing command line and no extra prose. - 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..a5c8c962 100644 --- a/docs/ai/RELAY_QUICKSTART.md +++ b/docs/ai/RELAY_QUICKSTART.md @@ -1,89 +1,46 @@ # Relay Quickstart (Minimal) -## 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 ``` -## Each working session +## 2) [BASH] Each session ```bash cd ~/ytree -scripts/relay-workers.sh start +scripts/relay-workers.sh stop;scripts/relay-workers.sh start ``` -## 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. - -## 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: - -```bash -cd ~/ytree -scripts/relay-prompts.sh stage --run-id --developer --auditor -scripts/relay-prompts.sh verify --run-id -``` - -## 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 ``` -Other views: +## 6) [BASH] Optional monitor ```bash -scripts/relay-monitor.sh --run --view normal -scripts/relay-monitor.sh --run --view verbose +scripts/relay-monitor.sh --run --view quiet --sound ``` -- `quiet` = status + `ACTION NEEDED` only -- `normal` = key transitions -- `verbose` = full stream (includes heartbeats) - -Stop monitor with `Ctrl+C`. - -## What to look for in AI updates - -Every update should include exactly one explicit line: - -- `ACTION NEEDED (maintainer): none` -- or `ACTION NEEDED (maintainer): reply ""` - -(“maintainer” means you.) - -## Finish / shutdown +## 7) [BASH] Done ```bash -cd ~/ytree 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` +For setup/details/troubleshooting, use `docs/ai/RELAY_RUNBOOK.md`. diff --git a/docs/ai/RELAY_RUNBOOK.md b/docs/ai/RELAY_RUNBOOK.md index c7760ae3..de3e414f 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 ``` @@ -42,10 +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. @@ -61,6 +72,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 diff --git a/docs/ai/WORKFLOW.md b/docs/ai/WORKFLOW.md index 2b353dcf..8131001e 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 ``` @@ -313,16 +313,14 @@ watch -n 5 'python3 scripts/relay_runtime.py dashboard --verbose --limit 20' 2. Architect starts exactly one durable run with stable `run_id` + idempotency key. 3. Unit lifecycle is fixed and durable: `architect_handoff -> developer_run -> auditor_run -> architect_validation`. 4. Architect emits exactly one runnable developer unit at a time (never multiple units in-flight for the same run unless explicitly designed). - * Before requesting maintainer approval to dispatch `developer_run`, architect MUST stage and verify relay prompt artifacts for the run id. + * Before dispatching `developer_run`, architect MUST stage and verify relay prompt artifacts for the run id using `scripts/relay-prompts.sh stage --run-id --auto` then `scripts/relay-prompts.sh verify --run-id `. 5. Every unit definition must include: * strict scope lock, * acceptance criteria, * verification commands, * blocker conditions, * bounded timeout + retry policy. -6. Architect status update to maintainer MUST include: - * `Reasoning level: ` - * `run_id`, `unit_id`, and latest event sequence. +6. Architect status update to maintainer MUST include `run_id`, `unit_id`, and latest event sequence. #### 3.1.4 Developer Pass (systemd Worker, Lease + Heartbeat, Single Unit) @@ -333,7 +331,7 @@ watch -n 5 'python3 scripts/relay_runtime.py dashboard --verbose --limit 20' * initial pass: full verification set listed for the unit, * correction/rework pass: rerun failing checks + directly impacted targeted tests, * avoid full `make qa-all` reruns unless risk materially changed or architect requests it, - * once maintainer has approved QA for a given accepted unit state, run `make qa-all` at most once for that state and rerun only after subsequent code changes. + * run `make qa-all` at most once for a given accepted code state and rerun only after subsequent code changes. 5. Worker MUST NOT mark unit complete while required checks are failing. 6. On success/failure/timeout, worker MUST emit explicit event log entries (no silent loops). 7. Developer status line to maintainer must be delta-only: net-new state + next action + changed handles only. @@ -361,6 +359,7 @@ watch -n 5 'python3 scripts/relay_runtime.py dashboard --verbose --limit 20' * terminal fail when retry budget is exhausted (no silent stop states), * if worker creation is policy-blocked, retry once with a reduced subagent-safe prompt profile (minimal technical payload only) and do not pause maintainer for that recoverable path. 3. Relay execution remains autonomous end-to-end; maintainer interruption is reserved strictly for `true_blocker_decision` and `commit_message_approval`. + * Workers must not be stopped/paused for routine process gating; stop/cancel is only for explicit maintainer stop requests or terminal failure recovery. * When maintainer input is required, architect MUST emit exactly one standalone line: `ACTION NEEDED (maintainer): reply ""`. * When no maintainer input is required, architect MUST emit: diff --git a/scripts/assets/sounds/all.mp3 b/scripts/assets/sounds/all.mp3 new file mode 100644 index 00000000..935d7bca Binary files /dev/null and b/scripts/assets/sounds/all.mp3 differ diff --git a/scripts/assets/sounds/gen.mp3 b/scripts/assets/sounds/gen.mp3 new file mode 100644 index 00000000..19306881 Binary files /dev/null and b/scripts/assets/sounds/gen.mp3 differ diff --git a/scripts/assets/sounds/hey.mp3 b/scripts/assets/sounds/hey.mp3 new file mode 100644 index 00000000..886eec7e Binary files /dev/null and b/scripts/assets/sounds/hey.mp3 differ diff --git a/scripts/relay-monitor.sh b/scripts/relay-monitor.sh index ee4a8a2b..49f91f60 100644 --- a/scripts/relay-monitor.sh +++ b/scripts/relay-monitor.sh @@ -20,11 +20,25 @@ INTERVAL=3 LIMIT=20 FOLLOW=1 AUTO_SELECT_RUN=0 +SOUND=0 +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="" +SOUND_PLAYER="" 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 +69,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,8 +115,47 @@ 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" +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 [[ "$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 @@ -165,8 +237,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 +270,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 +298,80 @@ PY )" "$RUN_ID" "$VIEW" "$LIMIT" } +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 [[ "$SOUND_PLAYER" == "ffplay" ]]; then + (ffplay -nodisp -autoexit -loglevel quiet "$sound_file" >/dev/null 2>&1 &) + elif [[ "$SOUND_PLAYER" == "mpv" ]]; then + (mpv --really-quiet --no-video "$sound_file" >/dev/null 2>&1 &) + elif [[ "$SOUND_PLAYER" == "mpg123" ]]; then + (mpg123 -q "$sound_file" >/dev/null 2>&1 &) + elif [[ "$SOUND_PLAYER" == "play" ]]; 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..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 --developer --auditor ;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"