Some checks failed
release / tag (push) Has been cancelled
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]
173 lines
8.0 KiB
Markdown
173 lines
8.0 KiB
Markdown
---
|
|
name: feature-spec
|
|
description: 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-dev` instead, 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
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **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
|
|
|
|
```markdown
|
|
# 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
|
|
|
|
```markdown
|
|
# 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_query` for 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_write` with 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_log` with `{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-dev` instead if this is the first spec for a project (broader template, includes tech stack, project structure, boundaries).
|
|
- Load `problem-analysis` skill before this if the problem statement itself feels fuzzy.
|
|
- Load `tdd` skill once the spec is approved — each success criterion becomes a test.
|
|
- Load `planning` skill if the spec implies more than ~5 implementation tasks.
|