#!/usr/bin/env bash # run-loop.sh — one round of a loop, cheap-model-first with Fable escalation. # Green loops only: prompts are read-only + write-to-own-state-file by design. set -euo pipefail LOOP="${1:?usage: run-loop.sh }" DIR="$HOME/.omp/loops/$LOOP" PROMPT="$DIR/loop.md" STATE="$DIR/state.md" LOG="$DIR/runs.log" [[ -f "$PROMPT" ]] || { echo "no such loop: $LOOP" >&2; exit 1; } [[ -f "$STATE" ]] || printf '# %s — state\n\n## queue\n\n## done\n\n## blocked\n' "$LOOP" > "$STATE" # Hard caps (loop #21 escalation policy + the article's stop rules): # - smol model runs the routine round, 10 min wall clock # - Fable (default model) only re-runs on an explicit ESCALATE marker, 20 min cap # - one escalation max per round; never more than one round per invocation SMOL_MAX=600 FABLE_MAX=1200 ts() { date -Is; } run() { # run — returns the model's stdout omp -p --no-session --max-time "$2" ${1:+--model "$1"} \ "$(cat "$PROMPT") Loop directory: $DIR State file (read it first, append to it, never rewrite history): $STATE Round timestamp: $(ts)" } echo "[$(ts)] round start (smol)" >> "$LOG" OUT="$(run "kimi-for-coding" "$SMOL_MAX" 2>>"$LOG" || true)" printf '%s\n' "$OUT" >> "$LOG" if grep -q '^ESCALATE' <<<"$OUT"; then echo "[$(ts)] escalating to default model" >> "$LOG" OUT="$(run "" "$FABLE_MAX" 2>>"$LOG" || true)" printf '%s\n' "$OUT" >> "$LOG" # Escalation ledger (loop #21): every Fable call must be visible and countable echo "[$(ts)] $LOOP escalated" >> "$HOME/.omp/loops/escalations.log" fi echo "[$(ts)] round end" >> "$LOG"