docs(index): add dream + experiment-spec rows, and gate the index
Both skills shipped without an index row. The rows existed — on the feature branches deleted in #22 — and were dropped by the squash-merge that followed a directory reorg: the SKILL.md landed under skills/, the row did not. Nothing errored, the index just under-reported the library to every harness reading it. Adding two rows by hand fixes today and not tomorrow, so the assertion goes in `task check` via bin/check-index, following the same pattern as bin/should-skip-release: a real script with a real test, not untested logic buried in a Taskfile. It checks both directions — a skill with no row, and a row naming a skill that no longer exists. Verified by mutation: deleting the `dream` row turns `task check` red and the report names it. Closes #15 Bump-Type: patch
This commit is contained in:
@@ -19,9 +19,11 @@ This index lists all available engineering skills. Load the full SKILL.md on dem
|
|||||||
| `gitea-ci` | Gitea CI/CD pipeline with act_runner, buildah, k3s, GitHub mirror | Setting up or debugging CI on this infra |
|
| `gitea-ci` | Gitea CI/CD pipeline with act_runner, buildah, k3s, GitHub mirror | Setting up or debugging CI on this infra |
|
||||||
| `debug` | Hypothesis-first debugging discipline | Any test failure or bug whose cause is not immediately obvious |
|
| `debug` | Hypothesis-first debugging discipline | Any test failure or bug whose cause is not immediately obvious |
|
||||||
| `feature-spec` | Implementation-level feature spec (tighter than spec-driven-dev) | Scoping one feature inside an already-specced project |
|
| `feature-spec` | Implementation-level feature spec (tighter than spec-driven-dev) | Scoping one feature inside an already-specced project |
|
||||||
|
| `experiment-spec` | Rigorous experiment spec — falsifiable hypothesis, quantitative acceptance, null-result protocol | Scoping a research/ML phase instead of a feature |
|
||||||
| `playwright` | Playwright E2E testing — setup, HTMX patterns, SSE streams, CI wiring | Adding browser tests to any project with a web UI |
|
| `playwright` | Playwright E2E testing — setup, HTMX patterns, SSE streams, CI wiring | Adding browser tests to any project with a web UI |
|
||||||
| `session-retrospective` | Surface non-obvious learnings from a session log | After a coding session ends, before context is lost |
|
| `session-retrospective` | Surface non-obvious learnings from a session log | After a coding session ends, before context is lost |
|
||||||
| `trainer` | Two-phase brain curation (score candidates, write past quality gate) | Periodic brain pruning and curation |
|
| `trainer` | Two-phase brain curation (score candidates, write past quality gate) | Periodic brain pruning and curation |
|
||||||
|
| `dream` | Reflective consolidation pass over an agent's memory directory | Pruning/merging memory files, rebuilding a memory index |
|
||||||
| `grill-me` | Structured plan interrogation — Quick Poke, Full Grill, Pre-mortem | Stress-testing a plan before committing; end of Diamond 1; before promoting to pre-prod |
|
| `grill-me` | Structured plan interrogation — Quick Poke, Full Grill, Pre-mortem | Stress-testing a plan before committing; end of Diamond 1; before promoting to pre-prod |
|
||||||
| `own-the-task` | Delegation ownership — Intake → Brief → Plan → Execute | A task is handed over open-ended; "take this off my plate"; before a plan exists |
|
| `own-the-task` | Delegation ownership — Intake → Brief → Plan → Execute | A task is handed over open-ended; "take this off my plate"; before a plan exists |
|
||||||
| `telos-load` | Load TELOS intention substrate at session start | Starting any koala session; before architectural decisions; CAD pipeline entry |
|
| `telos-load` | Load TELOS intention substrate at session start | Starting any koala session; before architectural decisions; CAD pipeline entry |
|
||||||
|
|||||||
+6
-1
@@ -94,7 +94,12 @@ tasks:
|
|||||||
count=$((count + 1))
|
count=$((count + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
# 3. Plugin manifest version must match the latest tag, or installers
|
# 3. SKILLS_INDEX.md lists exactly the skills that exist. Delegated
|
||||||
|
# to bin/check-index so the rule is covered by
|
||||||
|
# tests/check_index_test.sh rather than living untested here.
|
||||||
|
sh '{{.TASKFILE_DIR}}/bin/check-index' '{{.TASKFILE_DIR}}' || fail=1
|
||||||
|
|
||||||
|
# 4. Plugin manifest version must match the latest tag, or installers
|
||||||
# never see the update (Claude Code gates updates on this field).
|
# never see the update (Claude Code gates updates on this field).
|
||||||
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
if [ -f '{{.PLUGIN_MANIFEST}}' ]; then
|
||||||
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1)
|
||||||
|
|||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# check-index — asserts SKILLS_INDEX.md lists exactly the skills that exist.
|
||||||
|
#
|
||||||
|
# The index is hand-maintained and drifts silently in both directions: a skill
|
||||||
|
# ships with no row (`dream` and `experiment-spec` did — their rows lived on
|
||||||
|
# branches squash-merged after a directory reorg, so the SKILL.md landed and
|
||||||
|
# the row did not, #15), or a row outlives the skill it names. Neither errors
|
||||||
|
# anywhere; the library just misreports itself to every harness that reads it.
|
||||||
|
#
|
||||||
|
# Usage: check-index [checkout-root] (defaults to this script's repo root)
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT=${1:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
|
||||||
|
INDEX="$ROOT/SKILLS_INDEX.md"
|
||||||
|
fail=0
|
||||||
|
|
||||||
|
if [ ! -f "$INDEX" ]; then
|
||||||
|
echo "ERROR: missing SKILLS_INDEX.md" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A row is a table line whose first cell is the skill name in backticks.
|
||||||
|
listed=$(sed -n 's/^|[[:space:]]*`\([^`]*\)`.*/\1/p' "$INDEX" | sort -u)
|
||||||
|
|
||||||
|
for dir in "$ROOT"/skills/*/; do
|
||||||
|
name=$(basename "$dir")
|
||||||
|
[ -f "$dir/SKILL.md" ] || continue
|
||||||
|
printf '%s\n' "$listed" | grep -qx "$name" || {
|
||||||
|
echo "ERROR: skills/$name has no row in SKILLS_INDEX.md" >&2
|
||||||
|
fail=1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
for name in $listed; do
|
||||||
|
[ -f "$ROOT/skills/$name/SKILL.md" ] || {
|
||||||
|
echo "ERROR: SKILLS_INDEX.md lists '$name', which has no skills/$name/SKILL.md" >&2
|
||||||
|
fail=1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $fail
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Tests for bin/check-index.
|
||||||
|
#
|
||||||
|
# The regression these exist for: SKILLS_INDEX.md is maintained by hand, and
|
||||||
|
# two skills (`dream`, `experiment-spec`) shipped without a row — the index
|
||||||
|
# rows lived on feature branches that were squash-merged after a directory
|
||||||
|
# reorg, so the SKILL.md landed and the row did not (#15, #22). Nothing
|
||||||
|
# errored; the index just quietly under-reported the library.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
|
SUT="$ROOT/bin/check-index"
|
||||||
|
|
||||||
|
pass=0
|
||||||
|
fail=0
|
||||||
|
WORK=${TMPDIR:-/tmp}/check_index_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
|
||||||
|
|
||||||
|
# new_fixture <name> — a checkout with skills/alpha and skills/beta.
|
||||||
|
new_fixture() {
|
||||||
|
_f="$WORK/$1"
|
||||||
|
mkdir -p "$_f/skills/alpha" "$_f/skills/beta"
|
||||||
|
printf -- '---\nname: alpha\ndescription: a\n---\n' > "$_f/skills/alpha/SKILL.md"
|
||||||
|
printf -- '---\nname: beta\ndescription: b\n---\n' > "$_f/skills/beta/SKILL.md"
|
||||||
|
printf '%s' "$_f"
|
||||||
|
}
|
||||||
|
|
||||||
|
index() {
|
||||||
|
_dir=$1; shift
|
||||||
|
printf '# Engineering Skills Index\n\n| Skill | Description | Use when |\n|---|---|---|\n' > "$_dir/SKILLS_INDEX.md"
|
||||||
|
for _n in "$@"; do
|
||||||
|
printf '| `%s` | desc | when |\n' "$_n" >> "$_dir/SKILLS_INDEX.md"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# expect <0|1> <label> <fixture> [grep-for]
|
||||||
|
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, but report never names %s)\n%s\n' "$_label" "$_needle" "$_out"; return
|
||||||
|
fi
|
||||||
|
pass=$((pass + 1)); printf ' ok %s\n' "$_label"
|
||||||
|
}
|
||||||
|
|
||||||
|
fx=$(new_fixture complete); index "$fx" alpha beta
|
||||||
|
expect 0 'every skill has a row' "$fx"
|
||||||
|
|
||||||
|
fx=$(new_fixture missing); index "$fx" alpha
|
||||||
|
expect 1 'a skill with no row is reported' "$fx" beta
|
||||||
|
|
||||||
|
fx=$(new_fixture stale); index "$fx" alpha beta gamma
|
||||||
|
expect 1 'a row for a skill that does not exist is reported' "$fx" gamma
|
||||||
|
|
||||||
|
fx=$(new_fixture noindex)
|
||||||
|
expect 1 'a missing index is reported' "$fx" SKILLS_INDEX.md
|
||||||
|
|
||||||
|
printf '\ncheck_index: %d passed, %d failed\n' "$pass" "$fail"
|
||||||
|
[ "$fail" -eq 0 ]
|
||||||
Reference in New Issue
Block a user