refactor(plugin)!: ship as a Claude Code plugin, one dir link per harness
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
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
---
|
||||
name: spec-driven-dev
|
||||
description: Write a structured specification before writing any code. Use when starting a new project, feature, or significant change. Adapted for a PM-first workflow where why comes before how.
|
||||
---
|
||||
|
||||
# Spec-Driven Development
|
||||
|
||||
## Overview
|
||||
|
||||
Write a structured specification before writing any code. The spec is the shared source of truth — it defines what we're building, why it matters, and how we'll know it's done.
|
||||
|
||||
**Code without a spec is guessing.**
|
||||
|
||||
A spec doesn't need to be long. A two-paragraph spec beats no spec. The value is in forcing clarity before code is written, not in the length of the document.
|
||||
|
||||
## Mathias PM Context
|
||||
|
||||
As a digital product manager building software:
|
||||
|
||||
- **Why before how:** The spec must capture the business context and user need before technical decisions. Agents reading the spec should understand *why this matters*, not just *what to build*.
|
||||
- **Explicit success criteria:** Vague requirements produce vague results. Every spec must have testable success criteria.
|
||||
- **Surfaces assumptions:** The spec's primary job is to surface misunderstandings before they become expensive code.
|
||||
- **Living document:** Update the spec when decisions change. An outdated spec is still better than no spec.
|
||||
|
||||
## When to Use
|
||||
|
||||
**Always create a spec when:**
|
||||
- Starting a new project or feature
|
||||
- Requirements are ambiguous or incomplete
|
||||
- The change touches multiple files or modules
|
||||
- You're about to make an architectural decision
|
||||
- The task would take more than a day to implement
|
||||
|
||||
**When NOT to use:** Single-line fixes, typos, or changes where requirements are unambiguous and self-contained.
|
||||
|
||||
## The Gated Workflow
|
||||
|
||||
Do not advance to the next phase without validation at each gate.
|
||||
|
||||
```
|
||||
SPECIFY → [review] → PLAN → [review] → TASKS → [review] → IMPLEMENT
|
||||
```
|
||||
|
||||
Each gate is a deliberate pause: does the next phase make sense given what we know?
|
||||
|
||||
## Phase 1: SPECIFY
|
||||
|
||||
### Surface Assumptions Immediately
|
||||
|
||||
Before writing spec content, list what you're assuming:
|
||||
|
||||
```
|
||||
ASSUMPTIONS I'M MAKING:
|
||||
1. This is a Go backend service (no frontend changes)
|
||||
2. Authentication is handled by the existing middleware
|
||||
3. The database is PostgreSQL (matching the rest of the stack)
|
||||
4. The feature is used by authenticated users only, not public
|
||||
→ Confirm or correct before I proceed.
|
||||
```
|
||||
|
||||
Don't silently fill in ambiguous requirements.
|
||||
|
||||
### Write the Spec
|
||||
|
||||
A spec covers six areas:
|
||||
|
||||
**1. Objective — WHY are we building this?**
|
||||
|
||||
This is the most important section. It must answer:
|
||||
- What user problem does this solve?
|
||||
- Who is the user?
|
||||
- What does success look like from the user's perspective?
|
||||
- Why now?
|
||||
|
||||
```markdown
|
||||
## Objective
|
||||
|
||||
Invoice importers at small accounting firms manually copy payment details
|
||||
from PDF invoices into their banking system, taking 10–20 minutes per invoice.
|
||||
|
||||
**User:** Invoice processor at an accounting firm (10–50 invoices/day)
|
||||
**Problem:** Manual data entry is slow, error-prone, and creates compliance risk
|
||||
**Goal:** Reduce per-invoice processing time from ~15 minutes to < 2 minutes
|
||||
|
||||
Success: Invoice processor can extract and queue a payment from a PDF in under 2 minutes,
|
||||
with confidence the data is correct.
|
||||
```
|
||||
|
||||
**2. Commands — Full executable commands**
|
||||
|
||||
```
|
||||
Build: task build
|
||||
Test: task test (or: go test ./...)
|
||||
Lint: task lint (or: golangci-lint run)
|
||||
Dev: task dev
|
||||
Deploy: task deploy:staging
|
||||
```
|
||||
|
||||
**3. Project Structure — Where things live**
|
||||
|
||||
```
|
||||
internal/
|
||||
domain/ → Core types and interfaces
|
||||
service/ → Business logic
|
||||
store/ → Database implementations
|
||||
handler/ → HTTP handlers
|
||||
cmd/
|
||||
server/ → Main entry point
|
||||
```
|
||||
|
||||
**4. Code Style — One real example beats three paragraphs**
|
||||
|
||||
```go
|
||||
// Error handling: always wrap with context
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse invoice PDF: %w", err)
|
||||
}
|
||||
|
||||
// Dependency injection: accept interfaces
|
||||
func NewInvoiceService(store InvoiceStore, parser PDFParser) *InvoiceService { ... }
|
||||
|
||||
// Context: always first parameter for I/O operations
|
||||
func (s *InvoiceService) ProcessPDF(ctx context.Context, r io.Reader) (Invoice, error) { ... }
|
||||
```
|
||||
|
||||
**5. Testing Strategy**
|
||||
|
||||
```
|
||||
Framework: testing package + testify
|
||||
Locations: *_test.go files in same package
|
||||
Unit tests: table-driven, in-memory implementations for stores
|
||||
Fast path: go test ./... (unit tests only)
|
||||
Full suite: go test -tags=integration ./... (includes DB tests)
|
||||
Coverage: >80% for business logic packages
|
||||
```
|
||||
|
||||
**6. Boundaries**
|
||||
|
||||
```
|
||||
Always:
|
||||
- Run go test ./... before committing
|
||||
- Wrap errors with fmt.Errorf("context: %w", err)
|
||||
- Pass ctx as first parameter to any I/O function
|
||||
- Run govulncheck before adding new dependencies
|
||||
|
||||
Ask first:
|
||||
- Schema changes
|
||||
- Adding new external dependencies
|
||||
- Changing public API contracts
|
||||
- Performance changes that affect existing behavior
|
||||
|
||||
Never:
|
||||
- Commit secrets or API keys
|
||||
- Remove or skip failing tests
|
||||
- Send client data to external APIs
|
||||
- Use naked returns on errors
|
||||
```
|
||||
|
||||
### Spec Template
|
||||
|
||||
```markdown
|
||||
# Spec: [Feature/Project Name]
|
||||
|
||||
## Objective
|
||||
[What we're building and why. User story or problem statement.]
|
||||
[Who is the user? What does success look like from their perspective?]
|
||||
|
||||
## Tech Stack
|
||||
[Language, key libraries, relevant existing infrastructure]
|
||||
|
||||
## Commands
|
||||
Build: [full command]
|
||||
Test: [full command]
|
||||
Lint: [full command]
|
||||
Dev: [full command]
|
||||
|
||||
## Project Structure
|
||||
[Directory layout with descriptions]
|
||||
|
||||
## Code Style
|
||||
[One real code example showing the patterns to follow]
|
||||
|
||||
## Testing Strategy
|
||||
[Framework, test locations, what to unit test vs integration test]
|
||||
|
||||
## Boundaries
|
||||
- Always: [...]
|
||||
- Ask first: [...]
|
||||
- Never: [...]
|
||||
|
||||
## Success Criteria
|
||||
[Specific, testable conditions that define "done"]
|
||||
- [ ] [Condition 1: metric/threshold/method]
|
||||
- [ ] [Condition 2: ...]
|
||||
|
||||
## Open Questions
|
||||
[Anything unresolved that needs input before implementation begins]
|
||||
```
|
||||
|
||||
## Reframing Vague Requirements
|
||||
|
||||
When you receive a vague requirement, translate it into specific success criteria before writing any spec content:
|
||||
|
||||
```
|
||||
Vague: "Make the invoice parser more reliable"
|
||||
|
||||
Reframed success criteria:
|
||||
- Parser correctly extracts IBAN from 95% of Swedish invoice formats
|
||||
- Parser correctly extracts total amount from 98% of tested invoices
|
||||
- Parser returns a structured error (not a panic) for unrecognized formats
|
||||
- Processing time < 2 seconds for PDFs up to 10MB
|
||||
→ Are these the right targets?
|
||||
```
|
||||
|
||||
## Phase 2: PLAN
|
||||
|
||||
With a validated spec, create a technical implementation plan:
|
||||
|
||||
1. Identify major components and their dependencies
|
||||
2. Determine implementation order (foundations first)
|
||||
3. Note risks and unknowns
|
||||
4. Identify what can be built in parallel vs. what must be sequential
|
||||
5. Define verification checkpoints between phases
|
||||
|
||||
The plan should be reviewable: anyone should be able to read it and say "yes, that's the right approach" or "no, change X."
|
||||
|
||||
Load the `planning` skill for detailed task breakdown.
|
||||
|
||||
## Phase 3: TASKS
|
||||
|
||||
Break the plan into discrete, implementable tasks. Load the `planning` skill for the full task breakdown methodology.
|
||||
|
||||
**Each task must have:**
|
||||
- Acceptance criteria
|
||||
- Verification step (test command, build, manual check)
|
||||
- File count estimate (no task should touch more than ~5 files)
|
||||
|
||||
## Phase 4: IMPLEMENT
|
||||
|
||||
Execute tasks one at a time. For each task:
|
||||
1. Load `tdd` skill — write failing tests first
|
||||
2. Implement minimal code to pass
|
||||
3. Load `clean-code` skill — refactor
|
||||
|
||||
## Keeping the Spec Alive
|
||||
|
||||
- **Update when decisions change** — spec first, then code
|
||||
- **Update when scope changes**
|
||||
- **Commit the spec** — it belongs in version control
|
||||
- **Reference the spec in PRs** — link to the section each PR implements
|
||||
|
||||
## Common Rationalizations
|
||||
|
||||
| Rationalization | Reality |
|
||||
|---|---|
|
||||
| "This is simple, I don't need a spec" | Simple tasks still need acceptance criteria. A two-line spec is fine. |
|
||||
| "I'll write the spec after" | That's documentation, not specification. The spec's value is forcing clarity *before* code. |
|
||||
| "The spec will slow us down" | A 15-minute spec prevents hours of rework. |
|
||||
| "Requirements will change anyway" | That's why the spec is a living document. |
|
||||
| "The user knows what they want" | Even clear requests have implicit assumptions. The spec surfaces those. |
|
||||
|
||||
## Verification
|
||||
|
||||
Before starting implementation:
|
||||
|
||||
- [ ] Spec covers all six core areas
|
||||
- [ ] Mathias has reviewed and approved the spec
|
||||
- [ ] Success criteria are specific and testable
|
||||
- [ ] Boundaries (Always/Ask First/Never) are defined
|
||||
- [ ] Open questions are resolved or accepted as unknowns
|
||||
- [ ] Spec is saved to a file in the repository
|
||||
|
||||
## Brain MCP Integration
|
||||
|
||||
### 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: "spec-driven-dev", phase: "<phase-name>", final_status: "pass" | "fail" | "skip", message: "<one-line summary>", duration_ms: <wall-clock>, project_root: "<absolute path>"}`
|
||||
|
||||
**Phases for this skill:** specify, plan, tasks, implement
|
||||
|
||||
**Status semantics:**
|
||||
- `pass` — the phase's intended outcome was reached (gate passed).
|
||||
- `fail` — the phase's intended outcome was NOT reached (gate blocked, rework required).
|
||||
- `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.
|
||||
|
||||
## Cross-References
|
||||
|
||||
- Load `problem-analysis` skill for deep requirement understanding before speccing
|
||||
- Load `user-stories` skill to decompose the spec into stories
|
||||
- Load `planning` skill for task breakdown
|
||||
- Load `feature-spec` skill once implementation begins, to scope individual features inside the project
|
||||
- Load `tdd` skill during implementation
|
||||
Reference in New Issue
Block a user