Files
timmy-config/bin/jidoka-gate.sh
STEP35 Burn Agent f32ff627c1 feat(jidoka): implement auto-halt gate for quality drops — stop the line on defect
Implements Jidoka (自働化) — automation with a human touch.
When the agent loop produces defective work, the line stops.

Implementation:
- bin/jidoka-gate.sh — gate script that checks quality of last N completions
- bin/quality-verify.sh — per-issue quality checker (PR exists, has files, mergeable, completion marker)
- Integrated into agent-loop.sh, claude-loop.sh, gemini-loop.sh — runs every JIDOKA_CHECK_INTERVAL completions (default 10)
- If >= JIDOKA_FAIL_THRESHOLD of SAMPLE_SIZE checks fail, a halt flag is created at ~/.hermes/logs/{agent}-jidoka-halt
- Telegram alert is sent on halt via existing bot token mechanism
- bin/claudemax-watchdog.sh updated to respect the halt flag — will NOT restart a halted agent

Configuration via environment:
- JIDOKA_CHECK_INTERVAL (default 10) — completions between gate checks
- JIDOKA_SAMPLE_SIZE (default 5) — how many recent closed issues to sample
- JIDOKA_FAIL_THRESHOLD (default 3) — failures needed to trigger halt

Accepts issue #346 as Closes.

Refs: #345 (Epic: Five Japanese Wisdoms)

Co-authored-by: Timmy <step35@burn.in>
2026-04-29 01:25:09 -04:00

94 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# jidoka-gate.sh — Stop the line on defect. Auto-halt loops when quality drops.
#
# Usage: jidoka-gate.sh <agent-name> <completions-checked> <fail-threshold>
# jidoka-gate.sh claude 5 3
#
# Checks quality of the last N completed issues using quality-verify.sh.
# If failures >= threshold, creates ~/.hermes/logs/{agent}-jidoka-halt flag
# and sends Telegram alert. Returns 0 = OK to continue, 1 = HALT triggered.
set -uo pipefail
AGENT="${1:?Usage: $0 <agent-name> <completions-checked> <fail-threshold>}"
CHECK_COUNT="${2:-5}"
FAIL_THRESHOLD="${3:-3}"
GITEA_URL="${GITEA_URL:-https://forge.alexanderwhitestone.com}"
LOG_DIR="${HOME}/.hermes/logs"
HALT_FLAG="${LOG_DIR}/${AGENT}-jidoka-halt"
TOKEN_FILE="${HOME}/.config/gitea/token"
TELEGRAM_TOKEN_FILE="${HOME}/.hermes/telegram_bot_token"
TELEGRAM_CHAT="-1003664764329"
mkdir -p "$LOG_DIR"
if [ ! -f "$TOKEN_FILE" ]; then
echo "ERROR: Gitea token not found at $TOKEN_FILE" >&2
exit 1
fi
GITEA_TOKEN="$(cat "$TOKEN_FILE" | tr -d '\n')"
TELEGRAM_TOKEN=""
if [ -f "$TELEGRAM_TOKEN_FILE" ]; then
TELEGRAM_TOKEN="$(cat "$TELEGRAM_TOKEN_FILE" | tr -d '\n')"
fi
if [ -f "$HALT_FLAG" ]; then
echo "JIDOKA HALT ACTIVE: $HALT_FLAG exists. Loop must not continue."
exit 1
fi
# Get last CHECK_COUNT closed issues assigned to this agent
SINCE="$(date -v-24H '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ')"
ISSUES_JSON=$(curl -sf "${GITEA_URL}/api/v1/repos/Timmy_Foundation/timmy-config/issues?state=closed&limit=${CHECK_COUNT}&sort=updated&direction=desc&since=${SINCE}" \
-H "Authorization: token ${GITEA_TOKEN}")
mapfile -t ISSUE_NUMS < <(echo "$ISSUES_JSON" | python3 -c "
import sys, json
agent = '${AGENT}'.lower()
issues = json.load(sys.stdin)
for iss in issues:
assignee = iss.get('assignee') or {}
if assignee and agent in assignee.get('login', '').lower():
print(iss['number'])
if len(issues) >= ${CHECK_COUNT}: break
" | head -n "${CHECK_COUNT}")
if [ ${#ISSUE_NUMS[@]} -lt "$CHECK_COUNT" ]; then
echo "JIDOKA: Only ${#ISSUE_NUMS[@]} recent closed issues found (< ${CHECK_COUNT}). Skipping gate."
exit 0
fi
FAIL_COUNT=0
RESULTS=()
for issue_num in "${ISSUE_NUMS[@]}"; do
if bash "$(dirname "$0")/quality-verify.sh" "$issue_num"; then
RESULTS+=("PASS: #${issue_num}")
else
RESULTS+=("FAIL: #${issue_num}")
((FAIL_COUNT++))
fi
done
echo "JIDOKA Gate results: ${FAIL_COUNT}/${CHECK_COUNT} failed"
printf ' %s\n' "${RESULTS[@]}"
if [ "$FAIL_COUNT" -ge "$FAIL_THRESHOLD" ]; then
echo "JIDOKA: Quality threshold breached (${FAIL_COUNT} >= ${FAIL_THRESHOLD}). STOPPING THE LINE."
echo " halted_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ') agent=${AGENT} failures=${FAIL_COUNT}/${CHECK_COUNT}" > "$HALT_FLAG"
if [ -n "$TELEGRAM_TOKEN" ]; then
MSG="JIDOKA: Line stopped. ${AGENT} failing quality checks. Last ${CHECK_COUNT}: ${FAIL_COUNT} failed. Threshold: ${FAIL_THRESHOLD}. Flag: $HALT_FLAG"
curl -sf -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{\"chat_id\":\"${TELEGRAM_CHAT}\",\"text\":\"${MSG}\"}" >/dev/null 2>&1 || true
fi
exit 1
fi
exit 0