2026-06-10 12:51:46 +02:00
#!/usr/bin/env bash
#
2026-06-14 21:46:23 +02:00
# Stop guard for two external-skill gates that conflict with boma conventions, where
# prose reminders repeatedly failed to hold (docs/FRICTION.md):
#
# 1. The execution-mode menu — writing-plans / subagent-driven-development script a
# "Subagent-Driven vs Inline Execution — which approach?" menu at the plan→execution
# handoff. boma's standing preference is to NEVER present it and proceed
2026-06-17 17:49:55 +02:00
# subagent-driven. (Recorded by the 2026-06-10 kaizen review; the 2026-06-17 review
# widened the matcher to also catch free-form *prose* re-asks of the same choice —
# e.g. "which execution approach?" — which the literal-menu matcher missed. The
# sibling push-vs-not re-ask is deliberately NOT hooked: a genuine "should I push?"
# is sometimes legitimate, so it stays a soft default via the
# dont-reask-settled-defaults memory rather than a hard block.)
2026-06-14 21:46:23 +02:00
# 2. The brainstorming spec-review gate — the brainstorming skill scripts "Spec written
# and committed … please review it before … the implementation plan." The standing
# agreement is to move directly from the committed spec to writing-plans. (Recorded
# by the 2026-06-14 kaizen review; 06-10/06-14 recurrences.)
2026-06-10 12:51:46 +02:00
#
# Fails OPEN: any parse/read problem → allow the stop. Respects stop_hook_active so a
2026-06-14 21:46:23 +02:00
# block can never loop. Match signatures are deliberately tight so they fire on the
# actual gate text, not on meta-discussion of it.
2026-06-10 12:51:46 +02:00
#
set -uo pipefail
input = $( cat 2>/dev/null) || exit 0
# Loop guard: if we already blocked once for this stop, let it through.
active = $( printf '%s' " $input " | jq -r '.stop_hook_active // false' 2>/dev/null) || exit 0
[ " $active " = "true" ] && exit 0
transcript = $( printf '%s' " $input " | jq -r '.transcript_path // empty' 2>/dev/null) || exit 0
[ -z " $transcript " ] || [ ! -r " $transcript " ] && exit 0
# Last assistant message's text blocks, joined.
text = $( jq -rs '
( [ .[ ] | select ( .type= = "assistant" ) ] | last) as $a
| ( $a .message.content // [ ] )
| if type = = "array" then [ .[ ] | select ( .type= = "text" ) | .text ] | join( "\n" )
elif type = = "string" then .
else "" end
' " $transcript " 2>/dev/null) || exit 0
low = " ${ text ,, } "
if [ [ " $low " = = *"inline execution" * \
2026-06-17 17:49:55 +02:00
&& ( " $low " = = *"which approach" * || " $low " = = *"two execution options" * ) ] ] \
|| [ [ " $low " = = *"subagent-driven or inline" * || " $low " = = *"inline or subagent" * ] ] \
|| [ [ " $low " = = *"subagent-driven vs inline" * || " $low " = = *"subagent vs inline" * \
|| " $low " = = *"inline vs subagent" * ] ] \
|| [ [ " $low " = = *"execution approach" * && " $low " = = *"?" * ] ] ; then
2026-06-10 12:51:46 +02:00
cat <<'JSON'
{ "decision" :"block" ,"reason" :"Execution-mode menu detected in your final message. boma standing preference (docs/FRICTION.md + always-subagent-driven-execution memory): never present the subagent-driven-vs-inline menu. Drop the menu and proceed with subagent-driven execution directly (superpowers:subagent-driven-development)." }
JSON
exit 0
fi
2026-06-14 21:46:23 +02:00
# Brainstorming spec-review gate: asking the user to review the committed spec before
# the implementation plan. Tight signature: "implementation plan" present, plus either the
# skill's literal "spec written and committed" line, or the review+spec+before combination.
if [ [ " $low " = = *"implementation plan" * \
&& ( " $low " = = *"spec written and committed" * \
|| ( " $low " = = *"review" * && " $low " = = *"the spec" * && " $low " = = *"before" * ) ) ] ] ; then
cat <<'JSON'
{ "decision" :"block" ,"reason" :"Brainstorming spec-review gate detected in your final message. boma standing agreement (docs/FRICTION.md): once the spec is written and committed, move directly to the implementation plan (superpowers:writing-plans) — do not stop to ask the user to review the spec first. Drop the gate and proceed." }
JSON
exit 0
fi
2026-06-10 12:51:46 +02:00
exit 0