feat(check): every skill states when it stops and hands back
Implements #5's decision. The hard part was #37's, not #5's: which skills need an escalation rule? A hardcoded list rots, sniffing the prose for "does this write anything" is unfalsifiable, and a frontmatter flag is the option #5 rejected wearing a hat. So there is no acting set. Every skill answers the question, and a read-only one answers it in a line. That turns an absence into a claim someone can disagree with, which is the whole point — "this skill never hands back" is a design decision worth stating, and today it was indistinguishable from nobody having thought about it. Eleven written here, all advisory or read-only, where the honest answer is short and the value is in being specific rather than generic. `cognitive-load` stops when the metric disagrees with the people reading the code, because that is evidence about the metric. `test-design` stops when the tests are fine and the design under test is what makes them awkward. `code-review` stops when the diff is big enough that a review would be a skim, and says so rather than producing confident coverage of a fraction. The remaining thirty are the acting set — writes, dispatches, credentials, releases — and each needs a rule someone thought about rather than a rote paragraph, so they sit in bin/escalation-pending.txt. That list only shrinks, and it is self-cleaning: a listed skill that HAS a section fails the gate, so an exemption cannot outlive its reason. Mutation-verified both ways. Six test cases including the stale-entry case, because an exemption for a skill that no longer exists is how a gate quietly stops covering something. Refs #37, closes the mechanism half Bump-Type: minor
This commit is contained in:
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
# check-escalation — every skill states when it stops and hands back.
|
||||
#
|
||||
# #5 decided this belongs in the body, not the frontmatter: no harness reads
|
||||
# frontmatter beyond name/description, so a `failure_criteria:` key would be a
|
||||
# guarantee nothing honours. The model reads the body.
|
||||
#
|
||||
# #37 then had to define which skills need one, and every obvious answer was
|
||||
# bad: a hardcoded list rots, sniffing the text for "does it write anything" is
|
||||
# unfalsifiable, and a frontmatter flag is the rejected option wearing a hat.
|
||||
#
|
||||
# So there is no acting set. EVERY skill answers the question. A read-only skill
|
||||
# answers it in one line — "read-only; findings are the output, nothing is
|
||||
# handed back" — which turns an absence into a claim someone can disagree with.
|
||||
#
|
||||
# bin/escalation-pending.txt is a ratchet for skills whose real rule has not
|
||||
# been written yet (a stop condition nobody thought about is not worth
|
||||
# enforcing). It is self-cleaning: a listed skill that HAS a section fails, so
|
||||
# the list cannot outlive its reason.
|
||||
#
|
||||
# Usage: check-escalation [checkout-root]
|
||||
|
||||
set -u
|
||||
|
||||
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
|
||||
PENDING="$ROOT/bin/escalation-pending.txt"
|
||||
fail=0
|
||||
|
||||
listed() {
|
||||
[ -f "$PENDING" ] || return 1
|
||||
sed 's/#.*//' "$PENDING" | grep -qx "[[:space:]]*$1[[:space:]]*"
|
||||
}
|
||||
|
||||
for dir in "$ROOT"/skills/*/; do
|
||||
[ -f "$dir/SKILL.md" ] || continue
|
||||
name=$(basename "$dir")
|
||||
|
||||
if grep -q '^## Escalation' "$dir/SKILL.md"; then
|
||||
if listed "$name"; then
|
||||
echo "ERROR: skills/$name has an Escalation section but is still in bin/escalation-pending.txt — remove it from the list" >&2
|
||||
fail=1
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
listed "$name" && continue
|
||||
echo "ERROR: skills/$name has no '## Escalation' section — say when it stops and hands back, or say it never does" >&2
|
||||
fail=1
|
||||
done
|
||||
|
||||
# A pending entry for a skill that no longer exists is a stale exemption, and a
|
||||
# stale exemption is how a gate quietly stops covering something. Looped without
|
||||
# a pipe on purpose: a `while read` on the right of a pipe runs in a subshell,
|
||||
# so `fail=1` would be set in a process that exits before it is read.
|
||||
if [ -f "$PENDING" ]; then
|
||||
entries=$(sed 's/#.*//' "$PENDING" | tr -d '[:blank:]' | grep -v '^$' || true)
|
||||
for entry in $entries; do
|
||||
[ -f "$ROOT/skills/$entry/SKILL.md" ] && continue
|
||||
echo "ERROR: bin/escalation-pending.txt lists '$entry', which has no skills/$entry/SKILL.md" >&2
|
||||
fail=1
|
||||
done
|
||||
fi
|
||||
|
||||
exit $fail
|
||||
@@ -0,0 +1,42 @@
|
||||
# Skills whose escalation rule has not been written yet (#37).
|
||||
#
|
||||
# This list only shrinks. A skill here is exempt from the '## Escalation'
|
||||
# requirement until someone writes its real stop conditions; a skill that HAS a
|
||||
# section and is still listed fails the gate, so the list cannot outlive its
|
||||
# reason.
|
||||
#
|
||||
# The ones already done were the advisory and read-only skills, where the honest
|
||||
# answer is a line. What is left is the acting set — skills that write files,
|
||||
# dispatch agents, touch credentials or move releases — and each of those needs
|
||||
# a rule someone thought about, not a rote paragraph. Per #19, one at a time.
|
||||
|
||||
atdd
|
||||
dream
|
||||
experiment-spec
|
||||
feature-spec
|
||||
gitea-ci
|
||||
own-the-task
|
||||
planning
|
||||
playwright
|
||||
problem-analysis
|
||||
refactoring
|
||||
regulatory-risk-assessment
|
||||
secret-hygiene
|
||||
session-retrospective
|
||||
spec-driven-dev
|
||||
superpowers-brainstorming
|
||||
superpowers-dispatching-parallel-agents
|
||||
superpowers-executing-plans
|
||||
superpowers-finishing-a-development-branch
|
||||
superpowers-receiving-code-review
|
||||
superpowers-requesting-code-review
|
||||
superpowers-subagent-driven-development
|
||||
superpowers-using-git-worktrees
|
||||
superpowers-using-superpowers
|
||||
superpowers-verification-before-completion
|
||||
superpowers-writing-plans
|
||||
superpowers-writing-skills
|
||||
tdd
|
||||
trainer
|
||||
user-stories
|
||||
web-shot
|
||||
Reference in New Issue
Block a user