mathias 51a2d396fd
CI / Check / Test (push) Successful in 1s
CI / Tag release (push) Successful in 1s
docs(spec): EARS-lite behavioural criteria and a context snapshot
Both skills gain EARS-lite as an option alongside prose criteria: WHEN/WHILE/IF
plus the unconditional form, for behaviour that a measurable threshold cannot
express. feature-spec also gains a Context snapshot section — the executor of a
spec routinely cannot open the architecture document it points at, so the facts
it depends on get inlined with provenance.

Pressure-tested per #19, and the RED phase argued against half the issue. A
subagent with no skill wrote a strong spec and then explained that it had
deliberately NOT inlined architecture context, "because both are owned by the
existing architecture document and duplicating them here would create two
drifting sources of truth". That objection is correct, so the section is built
around answering it: snapshot what the implementer cannot look up, always cite
source and version, and state that the source wins on disagreement. Prose
criteria came out fine unaided; EARS phrasing did not appear at all, which is
where the real gap was.

The GREEN run then found seven defects in the first draft, all fixed here:

- The example was in the same domain as the test scenario, so it got copied
  rather than read — the spec inherited an extraction-confidence requirement
  its feature request never mentioned. The example is now deliberately
  off-domain, with that failure written up as the reason.
- EARS-lite had no unconditional form, so a genuine invariant ("no instruction
  above threshold ever reaches a file") had to be bent into a WHILE that a
  system trivially satisfies by never entering the state. Added.
- "Maps 1:1 onto a test" was false for the WHILE form, which is universally
  quantified. Softened to say which forms map to one test and which do not.
- The provenance rule had no answer for the case it was written for: an author
  who also cannot read the source. Now: mark the block UNVERIFIED in those
  words and make confirmation the implementer's first task.
- "Snapshot only what this spec's decisions rest on" was narrower than the
  section's own purpose; a table name underpins no decision and still blocks
  the migration. Widened, with the omissions stated deliberately.
- "Say so in the section itself" did not say what, at what granularity. Now one
  opening line, not a per-fact annotation.
- The template had no slot for EARS lines, so their placement was a guess.
  Added as a subsection with a note that they are properties, not checkboxes.

Closes #12
Bump-Type: minor
2026-08-29 11:40:42 +02:00

skills

Engineering skills library — markdown SKILL.md files that plug into any AI-coding harness.

For Claude Code this repo is a plugin, installed from the marketplace it also hosts; that path carries the hooks and MCP servers too. For Crush, Pi and OpenCode it is a plain library wired by install.sh as one directory symlink per harness. See SKILLS_INDEX.md.

What is a skill?

A skill is a single directory containing one or more markdown files. The canonical entrypoint is <name>/SKILL.md. Additional reference material lives alongside in the same directory (e.g. clean-code/references/code-smells.md). Harnesses load skills on demand — keep SKILL.md short and triggerable; push depth into references.

See SKILLS_INDEX.md for the full catalogue with descriptions and "use when" triggers.

Install

Claude Code — the plugin

/plugin marketplace add https://git.d-ma.be/mathias/skills.git
/plugin install skills@mathias

The plugin ships skills, the secret-guard + pre-task-ritual hooks, and the brain/gitea/infra MCP servers as one versioned unit. Skills are namespaced (skills:tdd). Updates land when .claude-plugin/plugin.json's version moves — CI bumps it on every release.

Crush, Pi, OpenCode — install.sh

curl -fsSL https://git.d-ma.be/mathias/skills/raw/branch/main/install.sh | bash

Idempotent. It clones or fast-forwards the checkout, then populates one managed directory and gives each harness a single link to it:

Path Target
~/.agents/skills/ a real directory, one symlink per skill (Pi reads this path directly)
~/.agents/skills/<skill> $SKILLS_CHECKOUT_DIR/skills/<skill>, or another configured source
~/.config/crush/skills ~/.agents/skills
~/.config/opencode/skills ~/.agents/skills

Total links are N + one per harness — not N x harnesses, which is what #23 was actually about.

More than one source of skills

~/.agents/skills used to be a single link straight at this checkout. That encoded an assumption nobody had stated — that this checkout is the only source of skills on the host — and on flamingo the only way to install this library was to displace Anthropic's official set, which lived in Crush's directory. Real capability loss dressed up as a tidy-up (#27).

Point SKILLS_EXTRA_SOURCES at any number of extra directories, colon separated, each holding <skill>/SKILL.md:

SKILLS_EXTRA_SOURCES=~/.config/crush/skills.anthropic bash install.sh
  • The checkout is always first and always wins a name collision — but the collision is logged, never resolved in silence. Quietly picking one is how stale content wins.
  • Removing a skill from a source removes its link on the next run.
  • Anything in the managed directory that is not a symlink is reported and left alone. install.sh does not delete content it did not create.
  • The source list is recorded at ~/.agents/skills-sources so bin/doctor can tell a link into a known source from a link into a forgotten one.

Where the checkout must live

SKILLS_CHECKOUT_DIR defaults to ~/.local/share/skills, and that default is load-bearing: it is host-local, outside any synced or shared directory.

On this fleet ~/dev is an iCloud folder shared by flamingo and iguana, and it spans two path roots — /Users/mathias on the Macs, /home/mathias on koala. A checkout there is a git working tree with two concurrent writers, and any absolute symlink stored there is broken on one side by construction. That is what produced 243 dangling links across 12 repos (#23). Do not "fix" the default to point into ~/dev.

Per-host env-var overrides

Env var Default
SKILLS_REPO_URL https://git.d-ma.be/mathias/skills.git
SKILLS_REF main (set to e.g. v0.4.0 to pin a release)
SKILLS_CHECKOUT_DIR $HOME/.local/share/skills
SKILLS_SKIP_FETCH unset (1 wires an existing checkout without touching HEAD)
AGENTS_SKILLS_DIR $HOME/.agents/skills
CRUSH_SKILLS_DIR $HOME/.config/crush/skills
OPENCODE_SKILLS_DIR $HOME/.config/opencode/skills
SKILLS_EXTRA_SOURCES unset (colon-separated extra source directories)

Antigravity and Mistral Vibe targets were removed — unused.

Checking a host

task doctor      # or bin/doctor

Reports dangling links, links resolving into a foreign checkout, skill-count mismatches, stray entries, and leftover per-skill Claude symlinks. Non-zero exit on drift. It also runs at session start via the plugin, so a drifting host says so rather than waiting to be discovered.

Versioning

Every commit to main is auto-tagged as a semver release by the .gitea/workflows/release.yml action. Bump direction follows the commit footer:

  • Bump-Type: major → X.0.0
  • Bump-Type: minor → 0.Y.0
  • anything else → 0.0.Z (patch, the default)

Consumer projects can pin a version by setting SKILLS_REF=v0.1.0 before task skills:install; default is main for always-current.

Contributing a skill

  1. Add a directory <short-kebab-name>/.
  2. Create <short-kebab-name>/SKILL.md — start with one-paragraph purpose, then triggers ("use when …"), then mechanics.
  3. Add a row to SKILLS_INDEX.md (description + use-when).
  4. Commit directly to main. Conventional-commit prefix (feat: / fix: / docs:). Add Bump-Type: minor footer when adding a new skill or expanding scope; default patch covers edits.

This repo is markdown-only and public — never include secrets, client names, or anything under NDA in a skill.

S
Description
Engineering skills library — markdown SKILL.md files installed into any project or harness via task skills:install
Readme
853 KiB
Languages
Shell 84%
JavaScript 9.5%
HTML 3.9%
TypeScript 2.6%