From a024b78cdeee5d6b947bd772bbacd2898a0e6cb7 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Fri, 4 Sep 2026 07:16:24 -0500 Subject: [PATCH] =?UTF-8?q?feat(hooks):=20CHANGE=20GATE=20=E2=80=94=20prod?= =?UTF-8?q?=20remote=20ops=20require=20.crush/active-cr=20[#767]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Q3 ruling 2026-09-04: Tier 2 of the t/325 ladder is mechanical. Prod- target remote.sh/remote-dns.sh invocations block without a filed GLPI Change id; dev lanes (sectestbed/preprod/test/tsys5/sandbox/kali) exempt; CR tooling exempt (chicken-and-egg). 7-case matrix green, shellcheck clean. https://projects.knownelement.com/issues/767#note-4191 --- hooks/ticket-gate.sh | 54 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/hooks/ticket-gate.sh b/hooks/ticket-gate.sh index 582f335..123fa35 100755 --- a/hooks/ticket-gate.sh +++ b/hooks/ticket-gate.sh @@ -41,6 +41,10 @@ if [ "$TOOL" = "bash" ]; then case "$CMD" in *active-ticket*) exit 0 ;; esac + # Filing/managing GLPI CRs (chicken-and-egg: CR tooling must always run) + case "$CMD" in + *active-cr*|*"glpi-change"*) exit 0 ;; + esac fi # For edit/write: exempt policy/hook files (these ARE the policy) @@ -64,10 +68,8 @@ for f in "$CRUSH_PROJECT_DIR"/.crush/active-ticket*; do GATE_OK=1 printf '{"context":"Active ticket(s): %s -> %s"}\n' "$(basename "$f")" "$(cat "$f")" done -[ "$GATE_OK" -eq 1 ] && exit 0 - -# No active ticket — block -cat >&2 <<'MSG' +if [ "$GATE_OK" -ne 1 ]; then + cat >&2 <<'MSG' TICKET GATE: No active ticket set. This project requires ticket-governed work (AGENTS.md Agent Authority). @@ -80,4 +82,46 @@ Clear the ticket when work is complete: > .crush/active-ticket MSG -exit 2 + exit 2 +fi + +# CHANGE GATE [#767] (t/325 §5 ladder; Q3 ruling 2026-09-04): prod-target +# remote operations additionally require a filed GLPI Change — its id in +# .crush/active-cr (clear when the CR is closed: > .crush/active-cr) +# Dev-lane targets are exempt (sectestbed/preprod/test/sandbox = Tier 0/1). +# File a CR in one line (tooling lives in KNEL/inventory): +# echo "why/what/where" | scripts/glpi-change.sh create --agent --title "[#NNN] summary" +if [ "$TOOL" = "bash" ]; then + case "$CMD" in + *"remote.sh vm-file"*|*"remote.sh prox-file"*|*"remote.sh vm-copy"*|*"remote.sh prox-copy"*|*"remote.sh vm"*|*"remote.sh prox"*|*"remote-dns.sh "*) + PROD=1 + case "$CMD" in + *sectestbed*|*preprod*|*glpi-test*|*kali*|*tsys5*|*sandbox*) PROD=0 ;; + esac + if [ "$PROD" -eq 1 ]; then + CR_OK=0 + for f in "$CRUSH_PROJECT_DIR"/.crush/active-cr*; do + [ -f "$f" ] && [ -s "$f" ] || continue + CR_OK=1 + printf '{"context":"Active CR: %s -> %s"}\n' "$(basename "$f")" "$(cat "$f")" + done + if [ "$CR_OK" -ne 1 ]; then + cat >&2 <<'MSG' +CHANGE GATE: prod-target remote operation requires a filed GLPI Change. + +File one, then set its id (Tier 2 of the t/325 escalation ladder): + + echo "what/why/where" | scripts/glpi-change.sh create --agent --title "[#NNN] summary" + echo '' > .crush/active-cr + +Dev-lane targets (sectestbed/preprod/test/tsys5) are exempt. +Clear when the CR closes: > .crush/active-cr +MSG + exit 2 + fi + fi + ;; + esac +fi + +exit 0