fix(design-gate): fail-visible on unverified tools (no silent READY)

When `claude` is unreachable, the plugin/MCP checks (magic, ui-ux-pro-max —
the most important design tools) return `unknown`. The old verdict folded that
into a silent `READY` exit 0 — a fail-OPEN: the gate granted "proceed" exactly
when it had verified nothing. Contradicts the fail-closed-on-uncertainty rule.

Now fail-VISIBLE (not strict fail-closed — claude can be merely slow, false
blocks would get the gate ignored): a third outcome READY BUT UNVERIFIED with a
distinct exit 11. It proceeds, but says so loudly and names the unchecked tools,
telling the user to confirm with `claude mcp list` / `claude plugin list`. The
distinct non-zero code also stops a naive `if gate; then proceed` shell caller
from silently passing. Also covers the non-reproducible "transient claude absent
after apply" flake seen in live testing.

design-gate.md §DECISION updated: exit-code line + a real branch-3 state for 11.

Verified: shellcheck clean; reachable+active -> READY exit 0; claude off PATH ->
READY BUT UNVERIFIED exit 11 naming magic+ui-ux-pro-max; magic off -> INCOMPLETE
exit 10 (trip branch intact after the restructure).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bastien Chanot
2026-06-21 11:57:48 +02:00
co-authored by Claude Opus 4.8
parent 131d0bcb5d
commit 4d191354c6
2 changed files with 37 additions and 26 deletions
+31 -23
View File
@@ -36,7 +36,7 @@
# disabledMcpServers is NEVER read — unreliable for bi-modal servers
# (magic/context7 can appear there yet be active via another channel).
#
# Exit: 0 = ready (proceed) · 10 = incomplete (gate trips) · 2 = error.
# Exit: 0 = ready · 11 = ready-but-unverified (proceed, say so) · 10 = incomplete (trips) · 2 = error.
# Usage: design-tool-gate.sh [profile] (default profile: design)
# ============================================================
set -euo pipefail
@@ -112,30 +112,38 @@ while IFS=$'\t' read -r type name; do
esac
done <<< "$plain"
# Verdict. Either class trips the gate.
trip=0
if [ "${#blocking[@]}" -gt 0 ] || [ "${#manual[@]}" -gt 0 ]; then trip=1; fi
if [ "$trip" -eq 0 ]; then
echo "design toolchain: READY — profile '$PROFILE' design tools active"
if [ "${#unverified[@]}" -gt 0 ]; then
echo " note: could not verify (claude CLI absent): ${unverified[*]}"
# Verdict — three outcomes:
# blocking/manual non-empty -> INCOMPLETE (exit 10): the gate trips.
# only unverified non-empty -> READY BUT UNVERIFIED (exit 11): fail-VISIBLE.
# claude was unreachable, so the plugin/MCP (magic, ui-ux-pro-max) could
# not be checked. Never pass this as a silent READY — proceed, but say so.
# nothing pending -> READY (exit 0).
if [ "${#blocking[@]}" -gt 0 ] || [ "${#manual[@]}" -gt 0 ]; then
echo "design toolchain: INCOMPLETE"
if [ "${#blocking[@]}" -gt 0 ]; then
echo " activate with /profile $PROFILE: ${blocking[*]}"
fi
exit 0
if [ "${#manual[@]}" -gt 0 ]; then
echo " required + manual step (API key / external install): ${manual[*]}"
case " ${manual[*]} " in
*" magic "*) echo " magic needs MAGIC_API_KEY in ~/.claude/.env (/profile $PROFILE runs toggle-external.sh)" ;;
esac
fi
if [ "${#unverified[@]}" -gt 0 ]; then
echo " also unverified (claude CLI unreachable): ${unverified[*]}"
fi
echo " → run: /profile $PROFILE"
exit 10
fi
echo "design toolchain: INCOMPLETE"
if [ "${#blocking[@]}" -gt 0 ]; then
echo " activate with /profile $PROFILE: ${blocking[*]}"
fi
if [ "${#manual[@]}" -gt 0 ]; then
echo " required + manual step (API key / external install): ${manual[*]}"
case " ${manual[*]} " in
*" magic "*) echo " magic needs MAGIC_API_KEY in ~/.claude/.env (/profile $PROFILE runs toggle-external.sh)" ;;
esac
fi
if [ "${#unverified[@]}" -gt 0 ]; then
echo " unverified (claude CLI absent): ${unverified[*]}"
echo "design toolchain: READY BUT UNVERIFIED — ${#unverified[@]} tool(s) not checked"
echo " unverified (claude CLI unreachable): ${unverified[*]}"
echo " the gate could NOT confirm the design plugin/MCP (e.g. magic,"
echo " ui-ux-pro-max) are active. Proceed only after checking manually:"
echo " claude mcp list claude plugin list"
exit 11
fi
echo " → run: /profile $PROFILE"
exit 10
echo "design toolchain: READY — profile '$PROFILE' design tools active"
exit 0