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>
94 lines
3.1 KiB
Bash
Executable File
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
|