diff --git a/gardener/gardener-run.sh b/gardener/gardener-run.sh index 31aa8c0..6c964dc 100755 --- a/gardener/gardener-run.sh +++ b/gardener/gardener-run.sh @@ -92,8 +92,11 @@ Supported actions: The commit-and-pr step converts JSONL to JSON array. The orchestrator executes actions after the PR merges. Do NOT call mutation APIs directly during the run." -build_sdk_prompt_footer "$GARDENER_API_EXTRA" -PROMPT_FOOTER="${PROMPT_FOOTER}## Completion protocol (REQUIRED) +# Reuse shared footer (API reference + environment), replace phase protocol +# shellcheck disable=SC2034 # consumed by build_prompt_footer +PHASE_FILE="" # not used in SDK mode +build_prompt_footer "$GARDENER_API_EXTRA" +PROMPT_FOOTER="${PROMPT_FOOTER%%## Phase protocol*}## Completion protocol (REQUIRED) When the commit-and-pr step creates a PR, write the PR number and stop: echo \"\$PR_NUMBER\" > '${GARDENER_PR_FILE}' Then STOP. Do NOT write PHASE: signals — the orchestrator handles CI, review, and merge. diff --git a/lib/formula-session.sh b/lib/formula-session.sh index 7c52035..670da95 100644 --- a/lib/formula-session.sh +++ b/lib/formula-session.sh @@ -291,33 +291,6 @@ build_graph_section() { fi } -# ── SDK helpers ─────────────────────────────────────────────────────────── - -# build_sdk_prompt_footer [EXTRA_API_LINES] -# Like build_prompt_footer but omits the phase protocol section (SDK mode). -# Sets PROMPT_FOOTER. -build_sdk_prompt_footer() { - # shellcheck disable=SC2034 # consumed by build_prompt_footer - PHASE_FILE="" # not used in SDK mode - build_prompt_footer "${1:-}" - PROMPT_FOOTER="${PROMPT_FOOTER%%## Phase protocol*}" -} - -# formula_worktree_setup WORKTREE -# Creates an isolated worktree for synchronous formula execution. -# Fetches primary branch, cleans stale worktree, creates new one, and -# sets an EXIT trap for cleanup. -# Requires globals: PROJECT_REPO_ROOT, PRIMARY_BRANCH. -formula_worktree_setup() { - local worktree="$1" - cd "$PROJECT_REPO_ROOT" || return - git fetch origin "$PRIMARY_BRANCH" 2>/dev/null || true - worktree_cleanup "$worktree" - git worktree add "$worktree" "origin/${PRIMARY_BRANCH}" --detach 2>/dev/null - # shellcheck disable=SC2064 # expand worktree now, not at trap time - trap "worktree_cleanup '$worktree'" EXIT -} - # ── Prompt + monitor helpers ────────────────────────────────────────────── # build_prompt_footer [EXTRA_API_LINES] diff --git a/planner/planner-run.sh b/planner/planner-run.sh index 313f6ef..878fdeb 100755 --- a/planner/planner-run.sh +++ b/planner/planner-run.sh @@ -1,16 +1,10 @@ #!/usr/bin/env bash # ============================================================================= -# planner-run.sh — Cron wrapper: planner execution via SDK + formula +# planner-run.sh — Cron wrapper: direct planner execution via Claude + formula # -# Synchronous bash loop using claude -p (one-shot invocation). -# No tmux sessions, no phase files — the bash script IS the state machine. -# -# Flow: -# 1. Guards: cron lock, memory check -# 2. Load formula (formulas/run-planner.toml) -# 3. Context: VISION.md, AGENTS.md, ops:RESOURCES.md, structural graph, -# planner memory, journal entries -# 4. agent_run(worktree, prompt) → Claude plans, may push knowledge updates +# Runs daily (or on-demand). Guards against concurrent runs and low memory. +# Creates a tmux session with Claude (opus) reading formulas/run-planner.toml. +# No action issues — the planner is a nervous system component, not work. # # Usage: # planner-run.sh [projects/disinto.toml] # project config (default: disinto) @@ -26,22 +20,24 @@ export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" source "$FACTORY_ROOT/lib/env.sh" # Use planner-bot's own Forgejo identity (#747) FORGE_TOKEN="${FORGE_PLANNER_TOKEN:-${FORGE_TOKEN}}" +# shellcheck source=../lib/agent-session.sh +source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh source "$FACTORY_ROOT/lib/formula-session.sh" # shellcheck source=../lib/worktree.sh source "$FACTORY_ROOT/lib/worktree.sh" # shellcheck source=../lib/guard.sh source "$FACTORY_ROOT/lib/guard.sh" -# shellcheck source=../lib/agent-sdk.sh -source "$FACTORY_ROOT/lib/agent-sdk.sh" LOG_FILE="$SCRIPT_DIR/planner.log" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -LOGFILE="$LOG_FILE" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -SID_FILE="/tmp/planner-session-${PROJECT_NAME}.sid" +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +SESSION_NAME="planner-${PROJECT_NAME}" +PHASE_FILE="/tmp/planner-session-${PROJECT_NAME}.phase" + +# shellcheck disable=SC2034 # read by monitor_phase_loop in lib/agent-session.sh +PHASE_POLL_INTERVAL=15 + SCRATCH_FILE="/tmp/planner-${PROJECT_NAME}-scratch.md" -WORKTREE="/tmp/${PROJECT_NAME}-planner-run" log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; } @@ -96,13 +92,14 @@ SCRATCH_CONTEXT=$(read_scratch_context "$SCRATCH_FILE") SCRATCH_INSTRUCTION=$(build_scratch_instruction "$SCRATCH_FILE") # ── Build prompt ───────────────────────────────────────────────────────── -build_sdk_prompt_footer " +build_prompt_footer " Relabel: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" -X PUT -H 'Content-Type: application/json' '${FORGE_API}/issues/{number}/labels' -d '{\"labels\":[LABEL_ID]}' Comment: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" -X POST -H 'Content-Type: application/json' '${FORGE_API}/issues/{number}/comments' -d '{\"body\":\"...\"}' Close: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" -X PATCH -H 'Content-Type: application/json' '${FORGE_API}/issues/{number}' -d '{\"state\":\"closed\"}' " -PROMPT="You are the strategic planner for ${FORGE_REPO}. Work through the formula below. +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +PROMPT="You are the strategic planner for ${FORGE_REPO}. Work through the formula below. You MUST write PHASE:done to '${PHASE_FILE}' when finished — the orchestrator will time you out if you return to the prompt without signalling. ## Project context ${CONTEXT_BLOCK}${MEMORY_BLOCK}${JOURNAL_BLOCK} @@ -116,14 +113,12 @@ ${SCRATCH_INSTRUCTION} ${PROMPT_FOOTER}" -# ── Create worktree ────────────────────────────────────────────────────── -formula_worktree_setup "$WORKTREE" - -# ── Run agent ───────────────────────────────────────────────────────────── +# ── Run session ────────────────────────────────────────────────────────── export CLAUDE_MODEL="opus" +run_formula_and_monitor "planner" -agent_run --worktree "$WORKTREE" "$PROMPT" -log "agent_run complete" - -rm -f "$SCRATCH_FILE" -log "--- Planner run done ---" +# ── Cleanup scratch file on normal exit ────────────────────────────────── +# FINAL_PHASE already set by run_formula_and_monitor +if [ "${FINAL_PHASE:-}" = "PHASE:done" ]; then + rm -f "$SCRATCH_FILE" +fi diff --git a/predictor/predictor-run.sh b/predictor/predictor-run.sh index fb9bf51..c7921c0 100755 --- a/predictor/predictor-run.sh +++ b/predictor/predictor-run.sh @@ -1,15 +1,10 @@ #!/usr/bin/env bash # ============================================================================= -# predictor-run.sh — Cron wrapper: predictor execution via SDK + formula +# predictor-run.sh — Cron wrapper: predictor execution via Claude + formula # -# Synchronous bash loop using claude -p (one-shot invocation). -# No tmux sessions, no phase files — the bash script IS the state machine. -# -# Flow: -# 1. Guards: cron lock, memory check -# 2. Load formula (formulas/run-predictor.toml) -# 3. Context: AGENTS.md, ops:RESOURCES.md, VISION.md, structural graph -# 4. agent_run(worktree, prompt) → Claude analyzes, writes to ops repo +# Runs daily (or on-demand). Guards against concurrent runs and low memory. +# Creates a tmux session with Claude (sonnet) reading formulas/run-predictor.toml. +# Files prediction/unreviewed issues for the planner to triage. # # Usage: # predictor-run.sh [projects/disinto.toml] # project config (default: disinto) @@ -27,22 +22,24 @@ export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" source "$FACTORY_ROOT/lib/env.sh" # Use predictor-bot's own Forgejo identity (#747) FORGE_TOKEN="${FORGE_PREDICTOR_TOKEN:-${FORGE_TOKEN}}" +# shellcheck source=../lib/agent-session.sh +source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh source "$FACTORY_ROOT/lib/formula-session.sh" # shellcheck source=../lib/worktree.sh source "$FACTORY_ROOT/lib/worktree.sh" # shellcheck source=../lib/guard.sh source "$FACTORY_ROOT/lib/guard.sh" -# shellcheck source=../lib/agent-sdk.sh -source "$FACTORY_ROOT/lib/agent-sdk.sh" LOG_FILE="$SCRIPT_DIR/predictor.log" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -LOGFILE="$LOG_FILE" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -SID_FILE="/tmp/predictor-session-${PROJECT_NAME}.sid" +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +SESSION_NAME="predictor-${PROJECT_NAME}" +PHASE_FILE="/tmp/predictor-session-${PROJECT_NAME}.phase" + +# shellcheck disable=SC2034 # read by monitor_phase_loop in lib/agent-session.sh +PHASE_POLL_INTERVAL=15 + SCRATCH_FILE="/tmp/predictor-${PROJECT_NAME}-scratch.md" -WORKTREE="/tmp/${PROJECT_NAME}-predictor-run" log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; } @@ -65,10 +62,10 @@ SCRATCH_CONTEXT=$(read_scratch_context "$SCRATCH_FILE") SCRATCH_INSTRUCTION=$(build_scratch_instruction "$SCRATCH_FILE") # ── Build prompt ───────────────────────────────────────────────────────── -build_sdk_prompt_footer -export CLAUDE_MODEL="sonnet" +build_prompt_footer -PROMPT="You are the prediction agent (goblin) for ${FORGE_REPO}. Work through the formula below. +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +PROMPT="You are the prediction agent (goblin) for ${FORGE_REPO}. Work through the formula below. You MUST write PHASE:done to '${PHASE_FILE}' when finished — the orchestrator will time you out if you return to the prompt without signalling. Your role: abstract adversary. Find the project's biggest weakness, challenge planner claims, and generate evidence. Explore when uncertain (file a prediction), @@ -91,12 +88,12 @@ ${FORMULA_CONTENT} ${SCRATCH_INSTRUCTION} ${PROMPT_FOOTER}" -# ── Create worktree ────────────────────────────────────────────────────── -formula_worktree_setup "$WORKTREE" +# ── Run session ────────────────────────────────────────────────────────── +export CLAUDE_MODEL="sonnet" +run_formula_and_monitor "predictor" -# ── Run agent ───────────────────────────────────────────────────────────── -agent_run --worktree "$WORKTREE" "$PROMPT" -log "agent_run complete" - -rm -f "$SCRATCH_FILE" -log "--- Predictor run done ---" +# ── Cleanup scratch file on normal exit ────────────────────────────────── +# FINAL_PHASE already set by run_formula_and_monitor +if [ "${FINAL_PHASE:-}" = "PHASE:done" ]; then + rm -f "$SCRATCH_FILE" +fi diff --git a/supervisor/supervisor-run.sh b/supervisor/supervisor-run.sh index 129666f..d32bd79 100755 --- a/supervisor/supervisor-run.sh +++ b/supervisor/supervisor-run.sh @@ -1,17 +1,14 @@ #!/usr/bin/env bash # ============================================================================= -# supervisor-run.sh — Cron wrapper: supervisor execution via SDK + formula +# supervisor-run.sh — Cron wrapper: supervisor execution via Claude + formula # -# Synchronous bash loop using claude -p (one-shot invocation). -# No tmux sessions, no phase files — the bash script IS the state machine. +# Runs every 20 minutes (or on-demand). Guards against concurrent runs and +# low memory. Collects metrics via preflight.sh, then creates a tmux session +# with Claude (sonnet) reading formulas/run-supervisor.toml. # -# Flow: -# 1. Guards: cron lock, memory check -# 2. Housekeeping: clean up stale crashed worktrees -# 3. Collect pre-flight metrics (supervisor/preflight.sh) -# 4. Load formula (formulas/run-supervisor.toml) -# 5. Context: AGENTS.md, preflight metrics, structural graph -# 6. agent_run(worktree, prompt) → Claude monitors, may clean up +# Replaces supervisor-poll.sh (bash orchestrator + claude -p one-shot) with +# formula-driven interactive Claude session matching the planner/predictor +# pattern. # # Usage: # supervisor-run.sh [projects/disinto.toml] # project config (default: disinto) @@ -29,22 +26,24 @@ export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" source "$FACTORY_ROOT/lib/env.sh" # Use supervisor-bot's own Forgejo identity (#747) FORGE_TOKEN="${FORGE_SUPERVISOR_TOKEN:-${FORGE_TOKEN}}" +# shellcheck source=../lib/agent-session.sh +source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh source "$FACTORY_ROOT/lib/formula-session.sh" # shellcheck source=../lib/worktree.sh source "$FACTORY_ROOT/lib/worktree.sh" # shellcheck source=../lib/guard.sh source "$FACTORY_ROOT/lib/guard.sh" -# shellcheck source=../lib/agent-sdk.sh -source "$FACTORY_ROOT/lib/agent-sdk.sh" LOG_FILE="$SCRIPT_DIR/supervisor.log" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -LOGFILE="$LOG_FILE" -# shellcheck disable=SC2034 # consumed by agent-sdk.sh -SID_FILE="/tmp/supervisor-session-${PROJECT_NAME}.sid" +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +SESSION_NAME="supervisor-${PROJECT_NAME}" +PHASE_FILE="/tmp/supervisor-session-${PROJECT_NAME}.phase" + +# shellcheck disable=SC2034 # read by monitor_phase_loop in lib/agent-session.sh +PHASE_POLL_INTERVAL=15 + SCRATCH_FILE="/tmp/supervisor-${PROJECT_NAME}-scratch.md" -WORKTREE="/tmp/${PROJECT_NAME}-supervisor-run" log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; } @@ -76,13 +75,10 @@ SCRATCH_CONTEXT=$(read_scratch_context "$SCRATCH_FILE") SCRATCH_INSTRUCTION=$(build_scratch_instruction "$SCRATCH_FILE") # ── Build prompt ───────────────────────────────────────────────────────── -build_sdk_prompt_footer -export CLAUDE_MODEL="sonnet" +build_prompt_footer -# ── Create worktree (before prompt assembly so trap is set early) ──────── -formula_worktree_setup "$WORKTREE" - -PROMPT="You are the supervisor agent for ${FORGE_REPO}. Work through the formula below. +# shellcheck disable=SC2034 # consumed by run_formula_and_monitor +PROMPT="You are the supervisor agent for ${FORGE_REPO}. Work through the formula below. You MUST write PHASE:done to '${PHASE_FILE}' when finished — the orchestrator will time you out if you return to the prompt without signalling. You have full shell access and --dangerously-skip-permissions. Fix what you can. File vault items for what you cannot. Do NOT ask permission — act first, report after. @@ -101,9 +97,12 @@ ${FORMULA_CONTENT} ${SCRATCH_INSTRUCTION} ${PROMPT_FOOTER}" -# ── Run agent ───────────────────────────────────────────────────────────── -agent_run --worktree "$WORKTREE" "$PROMPT" -log "agent_run complete" +# ── Run session ────────────────────────────────────────────────────────── +export CLAUDE_MODEL="sonnet" +run_formula_and_monitor "supervisor" 1200 -rm -f "$SCRATCH_FILE" -log "--- Supervisor run done ---" +# ── Cleanup scratch file on normal exit ────────────────────────────────── +# FINAL_PHASE already set by run_formula_and_monitor +if [ "${FINAL_PHASE:-}" = "PHASE:done" ]; then + rm -f "$SCRATCH_FILE" +fi