feat: brain-template v2.0 — BSI-v3 complet + tiers documentés
- README reécrit : tiers free/pro/full + modèle clé API + multi-instance - Sync agents/ (57 agents, kernel-isolation validated) - Sync scripts/ BSI-v3 (file-lock, preflight, human-gate, brain-status) - KERNEL.md v0.7.0 — zones + délégation + rendering + isolation - brain-compose.yml v0.7.0 — rendering mode + kerneluser - workflows/ — template + brain-engine exemple - locks/.gitkeep + claims/.gitkeep - helloWorld : RAG boot tier full only (bsi-rag retiré du template)
This commit is contained in:
324
scripts/human-gate-ack.sh
Executable file
324
scripts/human-gate-ack.sh
Executable file
@@ -0,0 +1,324 @@
|
||||
#!/bin/bash
|
||||
# human-gate-ack.sh — BSI-v3-5 Human Gate
|
||||
# Gère les pauses planifiées (gate:human) et les arrêts d'urgence (pause/resume/abort).
|
||||
# Point de contrôle humain sur le flux satellite.
|
||||
#
|
||||
# Usage :
|
||||
# human-gate-ack.sh gate <sess_id> [message] → déclare un gate:human (satellite s'arrête)
|
||||
# human-gate-ack.sh approve <sess_id> [message] → valide le gate → reprise
|
||||
# human-gate-ack.sh reject <sess_id> [message] → refuse le gate → failed
|
||||
# human-gate-ack.sh pause <sess_id> [message] → arrêt d'urgence (cascade enfants)
|
||||
# human-gate-ack.sh resume <sess_id> [message] → reprise après pause
|
||||
# human-gate-ack.sh abort <sess_id> [message] → abandon définitif
|
||||
# human-gate-ack.sh status <sess_id> → état du claim + enfants
|
||||
#
|
||||
# Statuts claim :
|
||||
# open → travail en cours
|
||||
# waiting_human → gate:human déclaré — attend confirmation
|
||||
# paused → arrêt d'urgence — pre-flight bloque enfants en cascade
|
||||
# closed → terminé ok
|
||||
# failed → terminé en erreur
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BRAIN_ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)"
|
||||
CLAIMS_DIR="$BRAIN_ROOT/claims"
|
||||
|
||||
# --- Helpers ---
|
||||
|
||||
get_claim_file() {
|
||||
echo "$CLAIMS_DIR/${1}.yml"
|
||||
}
|
||||
|
||||
get_status() {
|
||||
local claim_file="$1"
|
||||
grep '^status:' "$claim_file" | sed 's/^[^:]*: *//' | tr -d '"' | head -1
|
||||
}
|
||||
|
||||
set_status() {
|
||||
local claim_file="$1"
|
||||
local new_status="$2"
|
||||
sed -i "s/^status:.*/status: $new_status/" "$claim_file"
|
||||
}
|
||||
|
||||
append_gate_event() {
|
||||
local claim_file="$1"
|
||||
local event="$2"
|
||||
local message="${3:-}"
|
||||
local ts
|
||||
ts=$(date +%Y-%m-%dT%H:%M)
|
||||
if ! grep -q '^gate_history:' "$claim_file"; then
|
||||
echo "gate_history:" >> "$claim_file"
|
||||
fi
|
||||
if [ -n "$message" ]; then
|
||||
echo " - { ts: \"$ts\", event: $event, message: \"$message\" }" >> "$claim_file"
|
||||
else
|
||||
echo " - { ts: \"$ts\", event: $event }" >> "$claim_file"
|
||||
fi
|
||||
}
|
||||
|
||||
write_signal() {
|
||||
local sess_id="$1"
|
||||
local signal_type="$2"
|
||||
local message="${3:-}"
|
||||
local sig_id="sig-$(date +%Y%m%d-%H%M%S)-${signal_type,,}"
|
||||
local brain_index="$BRAIN_ROOT/BRAIN-INDEX.md"
|
||||
local ts
|
||||
ts=$(date +%Y-%m-%dT%H:%M)
|
||||
|
||||
# Insérer dans la table signals de BRAIN-INDEX.md
|
||||
local signal_row="| $sig_id | $sess_id | — | $signal_type | ${message:-$signal_type} | pending |"
|
||||
if grep -q '## Signals' "$brain_index" 2>/dev/null; then
|
||||
sed -i "/^## Signals/a $signal_row" "$brain_index"
|
||||
fi
|
||||
echo "$sig_id"
|
||||
}
|
||||
|
||||
# Trouve tous les enfants directs d'un claim (parent_satellite = sess_id)
|
||||
find_children() {
|
||||
local parent_id="$1"
|
||||
grep -l "parent_satellite:.*$parent_id" "$CLAIMS_DIR"/*.yml 2>/dev/null \
|
||||
| xargs -I{} basename {} .yml 2>/dev/null || true
|
||||
}
|
||||
|
||||
# --- GATE (satellite déclare son arrêt planifié) ---
|
||||
cmd_gate() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-gate:human déclenché}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
local current
|
||||
current=$(get_status "$claim_file")
|
||||
if [ "$current" != "open" ]; then
|
||||
echo "❌ Claim non-open (status: $current) — gate:human ignoré"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set_status "$claim_file" "waiting_human"
|
||||
append_gate_event "$claim_file" "HUMAN_GATE" "$message"
|
||||
write_signal "$sess_id" "HUMAN_GATE" "$message" > /dev/null
|
||||
|
||||
echo "🔶 HUMAN GATE — $sess_id"
|
||||
echo " Message : $message"
|
||||
echo " Status : waiting_human"
|
||||
echo " Commande : human-gate-ack.sh approve|reject $sess_id"
|
||||
}
|
||||
|
||||
# --- APPROVE ---
|
||||
cmd_approve() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-approuvé}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
local current
|
||||
current=$(get_status "$claim_file")
|
||||
if [ "$current" != "waiting_human" ]; then
|
||||
echo "❌ Claim non en waiting_human (status: $current)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set_status "$claim_file" "open"
|
||||
append_gate_event "$claim_file" "APPROVED" "$message"
|
||||
|
||||
echo "✅ Gate approuvé — $sess_id"
|
||||
echo " Satellite peut reprendre."
|
||||
}
|
||||
|
||||
# --- REJECT ---
|
||||
cmd_reject() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-refusé}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
set_status "$claim_file" "failed"
|
||||
append_gate_event "$claim_file" "REJECTED" "$message"
|
||||
write_signal "$sess_id" "BLOCKED_ON" "$message" > /dev/null
|
||||
|
||||
echo "🚫 Gate refusé — $sess_id → failed"
|
||||
}
|
||||
|
||||
# --- PAUSE (arrêt d'urgence + cascade) ---
|
||||
cmd_pause() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-pause urgence}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
local current
|
||||
current=$(get_status "$claim_file")
|
||||
if [ "$current" = "closed" ] || [ "$current" = "failed" ]; then
|
||||
echo "❌ Claim déjà terminé (status: $current)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set_status "$claim_file" "paused"
|
||||
append_gate_event "$claim_file" "PAUSED" "$message"
|
||||
write_signal "$sess_id" "PAUSED" "$message" > /dev/null
|
||||
|
||||
echo "⏸ PAUSE — $sess_id"
|
||||
echo " Message : $message"
|
||||
echo " Cascade : pré-flight bloquera tous les enfants"
|
||||
|
||||
# Cascade — pause récursive des enfants open/waiting
|
||||
local children
|
||||
children=$(find_children "$sess_id")
|
||||
if [ -n "$children" ]; then
|
||||
echo " Enfants :"
|
||||
for child_id in $children; do
|
||||
local child_file
|
||||
child_file=$(get_claim_file "$child_id")
|
||||
local child_status
|
||||
child_status=$(get_status "$child_file")
|
||||
if [ "$child_status" = "open" ] || [ "$child_status" = "waiting_human" ]; then
|
||||
set_status "$child_file" "paused"
|
||||
append_gate_event "$child_file" "PAUSED_CASCADE" "parent $sess_id paused"
|
||||
echo " ⏸ $child_id (cascade)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Reprise : human-gate-ack.sh resume $sess_id"
|
||||
echo " Abandon : human-gate-ack.sh abort $sess_id"
|
||||
}
|
||||
|
||||
# --- RESUME ---
|
||||
cmd_resume() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-reprise}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
local current
|
||||
current=$(get_status "$claim_file")
|
||||
if [ "$current" != "paused" ]; then
|
||||
echo "❌ Claim non en pause (status: $current)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set_status "$claim_file" "open"
|
||||
append_gate_event "$claim_file" "RESUMED" "$message"
|
||||
|
||||
echo "▶️ RESUME — $sess_id"
|
||||
|
||||
# Cascade resume des enfants paused par cascade
|
||||
local children
|
||||
children=$(find_children "$sess_id")
|
||||
if [ -n "$children" ]; then
|
||||
for child_id in $children; do
|
||||
local child_file
|
||||
child_file=$(get_claim_file "$child_id")
|
||||
local child_status
|
||||
child_status=$(get_status "$child_file")
|
||||
if [ "$child_status" = "paused" ]; then
|
||||
# Vérifier que la pause vient bien d'une cascade (pas d'une pause manuelle directe)
|
||||
if grep -q "PAUSED_CASCADE" "$child_file" 2>/dev/null; then
|
||||
set_status "$child_file" "open"
|
||||
append_gate_event "$child_file" "RESUMED_CASCADE" "parent $sess_id resumed"
|
||||
echo " ▶️ $child_id (cascade)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo " Satellite peut reprendre — pre-flight passera CHECK 1."
|
||||
}
|
||||
|
||||
# --- ABORT ---
|
||||
cmd_abort() {
|
||||
local sess_id="$1"
|
||||
local message="${2:-abandon}"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
set_status "$claim_file" "failed"
|
||||
append_gate_event "$claim_file" "ABORTED" "$message"
|
||||
write_signal "$sess_id" "BLOCKED_ON" "aborted: $message" > /dev/null
|
||||
|
||||
echo "💀 ABORT — $sess_id → failed"
|
||||
|
||||
# Cascade abort des enfants
|
||||
local children
|
||||
children=$(find_children "$sess_id")
|
||||
if [ -n "$children" ]; then
|
||||
for child_id in $children; do
|
||||
local child_file
|
||||
child_file=$(get_claim_file "$child_id")
|
||||
local child_status
|
||||
child_status=$(get_status "$child_file")
|
||||
if [ "$child_status" != "closed" ] && [ "$child_status" != "failed" ]; then
|
||||
set_status "$child_file" "failed"
|
||||
append_gate_event "$child_file" "ABORTED_CASCADE" "parent $sess_id aborted"
|
||||
echo " 💀 $child_id (cascade)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# --- STATUS ---
|
||||
cmd_status() {
|
||||
local sess_id="$1"
|
||||
local claim_file
|
||||
claim_file=$(get_claim_file "$sess_id")
|
||||
|
||||
[ -f "$claim_file" ] || { echo "❌ Claim introuvable : $sess_id"; exit 1; }
|
||||
|
||||
local current
|
||||
current=$(get_status "$claim_file")
|
||||
local scope
|
||||
scope=$(grep '^scope:' "$claim_file" | sed 's/^[^:]*: *//' | tr -d '"')
|
||||
|
||||
case "$current" in
|
||||
open) echo "🟢 open — $sess_id [$scope]" ;;
|
||||
waiting_human) echo "🔶 waiting_human — $sess_id [$scope]" ;;
|
||||
paused) echo "⏸ paused — $sess_id [$scope]" ;;
|
||||
closed) echo "✅ closed — $sess_id [$scope]" ;;
|
||||
failed) echo "❌ failed — $sess_id [$scope]" ;;
|
||||
*) echo "❓ $current — $sess_id [$scope]" ;;
|
||||
esac
|
||||
|
||||
# Enfants
|
||||
local children
|
||||
children=$(find_children "$sess_id")
|
||||
if [ -n "$children" ]; then
|
||||
echo " Enfants :"
|
||||
for child_id in $children; do
|
||||
local child_file
|
||||
child_file=$(get_claim_file "$child_id")
|
||||
local child_status
|
||||
child_status=$(get_status "$child_file")
|
||||
echo " $child_status — $child_id"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Router ---
|
||||
CMD="${1:-}"
|
||||
case "$CMD" in
|
||||
gate) cmd_gate "${2:-}" "${3:-}" ;;
|
||||
approve) cmd_approve "${2:-}" "${3:-}" ;;
|
||||
reject) cmd_reject "${2:-}" "${3:-}" ;;
|
||||
pause) cmd_pause "${2:-}" "${3:-}" ;;
|
||||
resume) cmd_resume "${2:-}" "${3:-}" ;;
|
||||
abort) cmd_abort "${2:-}" "${3:-}" ;;
|
||||
status) cmd_status "${2:-}" ;;
|
||||
*)
|
||||
echo "Usage : human-gate-ack.sh <gate|approve|reject|pause|resume|abort|status> <sess_id> [message]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user