From 83b069afba857be524322222dc836c50a8e40d79 Mon Sep 17 00:00:00 2001 From: Mathias Date: Sat, 29 Aug 2026 09:51:59 +0200 Subject: [PATCH] docs(index): add dream + experiment-spec rows, and gate the index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- SKILLS_INDEX.md | 2 ++ Taskfile.yml | 7 +++- bin/check-index | 42 +++++++++++++++++++++++ tests/check_index_test.sh | 70 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100755 bin/check-index create mode 100644 tests/check_index_test.sh diff --git a/SKILLS_INDEX.md b/SKILLS_INDEX.md index 031bf2c..a3a396a 100644 --- a/SKILLS_INDEX.md +++ b/SKILLS_INDEX.md @@ -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 | | `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 | +| `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 | | `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 | +| `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 | | `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 | diff --git a/Taskfile.yml b/Taskfile.yml index e61cfea..23382ac 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -94,7 +94,12 @@ tasks: count=$((count + 1)) 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). if [ -f '{{.PLUGIN_MANIFEST}}' ]; then manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1) diff --git a/bin/check-index b/bin/check-index new file mode 100755 index 0000000..53eafde --- /dev/null +++ b/bin/check-index @@ -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 diff --git a/tests/check_index_test.sh b/tests/check_index_test.sh new file mode 100644 index 0000000..511f384 --- /dev/null +++ b/tests/check_index_test.sh @@ -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 — 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>