Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d140ec525 | ||
|
|
2fcc15e9e4 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "skills",
|
||||
"description": "Mathias's engineering skills library, plus the hooks and MCP servers that make it enforceable: TDD, clean code, debugging, specs, discovery, CI, and secret hygiene.",
|
||||
"version": "0.13.4",
|
||||
"version": "0.14.0",
|
||||
"author": {
|
||||
"name": "Mathias",
|
||||
"url": "https://git.d-ma.be/mathias"
|
||||
|
||||
+6
-1
@@ -114,7 +114,12 @@ tasks:
|
||||
# session_id is its only required field, and nine skills omitted it.
|
||||
sh '{{.TASKFILE_DIR}}/bin/check-session-log' '{{.TASKFILE_DIR}}' || fail=1
|
||||
|
||||
# 6. Plugin manifest version must match the latest tag, or installers
|
||||
# 6. Every skill says when it stops and hands back (#5). Skills whose
|
||||
# rule is not written yet are listed in bin/escalation-pending.txt,
|
||||
# which only shrinks.
|
||||
sh '{{.TASKFILE_DIR}}/bin/check-escalation' '{{.TASKFILE_DIR}}' || fail=1
|
||||
|
||||
# 7. Plugin manifest version must match the latest tag, or installers
|
||||
# never see the update (Claude Code gates updates on this field).
|
||||
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
||||
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
||||
|
||||
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
|
||||
@@ -258,3 +258,13 @@ decide
|
||||
- `mathias/cad-atlas#11` renders the same `cad stuck` output as a pipeline overlay — read that
|
||||
instead of this skill's text output when a visual "where is everything stuck" view is wanted
|
||||
over working one row in depth.
|
||||
|
||||
## Escalation
|
||||
|
||||
Read-only by construction, and the Iron Laws above are the escalation rule: never close
|
||||
an issue, never dispatch, never write a label. Everything that is the operator's call
|
||||
leaves as a `DECISION NEEDED` block (question / options / recommendation / blocks).
|
||||
|
||||
Stop and hand back when: the stuck queue is empty; a diagnosis needs a write you are not
|
||||
allowed to make; or two runs in a row produce the same finding, which means the finding
|
||||
is not being acted on and repeating it is noise.
|
||||
|
||||
@@ -284,3 +284,11 @@ During refactoring:
|
||||
- `references/python-adaptation.md` — Python conventions: error chaining, dataclasses, the truthiness trap
|
||||
- Load `solid` skill for SOLID principles with Go examples
|
||||
- Load `refactoring` skill for systematic refactoring techniques
|
||||
|
||||
## Escalation
|
||||
|
||||
Advisory during the REFACTOR phase. Suggests; does not restructure behaviour.
|
||||
|
||||
Stop and hand back when: the cleanup you are proposing changes behaviour. That is not
|
||||
refactoring, it is a change, and it needs its own failing test first — hand back to
|
||||
`tdd`.
|
||||
|
||||
@@ -285,3 +285,12 @@ treats `pass` as success, `fail` as failure, `skip` as neither.
|
||||
- Load `refactoring` skill when recommending fixes
|
||||
- Load `test-design` skill for reviewing test quality
|
||||
- Load `solid` skill for SOLID principle violations
|
||||
|
||||
## Escalation
|
||||
|
||||
Read-only analysis. Findings are the output; nothing is handed back mid-review.
|
||||
|
||||
Stop and hand back when: the diff is large enough that a review would be a skim — say so
|
||||
and ask for it in parts, rather than producing confident coverage of a fraction of it; or
|
||||
the problem is structural rather than pointwise, in which case name it and hand off to
|
||||
`solid` or `refactoring` instead of filing twenty line-level comments about one cause.
|
||||
|
||||
@@ -347,3 +347,12 @@ type UserWriter interface {
|
||||
- Load `refactoring` skill for systematic techniques to reduce identified hotspots
|
||||
- Load `solid` skill for structural fixes (coupling, cohesion)
|
||||
- Load `clean-code` skill for naming improvements
|
||||
|
||||
## Escalation
|
||||
|
||||
Read-only measurement. The score is the output.
|
||||
|
||||
Stop and hand back when: the measurement disagrees with the reader's lived experience of
|
||||
the code. A metric that says "fine" about code everyone finds hard is evidence about the
|
||||
metric, not about the code, and continuing to defend the number is how a measure becomes
|
||||
a target.
|
||||
|
||||
@@ -182,3 +182,13 @@ Techniques kept in `debug/references/`, inherited from the deleted
|
||||
including as one of the fixes already tried. A bumped timeout is a symptom.
|
||||
- `find-polluter.sh` — run when a test passes alone and fails in the suite, or when
|
||||
something in the tree appears that no test admits to creating.
|
||||
|
||||
## Escalation
|
||||
|
||||
Diagnose only — Iron Law 3 is already an escalation rule: the fix is a separate TDD cycle,
|
||||
not this skill's output.
|
||||
|
||||
Stop and hand back when: the report contains no failure text and no locator, in which case
|
||||
name the specific artifact you need (run id, job log, stack trace) and stop rather than
|
||||
hypothesising into a vacuum; or the evidence needed to discriminate between your top two
|
||||
hypotheses requires access you do not have.
|
||||
|
||||
@@ -152,3 +152,12 @@ hand-wavy. Crisp solution + fuzzy problem = reverse-engineered justification. St
|
||||
|
||||
> Named instances of both war stories are client-tagged and held out of this public skill
|
||||
> pending client-store decision. The transferable lesson lives here; the wrapper stays out.
|
||||
|
||||
## Escalation
|
||||
|
||||
Advisory. Produces a problem brief; commits to nothing.
|
||||
|
||||
Stop and hand back when: the evidence needed to size a problem requires talking to
|
||||
customers you cannot reach. Sizing a problem from assumptions and presenting it as
|
||||
discovery is worse than saying the number is unknown — it launders a guess into a brief
|
||||
that later decisions will cite.
|
||||
|
||||
@@ -148,3 +148,12 @@ This skill maps to the **convergence moments** in the Double Diamond workflow:
|
||||
- **Mid-Diamond 2 (Develop):** Use Quick Poke when an agent's experiment result is ambiguous and you're deciding whether to continue or pivot.
|
||||
|
||||
The skill does NOT replace human judgment at convergence points — it sharpens the plan so that judgment is better informed.
|
||||
|
||||
## Escalation
|
||||
|
||||
Advisory. Produces sharper questions and a better plan; decides nothing.
|
||||
|
||||
Stop and hand back when: the plan's weak point is a decision that is the operator's to
|
||||
make rather than a gap in reasoning — surface it as a question with options and a
|
||||
recommendation, and do not grill past it. Also stop when a round produces no new
|
||||
objection: interrogation past that point manufactures doubt rather than finding it.
|
||||
|
||||
@@ -261,3 +261,12 @@ Domain types know nothing about HTTP, SQL, or external APIs.
|
||||
- `references/python-adaptation.md` — the Python rewrite: Protocol over ABC, and what does not carry over from Go
|
||||
- Load `clean-code` skill for naming and structure
|
||||
- Load `code-review` skill for detecting violations during review
|
||||
|
||||
## Escalation
|
||||
|
||||
Advisory. Names principle violations and suggests structure; writes no code.
|
||||
|
||||
Stop and hand back when: applying a principle would require a change larger than the one
|
||||
under discussion. "This wants dependency inversion" on a two-line fix is a design
|
||||
conversation, not a review comment, and forcing it through as one is how a small change
|
||||
acquires an architecture.
|
||||
|
||||
@@ -151,3 +151,12 @@ stays in the client boundary**:
|
||||
- Council/forum names, internal stage labels, internal tooling, named contacts, strategy
|
||||
specifics, deal terms → client dir/org only, local models only, never the public brain.
|
||||
- This skill carries the **pattern**; the client carries the **wrapper**. Never merge them.
|
||||
|
||||
## Escalation
|
||||
|
||||
Advisory. Prepares or chairs a gate; the council decides, not this skill.
|
||||
|
||||
Stop and hand back when: a discipline's evidence is missing rather than weak. A gap on a
|
||||
gating discipline means do not bring the ask — bring a feedback-only ask instead. Marking
|
||||
it "cleared with a note" is precisely the gate theatre the anti-patterns section is
|
||||
about.
|
||||
|
||||
@@ -82,3 +82,11 @@ created in a CAD session should trace to a TELOS GOAL. Every GOAL
|
||||
traces to a PROBLEM.
|
||||
|
||||
Traceability chain: PROBLEM → GOAL → SPEC → TICKET → IMPL → TEST
|
||||
|
||||
## Escalation
|
||||
|
||||
Read-only. Loads intention context; changes nothing.
|
||||
|
||||
Stop and hand back when: the brain is unreachable or returns nothing for the wing. Working
|
||||
from a remembered TELOS is the failure this skill exists to prevent — say the load failed
|
||||
rather than reconstructing it from memory.
|
||||
|
||||
@@ -309,3 +309,11 @@ Apply this checklist to every new test:
|
||||
- Load `tdd` skill for the full TDD workflow
|
||||
- Load `code-review` skill for test quality review during pre-merge review
|
||||
- See `clean-code/references/code-smells.md` for testing-specific smells
|
||||
|
||||
## Escalation
|
||||
|
||||
Read-only scoring of existing tests.
|
||||
|
||||
Stop and hand back when: the tests are fine and the *design under test* is what makes them
|
||||
awkward. Hard-to-test is a design finding, not a test finding, and rewriting the tests
|
||||
around it hides the thing worth knowing.
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/bin/sh
|
||||
# Tests for bin/check-escalation.
|
||||
#
|
||||
# #5 asked whether SKILL.md needs first-class failure/escalation fields and
|
||||
# decided: not frontmatter (nothing reads it), a body section (the model reads
|
||||
# that). #37 then had to define WHICH skills need one, and every obvious answer
|
||||
# was bad — a hardcoded list rots, content sniffing is unfalsifiable, a
|
||||
# frontmatter flag is the rejected option through the back door.
|
||||
#
|
||||
# So there is no acting set. EVERY skill answers the question; a read-only one
|
||||
# answers it in a line. The classification problem disappears, and "this skill
|
||||
# never hands back" becomes a stated claim rather than an absence.
|
||||
#
|
||||
# bin/escalation-pending.txt is a ratchet for skills whose real rule has not
|
||||
# been written yet. It is self-cleaning: a skill that has a section AND is still
|
||||
# listed fails, so the list cannot silently outlive its reason.
|
||||
|
||||
set -u
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
SUT="$ROOT/bin/check-escalation"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
WORK=${TMPDIR:-/tmp}/check_escalation_test.$$
|
||||
|
||||
if [ ! -f "$SUT" ]; then
|
||||
printf 'FAIL: %s does not exist\n' "$SUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() { rm -rf "$WORK"; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# fixture <name> — an empty checkout with a bin/ for the pending list.
|
||||
fixture() {
|
||||
_f="$WORK/$1"
|
||||
mkdir -p "$_f/skills" "$_f/bin"
|
||||
: > "$_f/bin/escalation-pending.txt"
|
||||
printf '%s' "$_f"
|
||||
}
|
||||
|
||||
# skill <root> <name> <has-section>
|
||||
skill() {
|
||||
mkdir -p "$1/skills/$2"
|
||||
{
|
||||
printf -- '---\nname: %s\ndescription: d\n---\n\n# %s\n' "$2" "$2"
|
||||
[ "$3" = yes ] && printf '\n## Escalation\n\nStop and hand back when X. Hand back by: saying so.\n'
|
||||
} > "$1/skills/$2/SKILL.md"
|
||||
}
|
||||
|
||||
# expect <rc> <label> <fixture> [needle]
|
||||
expect() {
|
||||
_want=$1; _label=$2; _fx=$3; _needle=${4:-}
|
||||
_out=$(sh "$SUT" "$_fx" 2>&1); _rc=$?
|
||||
if [ "$_rc" -ne "$_want" ]; then
|
||||
fail=$((fail + 1)); printf ' FAIL %s (want rc=%s got %s)\n%s\n' "$_label" "$_want" "$_rc" "$_out"; return
|
||||
fi
|
||||
if [ -n "$_needle" ] && ! printf '%s' "$_out" | grep -q "$_needle"; then
|
||||
fail=$((fail + 1)); printf ' FAIL %s (rc ok, report never says %s)\n%s\n' "$_label" "$_needle" "$_out"; return
|
||||
fi
|
||||
pass=$((pass + 1)); printf ' ok %s\n' "$_label"
|
||||
}
|
||||
|
||||
f=$(fixture ok); skill "$f" alpha yes
|
||||
expect 0 'a skill with an Escalation section passes' "$f"
|
||||
|
||||
f=$(fixture missing); skill "$f" alpha no
|
||||
expect 1 'a skill with no section fails, naming it' "$f" 'alpha'
|
||||
|
||||
f=$(fixture pending); skill "$f" alpha no; printf 'alpha\n' > "$f/bin/escalation-pending.txt"
|
||||
expect 0 'a listed skill is allowed through the ratchet' "$f"
|
||||
|
||||
f=$(fixture done); skill "$f" alpha yes; printf 'alpha\n' > "$f/bin/escalation-pending.txt"
|
||||
expect 1 'a listed skill that now HAS a section fails — remove it from the list' "$f" 'escalation-pending'
|
||||
|
||||
f=$(fixture stale); skill "$f" alpha yes; printf 'ghost\n' > "$f/bin/escalation-pending.txt"
|
||||
expect 1 'a listed skill that does not exist fails' "$f" 'ghost'
|
||||
|
||||
f=$(fixture comments); skill "$f" alpha no
|
||||
printf '# a comment, and a blank line follow\n\nalpha\n' > "$f/bin/escalation-pending.txt"
|
||||
expect 0 'comments and blank lines in the list are ignored' "$f"
|
||||
|
||||
printf '\ncheck_escalation: %d passed, %d failed\n' "$pass" "$fail"
|
||||
[ "$fail" -eq 0 ]
|
||||
Reference in New Issue
Block a user