fix: feat: active-state files — per-cron guard with self-off semantics (#622)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e041b20823
commit
e535ed776f
10 changed files with 47 additions and 1 deletions
|
|
@ -100,6 +100,7 @@ echo "=== 2/2 Function resolution ==="
|
||||||
# lib/secret-scan.sh — sourced by file-action-issue.sh, phase-handler.sh (scan_for_secrets, redact_secrets)
|
# lib/secret-scan.sh — sourced by file-action-issue.sh, phase-handler.sh (scan_for_secrets, redact_secrets)
|
||||||
# lib/formula-session.sh — sourced by formula-driven agents (acquire_cron_lock, run_formula_and_monitor, etc.)
|
# lib/formula-session.sh — sourced by formula-driven agents (acquire_cron_lock, run_formula_and_monitor, etc.)
|
||||||
# lib/mirrors.sh — sourced by merge sites (mirror_push)
|
# lib/mirrors.sh — sourced by merge sites (mirror_push)
|
||||||
|
# lib/guard.sh — sourced by all cron entry points (check_active)
|
||||||
#
|
#
|
||||||
# Excluded — not sourced inline by agents:
|
# Excluded — not sourced inline by agents:
|
||||||
# lib/ci-debug.sh — standalone CLI tool, run directly (not sourced)
|
# lib/ci-debug.sh — standalone CLI tool, run directly (not sourced)
|
||||||
|
|
@ -110,7 +111,7 @@ echo "=== 2/2 Function resolution ==="
|
||||||
# If a new lib file is added and sourced by agents, add it to LIB_FUNS below
|
# If a new lib file is added and sourced by agents, add it to LIB_FUNS below
|
||||||
# and add a check_script call for it in the lib files section further down.
|
# and add a check_script call for it in the lib files section further down.
|
||||||
LIB_FUNS=$(
|
LIB_FUNS=$(
|
||||||
for f in lib/agent-session.sh lib/env.sh lib/ci-helpers.sh lib/load-project.sh lib/secret-scan.sh lib/file-action-issue.sh lib/formula-session.sh lib/mirrors.sh; do
|
for f in lib/agent-session.sh lib/env.sh lib/ci-helpers.sh lib/load-project.sh lib/secret-scan.sh lib/file-action-issue.sh lib/formula-session.sh lib/mirrors.sh lib/guard.sh; do
|
||||||
if [ -f "$f" ]; then get_fns "$f"; fi
|
if [ -f "$f" ]; then get_fns "$f"; fi
|
||||||
done | sort -u
|
done | sort -u
|
||||||
)
|
)
|
||||||
|
|
@ -181,6 +182,7 @@ check_script lib/file-action-issue.sh lib/secret-scan.sh
|
||||||
check_script lib/formula-session.sh lib/agent-session.sh
|
check_script lib/formula-session.sh lib/agent-session.sh
|
||||||
check_script lib/load-project.sh
|
check_script lib/load-project.sh
|
||||||
check_script lib/mirrors.sh
|
check_script lib/mirrors.sh
|
||||||
|
check_script lib/guard.sh
|
||||||
|
|
||||||
# Standalone lib scripts (not sourced by agents; run directly or as services).
|
# Standalone lib scripts (not sourced by agents; run directly or as services).
|
||||||
# Still checked for function resolution against LIB_FUNS + own definitions.
|
# Still checked for function resolution against LIB_FUNS + own definitions.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ set -euo pipefail
|
||||||
|
|
||||||
export PROJECT_TOML="${1:-}"
|
export PROJECT_TOML="${1:-}"
|
||||||
source "$(dirname "$0")/../lib/env.sh"
|
source "$(dirname "$0")/../lib/env.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$(dirname "$0")/../lib/guard.sh"
|
||||||
|
check_active action
|
||||||
|
|
||||||
LOGFILE="${FACTORY_ROOT}/action/action-poll-${PROJECT_NAME:-default}.log"
|
LOGFILE="${FACTORY_ROOT}/action/action-poll-${PROJECT_NAME:-default}.log"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ source "$(dirname "$0")/../lib/env.sh"
|
||||||
source "$(dirname "$0")/../lib/ci-helpers.sh"
|
source "$(dirname "$0")/../lib/ci-helpers.sh"
|
||||||
# shellcheck source=../lib/mirrors.sh
|
# shellcheck source=../lib/mirrors.sh
|
||||||
source "$(dirname "$0")/../lib/mirrors.sh"
|
source "$(dirname "$0")/../lib/mirrors.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$(dirname "$0")/../lib/guard.sh"
|
||||||
|
check_active dev
|
||||||
|
|
||||||
# Gitea labels API requires []int64 — look up the "underspecified" label ID once
|
# Gitea labels API requires []int64 — look up the "underspecified" label ID once
|
||||||
UNDERSPECIFIED_LABEL_ID=$(forge_api GET "/labels" 2>/dev/null \
|
UNDERSPECIFIED_LABEL_ID=$(forge_api GET "/labels" 2>/dev/null \
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ source "$FACTORY_ROOT/lib/formula-session.sh"
|
||||||
source "$FACTORY_ROOT/lib/ci-helpers.sh"
|
source "$FACTORY_ROOT/lib/ci-helpers.sh"
|
||||||
# shellcheck source=../lib/mirrors.sh
|
# shellcheck source=../lib/mirrors.sh
|
||||||
source "$FACTORY_ROOT/lib/mirrors.sh"
|
source "$FACTORY_ROOT/lib/mirrors.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$FACTORY_ROOT/lib/guard.sh"
|
||||||
|
|
||||||
LOG_FILE="$SCRIPT_DIR/gardener.log"
|
LOG_FILE="$SCRIPT_DIR/gardener.log"
|
||||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||||
|
|
@ -52,6 +54,7 @@ _GARDENER_CRASH_COUNT=0
|
||||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
# ── Guards ────────────────────────────────────────────────────────────────
|
# ── Guards ────────────────────────────────────────────────────────────────
|
||||||
|
check_active gardener
|
||||||
acquire_cron_lock "/tmp/gardener-run.lock"
|
acquire_cron_lock "/tmp/gardener-run.lock"
|
||||||
check_memory 2000
|
check_memory 2000
|
||||||
|
|
||||||
|
|
|
||||||
21
lib/guard.sh
Normal file
21
lib/guard.sh
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# guard.sh — Active-state guard for cron entry points
|
||||||
|
#
|
||||||
|
# Each agent checks for a state file before running. If the file
|
||||||
|
# doesn't exist, the agent logs a skip and exits cleanly.
|
||||||
|
#
|
||||||
|
# State files live in $FACTORY_ROOT/state/:
|
||||||
|
# .dev-active, .reviewer-active, .planner-active, etc.
|
||||||
|
#
|
||||||
|
# Presence = permission to run. Absence = skip (factory off by default).
|
||||||
|
|
||||||
|
# check_active <agent_name>
|
||||||
|
# Exit 0 (skip) if the state file is absent.
|
||||||
|
check_active() {
|
||||||
|
local agent_name="$1"
|
||||||
|
local state_file="${FACTORY_ROOT}/state/.${agent_name}-active"
|
||||||
|
if [ ! -f "$state_file" ]; then
|
||||||
|
log "${agent_name} not active — skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,8 @@ source "$FACTORY_ROOT/lib/env.sh"
|
||||||
source "$FACTORY_ROOT/lib/agent-session.sh"
|
source "$FACTORY_ROOT/lib/agent-session.sh"
|
||||||
# shellcheck source=../lib/formula-session.sh
|
# shellcheck source=../lib/formula-session.sh
|
||||||
source "$FACTORY_ROOT/lib/formula-session.sh"
|
source "$FACTORY_ROOT/lib/formula-session.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$FACTORY_ROOT/lib/guard.sh"
|
||||||
|
|
||||||
LOG_FILE="$SCRIPT_DIR/planner.log"
|
LOG_FILE="$SCRIPT_DIR/planner.log"
|
||||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||||
|
|
@ -36,6 +38,7 @@ SCRATCH_FILE="/tmp/planner-${PROJECT_NAME}-scratch.md"
|
||||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
# ── Guards ────────────────────────────────────────────────────────────────
|
# ── Guards ────────────────────────────────────────────────────────────────
|
||||||
|
check_active planner
|
||||||
acquire_cron_lock "/tmp/planner-run.lock"
|
acquire_cron_lock "/tmp/planner-run.lock"
|
||||||
check_memory 2000
|
check_memory 2000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ source "$FACTORY_ROOT/lib/env.sh"
|
||||||
source "$FACTORY_ROOT/lib/agent-session.sh"
|
source "$FACTORY_ROOT/lib/agent-session.sh"
|
||||||
# shellcheck source=../lib/formula-session.sh
|
# shellcheck source=../lib/formula-session.sh
|
||||||
source "$FACTORY_ROOT/lib/formula-session.sh"
|
source "$FACTORY_ROOT/lib/formula-session.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$FACTORY_ROOT/lib/guard.sh"
|
||||||
|
|
||||||
LOG_FILE="$SCRIPT_DIR/predictor.log"
|
LOG_FILE="$SCRIPT_DIR/predictor.log"
|
||||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||||
|
|
@ -38,6 +40,7 @@ SCRATCH_FILE="/tmp/predictor-${PROJECT_NAME}-scratch.md"
|
||||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
# ── Guards ────────────────────────────────────────────────────────────────
|
# ── Guards ────────────────────────────────────────────────────────────────
|
||||||
|
check_active predictor
|
||||||
acquire_cron_lock "/tmp/predictor-run.lock"
|
acquire_cron_lock "/tmp/predictor-run.lock"
|
||||||
check_memory 2000
|
check_memory 2000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ set -euo pipefail
|
||||||
export PROJECT_TOML="${1:-}"
|
export PROJECT_TOML="${1:-}"
|
||||||
source "$(dirname "$0")/../lib/env.sh"
|
source "$(dirname "$0")/../lib/env.sh"
|
||||||
source "$(dirname "$0")/../lib/ci-helpers.sh"
|
source "$(dirname "$0")/../lib/ci-helpers.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$(dirname "$0")/../lib/guard.sh"
|
||||||
|
check_active reviewer
|
||||||
|
|
||||||
REPO_ROOT="${PROJECT_REPO_ROOT}"
|
REPO_ROOT="${PROJECT_REPO_ROOT}"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
|
||||||
2
state/.gitignore
vendored
Normal file
2
state/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Active-state files are runtime state, not committed
|
||||||
|
.*-active
|
||||||
|
|
@ -28,6 +28,8 @@ source "$FACTORY_ROOT/lib/env.sh"
|
||||||
source "$FACTORY_ROOT/lib/agent-session.sh"
|
source "$FACTORY_ROOT/lib/agent-session.sh"
|
||||||
# shellcheck source=../lib/formula-session.sh
|
# shellcheck source=../lib/formula-session.sh
|
||||||
source "$FACTORY_ROOT/lib/formula-session.sh"
|
source "$FACTORY_ROOT/lib/formula-session.sh"
|
||||||
|
# shellcheck source=../lib/guard.sh
|
||||||
|
source "$FACTORY_ROOT/lib/guard.sh"
|
||||||
|
|
||||||
LOG_FILE="$SCRIPT_DIR/supervisor.log"
|
LOG_FILE="$SCRIPT_DIR/supervisor.log"
|
||||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||||
|
|
@ -42,6 +44,7 @@ SCRATCH_FILE="/tmp/supervisor-${PROJECT_NAME}-scratch.md"
|
||||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
# ── Guards ────────────────────────────────────────────────────────────────
|
# ── Guards ────────────────────────────────────────────────────────────────
|
||||||
|
check_active supervisor
|
||||||
acquire_cron_lock "/tmp/supervisor-run.lock"
|
acquire_cron_lock "/tmp/supervisor-run.lock"
|
||||||
check_memory 2000
|
check_memory 2000
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue