fix(work): import hashlib and wire heal subparser into main CLI
This commit is contained in:
@@ -42,6 +42,15 @@ REALERT_MIN="${FLEET_ALERT_REALERT_MIN:-30}"
|
||||
# forever. Overridable per environment.
|
||||
INPUT_WAIT_TTL="${FLEET_ALERT_INPUT_WAIT_TTL:-1800}"
|
||||
BROWSER_APPROVAL_TTL="${FLEET_ALERT_BROWSER_APPROVAL_TTL:-1800}"
|
||||
# Routine input_wait task patterns (2026-10-08, P4): scheduled-task
|
||||
# confirmations matching these (case-insensitive) are noise-grade
|
||||
# housekeeping that auto-dismisses at TTL. They go to the digest
|
||||
# (kind=DIGEST in the outbox; the #lobby relay ignores non-ALERT/
|
||||
# RECOVERY kinds) instead of paging CRITICAL. Anything NOT matching
|
||||
# stays CRITICAL (fail-closed). Pipe-separated; overridable per
|
||||
# environment. ALL of a node's waits must match for the node to
|
||||
# classify as routine.
|
||||
INPUT_WAIT_ROUTINE_PATTERNS="${FLEET_ALERT_INPUT_WAIT_ROUTINE:-scavenger|background worker|daily checkin|auto-work-queue}"
|
||||
QUIET_HOURS="${FLEET_ALERT_QUIET_HOURS:-}"
|
||||
DRY_RUN="${FLEET_ALERT_DRY_RUN:-0}"
|
||||
INJECT_FAIL="${FLEET_ALERT_INJECT_FAIL:-}"
|
||||
@@ -196,6 +205,48 @@ notify_input_wait() {
|
||||
fi
|
||||
}
|
||||
|
||||
input_wait_routine() { # <node_data_json> -> prints 1 if ALL waits match routine patterns, else 0
|
||||
# P4 (2026-10-08): classify a node's input waits as routine (digest)
|
||||
# or novel (CRITICAL). Fail-closed: empty/unparseable waits, empty
|
||||
# patterns, regex errors, or ANY non-matching wait -> 0 (page it).
|
||||
INPUT_WAIT_ROUTINE_PATTERNS="$INPUT_WAIT_ROUTINE_PATTERNS" python3 - "$1" <<'PYEOF'
|
||||
import json, os, re, sys
|
||||
pats = [p.strip() for p in os.environ.get("INPUT_WAIT_ROUTINE_PATTERNS", "").split("|") if p.strip()]
|
||||
try:
|
||||
waits = json.loads(sys.argv[1]).get("waits", [])
|
||||
except Exception:
|
||||
waits = []
|
||||
if not waits or not pats:
|
||||
print(0)
|
||||
sys.exit()
|
||||
for w in waits:
|
||||
task = w.get("task") or ""
|
||||
try:
|
||||
matched = any(re.search(p, task, re.I) for p in pats)
|
||||
except re.error:
|
||||
matched = False
|
||||
if not matched:
|
||||
print(0)
|
||||
sys.exit()
|
||||
print(1)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
target_plausible_false() { # <node_data_json> -> prints 1 if target_plausible is explicitly false, else 0
|
||||
# P1 follow-up (2026-10-08): the approval target parser flags garbage
|
||||
# tokens (e.g. "echo", "true") as target_plausible=false. Implausible
|
||||
# targets go to the digest instead of paging CRITICAL. Fail-closed:
|
||||
# missing field, null, non-boolean, or unparseable JSON -> 0 (page it).
|
||||
python3 - "$1" <<'PYEOF_INNER'
|
||||
import json, sys
|
||||
try:
|
||||
v = json.loads(sys.argv[1]).get("target_plausible")
|
||||
except Exception:
|
||||
v = None
|
||||
print(1 if v is False else 0)
|
||||
PYEOF_INNER
|
||||
}
|
||||
|
||||
injected() { # cond -> 0 if injected-fail
|
||||
case ",$INJECT_FAIL," in *,"$1,"*) return 0;; *) return 1;; esac
|
||||
}
|
||||
@@ -241,6 +292,7 @@ info = approvals.inspect_node_approvals('$node')
|
||||
out = {
|
||||
'has_pending': info.get('has_pending', False),
|
||||
'target': info.get('target') or info.get('ip') or 'unknown',
|
||||
'target_plausible': info.get('target_plausible'),
|
||||
'title': info.get('title') or '',
|
||||
'waits': info.get('input_waits') or []
|
||||
}
|
||||
@@ -262,8 +314,20 @@ print(json.dumps(out))
|
||||
read -r action fails < <(state_machine "$cond" "$failing" "$BROWSER_APPROVAL_TTL")
|
||||
case "$action" in
|
||||
ALERT_FIRST|ALERT_REALERT)
|
||||
emit_record "ALERT" "$cond" "$detail" "$fails"
|
||||
echo "$cond" >> "$STATE_DIR/.alerts.tmp"
|
||||
if [ "$(target_plausible_false "$node_data")" = "1" ]; then
|
||||
# P1 follow-up (2026-10-08): implausible approval target
|
||||
# (parser artifact, target_plausible=false) -> digest, don't
|
||||
# page. kind=DIGEST is ignored by the #lobby relay; the
|
||||
# triage digest consumer batches these. No .alerts.tmp
|
||||
# entry, so no box_notify broadcast either — the digest is
|
||||
# the only output. Missing/unparseable field -> CRITICAL
|
||||
# (fail-closed; handled inside target_plausible_false).
|
||||
emit_record "DIGEST" "$cond" "implausible target: $detail" "$fails"
|
||||
log "$cond implausible target x$fails — digested, not paged"
|
||||
else
|
||||
emit_record "ALERT" "$cond" "$detail" "$fails"
|
||||
echo "$cond" >> "$STATE_DIR/.alerts.tmp"
|
||||
fi
|
||||
;;
|
||||
RECOVERY)
|
||||
emit_record "RECOVERY" "$cond" "$detail" "$fails"
|
||||
@@ -308,8 +372,17 @@ if w:
|
||||
read -r action_in fails_in < <(state_machine "$cond_in" "$failing_in" "$INPUT_WAIT_TTL")
|
||||
case "$action_in" in
|
||||
ALERT_FIRST|ALERT_REALERT)
|
||||
emit_record "ALERT" "$cond_in" "$detail_in" "$fails_in"
|
||||
echo "$cond_in|$detail_in" >> "$STATE_DIR/.alerts.tmp"
|
||||
if [ "$(input_wait_routine "$node_data")" = "1" ]; then
|
||||
# P4 (2026-10-08): routine housekeeping -> digest, don't page.
|
||||
# kind=DIGEST is ignored by the #lobby relay; the triage
|
||||
# digest consumer batches these. No .alerts.tmp entry, so
|
||||
# no targeted DM either — the digest is the only output.
|
||||
emit_record "DIGEST" "$cond_in" "routine: $detail_in" "$fails_in"
|
||||
log "$cond_in routine input_wait x$fails_in — digested, not paged"
|
||||
else
|
||||
emit_record "ALERT" "$cond_in" "$detail_in" "$fails_in"
|
||||
echo "$cond_in|$detail_in" >> "$STATE_DIR/.alerts.tmp"
|
||||
fi
|
||||
;;
|
||||
RECOVERY)
|
||||
emit_record "RECOVERY" "$cond_in" "$detail_in" "$fails_in"
|
||||
|
||||
Reference in New Issue
Block a user