Skills move to skills/<name>/SKILL.md and the repo gains both manifests, so it is now a marketplace hosting exactly one plugin whose root is the repo root (source: "."). Claude Code installs it from git; no symlinks involved. Why: install.sh wired the library into every consuming repo as an ABSOLUTE symlink to $CHECKOUT_DIR. The checkout moved three times, most recently when local-dev@1966d5a dropped the embedded .skills/ copy, and nothing migrated the links — 243 dangling links across 12 repos, hidden from git by the **/.claude/skills/ ignore rule. Worse, ~/dev spans two path roots (/Users/mathias on the iCloud-shared Macs, /home/mathias on koala), so an absolute link stored there is broken on one side by construction. Re-running the installer cannot fix that; only removing the mechanism can. install.sh keeps the harnesses with no plugin system and drops from 42 links per harness to one directory link, via the convergent ~/.agents/skills convention that Pi and OpenCode both read: ~/.agents/skills -> $CHECKOUT_DIR/skills ~/.config/crush/skills -> ~/.agents/skills ~/.config/opencode/skills -> ~/.agents/skills wire_claude_repo is deleted outright rather than gated behind a flag — on a mixed-root fleet it cannot be made correct. Antigravity and Vibe targets go too; both unused. CHECKOUT_DIR keeps its host-local default deliberately: pointing it into ~/dev would put a git working tree inside iCloud with two concurrent writers. Taskfile drops five near-identical wiring loops (they had already drifted) and delegates to install.sh. `task check` gains frontmatter validation, a stray-root-entry gate, and manifest/tag version parity — the loose secret-hygiene.md sat unloadable for months because nothing checked layout. Verified: claude plugin validate . passes clean; a probe skill loaded as skills:zzprobe through --plugin-dir; task check green at 42 skills. Refs: #23 Bump-Type: minor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
8.0 KiB
name, description
| name | description |
|---|---|
| feature-spec | Write a tight implementation-level spec for a single feature or component before coding it. Use after a project-level spec exists (see spec-driven-dev) and you need to nail down one feature inside it. |
Feature Spec
Overview
A feature spec is the implementation-level contract for one feature or component, written by an engineer before code. It is shorter and more focused than a project-level spec (spec-driven-dev) — it assumes the project context, the stack, and the user are already known. Its job is to force one decision: what does done look like, and what is explicitly not in this feature.
Core principle: If you cannot write three measurable success criteria, you do not understand the feature well enough to build it.
When to Use
- About to start implementing a feature inside an existing, already-specced project
- A change that touches more than one file or one package
- A change that introduces a new public API, schema, or external contract
- Any time the requirements feel "obvious" — that feeling usually means assumptions are stacking silently
When NOT to use:
- Project kickoff or first-time architecture decisions — use
spec-driven-devinstead, which covers the broader lifecycle (Objective, Tech Stack, Project Structure, Boundaries, etc.) - Single-line fixes, typos, dependency bumps
- Spike or throwaway prototypes (note them as such; no spec needed)
Iron Laws
- Success criteria must be measurable. "The system is fast" is banned. "p99 < 200ms under 100 RPS" is valid. If you cannot state a measurement, the criterion is not a criterion.
- An "Out of scope" section is mandatory. If you do not draw the boundary, the implementer (you, future-you, or another agent) will guess wrong.
- Every technical decision in "Technical approach" must have a rationale. A decision is any choice where a reasonable alternative existed and was not taken. "Use Postgres" is not a decision; "use Postgres because the rest of the stack uses it and we need transactions across user + invoice" is.
Spec Template
# Spec: [Feature Name]
## Problem Statement
What problem does this feature solve? For whom? Why now?
(2-4 sentences. If you cannot answer "why now," ask before writing the rest.)
## Success Criteria
- [ ] Criterion 1 — measurable and verifiable
- [ ] Criterion 2 — measurable and verifiable
- [ ] Criterion 3 — measurable and verifiable
## Constraints
Non-negotiable requirements the solution must satisfy.
Examples: "must not change the public API", "must complete in < 500ms",
"must not require a database migration", "must work offline".
## Out of Scope
What this feature explicitly does NOT do, even if related.
Anything plausibly in scope that you decided to defer goes here.
## Technical Approach
The architecture or design decisions, with rationale for each.
Each bullet should answer "why this choice over the obvious alternative."
## Risks
What could go wrong, and how it would be mitigated or detected.
At least one risk must be listed; "no risks" usually means insufficient analysis.
Worked Example
# Spec: IBAN extraction from Swedish PDF invoices
## Problem Statement
Invoice processors at small accounting firms manually copy IBANs from PDF
invoices into the banking system. The current parser handles ~60% of formats;
40% require manual fallback, defeating the automation premise.
Why now: the pilot customer is onboarding three new firms next month, all of
which use formats currently in the 40%.
## Success Criteria
- [ ] IBAN extracted correctly from ≥ 95% of a 200-invoice test corpus
- [ ] Zero false positives (extracting a non-IBAN string as an IBAN) on the corpus
- [ ] p99 extraction time < 500ms per invoice
- [ ] Returns a structured error (not a panic) for unrecognized formats
## Constraints
- Must not require a new dependency (PDF library is already chosen)
- Must work without network access (no external IBAN validation API)
- Must be callable from the existing `Parser.Extract(ctx, r)` signature
## Out of Scope
- Non-Swedish IBAN formats (separate spec when needed)
- BIC/SWIFT extraction (handled by a separate parser)
- OCR — input is assumed to be text-extractable PDF, not scanned image
- Validating that the IBAN refers to a real account
## Technical Approach
- Layered regex-then-checksum approach: regex narrows candidates, MOD-97
checksum validates. Rationale: pure regex is too permissive; pure
checksum requires too many candidates to test.
- Use the existing `pdfplumber` text layer rather than re-extracting.
Rationale: extraction is already the slow path; reusing the layer
avoids doubling extraction time.
- Return `(iban string, ok bool, err error)` not `(*IBAN, error)`.
Rationale: the caller almost always wants to know "did we find one"
separately from "did extraction itself fail," and a pointer return
invites nil-deref bugs.
## Risks
- Regex tuned to current corpus may overfit; mitigation: corpus is held out
per-firm, so we will see degradation at the next firm onboarding before
it reaches production.
- MOD-97 implementation bugs are subtle; mitigation: cross-check against
the Wikipedia reference implementation in tests.
Common Rationalizations
| Rationalization | Reality |
|---|---|
| "It's a small feature, I don't need a spec" | Small features still need a measurable definition of done. The spec stays small too. |
| "I'll write the spec after, when I know more" | Then it is documentation, not a spec. The point is forcing clarity before the code locks in your assumptions. |
| "The success criteria are obvious" | If they are obvious, they are easy to write down. If they are hard to write down, they are not obvious. |
| "I don't have time to enumerate out-of-scope items" | You will spend more time arguing over scope creep at review than writing this section now. |
Brain MCP Integration
The brain holds prior feature specs across the project. Use it to keep constraints and out-of-scope decisions coherent across related features.
At spec start:
- Run
brain_queryfor prior specs in the same area or for the same problem domain. Reusing the same constraints and out-of-scope decisions across related features keeps the system coherent.
After spec is approved:
- Run
brain_writewith the spec's success criteria and out-of-scope list. Future related specs can cross-reference rather than re-derive.
Logging
Call session_log once at the end of every phase to record the outcome.
Pass-rate is computed downstream by the /pass-rate HTTP endpoint, which
treats pass as success, fail as failure, skip as neither.
At end of each phase:
session_logwith{skill: "feature-spec", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}
Phases for this skill: draft
Status semantics:
pass— the phase's intended outcome was reached (spec drafted and approved).fail— the phase's intended outcome was NOT reached (spec rejected, blocked on unresolved questions).skip— phase was skipped intentionally.
Why this matters: the routing pod (Plan 6) reads pass-rate to decide whether to route a future call to a local model. If your skill never logs, the routing pod sees no data.
Mode 2 Routing Note
Spec writing requires interpretation of intent and tradeoff reasoning — work where Claude generally outperforms a 9-13B local model. This skill is intentionally not a Mode 2 routing candidate. In Mode 2, the routing layer should leave feature-spec calls on Claude.
Cross-References
- Load
spec-driven-devinstead if this is the first spec for a project (broader template, includes tech stack, project structure, boundaries). - Load
problem-analysisskill before this if the problem statement itself feels fuzzy. - Load
tddskill once the spec is approved — each success criterion becomes a test. - Load
planningskill if the spec implies more than ~5 implementation tasks.