Phase 1 of mathias/skills extraction (infra#62 Track D — homelab next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill dirs + SKILLS_INDEX.md) and adds the installation surface: - Taskfile.yml — install / update / list / release / check targets - install.sh — bootstrap installer for hosts without Task. Idempotent symlink wirer; default checkout at ~/.local/share/skills/ on every host; SKILLS_REF env var pins a tag (default: main). - .gitea/workflows/release.yml — auto-tag every push to main by Bump-Type footer (major/minor/patch, default patch). Skipped when commit contains [skip-release]. - README — usage, versioning, contribution flow, secret-hygiene rule. Phase 1 wires Claude Code only (~/.claude/skills/<name> global + <repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode, antigravity, and gitea-resident agents (cobalt-dingo, agentsquad) once their skill conventions are researched. Public repo, markdown-only — no secrets, no client names. Verified via pre-push grep before initial push. [skip-release]
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.