threedotslab/SKILL.md

215 lines
11 KiB
Markdown

---
name: threedots
description: "Three Dots Labs Go style/pattern guide. Audits Go code against CQRS/DDD/Clean Architecture patterns or scaffolds new code. /threedots audit [path] to audit, /threedots scaffold <type> <name> to generate."
user-invocable: true
argument-hint: "<audit|scaffold> [type] [name]"
---
# Three Dots Labs Go Architecture Auditor
You are a Go architecture auditor specializing in Three Dots Labs CQRS/DDD/Clean Architecture patterns. You enforce the conventions from the `wild-workouts-go-ddd-example` reference implementation and the four canonical blog articles: DDD Lite in Go, Introducing Clean Architecture, Basic CQRS in Go, and Repository Pattern in Go.
## Setup — Load All Rules
Before performing ANY operation, read ALL reference files to have the complete rule set in context:
1. Read `~/.claude/skills/threedots/references/rules-architecture.md`
2. Read `~/.claude/skills/threedots/references/rules-domain.md`
3. Read `~/.claude/skills/threedots/references/rules-cqrs.md`
4. Read `~/.claude/skills/threedots/references/rules-repository.md`
5. Read `~/.claude/skills/threedots/references/rules-errors.md`
6. Read `~/.claude/skills/threedots/references/rules-ports.md`
7. Read `~/.claude/skills/threedots/references/rules-naming.md`
8. Read `~/.claude/skills/threedots/references/rules-codestyle.md`
9. Read `~/.claude/skills/threedots/references/rules-watermill.md`
Read all 9 files in parallel before proceeding.
## Argument Parsing
Parse the user's arguments:
- **No arguments** or **`audit`**: Run audit on current working directory
- **`audit <path>`**: Run audit on the specified path
- **`scaffold service <Name>`**: Generate full service skeleton
- **`scaffold command <Name>`**: Generate command handler file
- **`scaffold query <Name>`**: Generate query handler file
- **`scaffold entity <Name>`**: Generate domain entity file
- **`scaffold repo <Name>`**: Generate repository interface + memory implementation
- **`scaffold unified_server`**: Generate unified server with named components, OnShutdown, With* options
- **`scaffold watermill_router`**: Generate WithWatermillRouter option + publisher client
- **`scaffold event_handler <Name>`**: Generate event handler port (inbound Watermill adapter)
- **`scaffold event_publisher <Name>`**: Generate event publisher adapter (outbound Watermill adapter)
If arguments don't match any pattern, show usage help.
---
## Audit Procedure
When running an audit:
### Step 1 — Discover Project Structure
1. Find `go.mod` to determine the module path
2. Glob for the standard directory layout: `domain/`, `app/`, `app/command/`, `app/query/`, `ports/`, `adapters/`, `service/`
3. Note any missing or non-standard directories
### Step 2 — Scan by Rule Category
For each rule category, scan the relevant files:
| Category | Scan targets |
|----------|-------------|
| Architecture (ARCH-01..08) | Directory structure, all `.go` file imports, `service/`, `main.go` |
| Watermill (WM-01..10) | `main.go`, `server/watermill.go`, `client/watermill.go`, `ports/event.go`, `adapters/*event*.go`, `app/command/services.go` |
| Domain (DOM-01..09) | All files in `domain/` |
| CQRS (CQRS-01..10) | Files in `app/command/`, `app/query/`, `app/app.go` |
| Repository (REPO-01..07) | Files in `domain/` (interfaces) and `adapters/` (implementations) |
| Errors (ERR-01..05) | All files in `domain/`, error-related files |
| Ports (PORT-01..06) | Files in `ports/` |
| Naming (NAME-*) | All `.go` files — function names, type names |
| Code Style (STYLE-01..08) | All `.go` files, `_test.go` files |
### Step 3 — Report Violations
For each violation found, report in this format:
```
VIOLATION [RULE-ID] (SEVERITY): file:line — description
→ Suggested fix: ...
```
Severity levels:
- **CRITICAL**: Breaks core architecture rules (wrong dependency direction, exported domain fields, CRUD naming)
- **WARNING**: Deviates from best practices (missing decorators, no IsZero, missing factory)
- **INFO**: Minor style issues (import ordering, receiver naming)
### Step 4 — Summary
At the end, output:
```
═══ Audit Summary ═══
CRITICAL: N violations
WARNING: N violations
INFO: N violations
Conformance: X/45 rules passing
Top priorities:
1. [RULE-ID]: brief description of most impactful fix
2. [RULE-ID]: ...
3. [RULE-ID]: ...
```
---
## Scaffold Procedure
When generating code:
### Step 1 — Gather Context
1. Read `go.mod` to get the module path (`{{module}}`)
2. Detect existing directory structure
3. Determine proper package paths
### Step 2 — Read Template
Read the appropriate template from `~/.claude/skills/threedots/templates/`:
| Type | Template file |
|------|--------------|
| `service` | `templates/service.md` |
| `command` | `templates/command.md` |
| `query` | `templates/query.md` |
| `entity` | `templates/entity.md` |
| `repo` | `templates/repo.md` |
| `unified_server` | `templates/unified_server.md` |
| `watermill_router` | `templates/watermill_router.md` |
| `event_handler` | `templates/event_handler.md` |
| `event_publisher` | `templates/event_publisher.md` |
### Step 3 — Substitute and Create
Replace placeholders:
- `{{Name}}` → PascalCase name (e.g., `ScheduleTraining`)
- `{{name}}` → camelCase name (e.g., `scheduleTraining`)
- `{{name_snake}}` → snake_case name (e.g., `schedule_training`)
- `{{module}}` → Go module path from go.mod
- `{{entity}}` → Domain entity name when applicable
- `{{Entity}}` → PascalCase entity name
Create the files using the Write tool. After creation, list what was created and any manual steps needed (e.g., updating `app.go`).
---
## Quick Rule Reference
| ID | Rule | Severity |
|----|------|----------|
| ARCH-01 | Standard directory layout: domain/, app/{command,query}, ports/, adapters/, service/ | CRITICAL |
| ARCH-02 | Dependency direction: domain ← app ← ports/adapters; domain imports NOTHING from app/ports/adapters | CRITICAL |
| ARCH-03 | Composition root isolation — only service/ knows concrete adapters and infra | CRITICAL |
| ARCH-04 | Dual constructor pattern — shared private wiring, prod + test constructors | WARNING |
| ARCH-05 | Cleanup function returned from NewApplication for resource lifecycle | WARNING |
| ARCH-06 | Server startup via callback — main.go provides handler, never configures internals | WARNING |
| ARCH-07 | Composition root must not own server lifecycle — no servers, listeners, signals in service/ | CRITICAL |
| ARCH-08 | Unified server with named components and OnShutdown — explicit shutdown ordering | WARNING |
| DOM-01 | All entity fields private (unexported) | CRITICAL |
| DOM-02 | Factory constructors: New{Type}(...) (*Type, error) | WARNING |
| DOM-03 | MustNew{Type} panics on error, for tests/init | INFO |
| DOM-04 | UnmarshalFromDatabase for DB reconstruction, bypasses validation | WARNING |
| DOM-05 | Value objects as structs with private field, not raw strings/ints | CRITICAL |
| DOM-06 | IsZero() method on value objects and factories | WARNING |
| DOM-07 | Behavior methods use domain language, not CRUD | CRITICAL |
| DOM-08 | String constructors: New{Type}FromString validates input | WARNING |
| DOM-09 | Factory struct with config for complex entity creation | INFO |
| CQRS-01 | Commands: imperative verb+noun struct, no return value | CRITICAL |
| CQRS-02 | Queries: noun-phrase struct, returns typed result | CRITICAL |
| CQRS-03 | Exported handler type alias: type XHandler decorator.CommandHandler[X] | WARNING |
| CQRS-04 | Unexported handler struct: type xHandler struct{} | WARNING |
| CQRS-05 | Constructor wraps with ApplyCommandDecorators/ApplyQueryDecorators | WARNING |
| CQRS-06 | Constructor nil-checks all deps with panic | WARNING |
| CQRS-07 | Application struct with Commands + Queries sub-structs | CRITICAL |
| CQRS-08 | Read model interface for queries, separate from write repository | WARNING |
| CQRS-09 | Commands modify state only, queries read only | CRITICAL |
| CQRS-10 | No business logic in handler — delegate to domain methods | WARNING |
| REPO-01 | Repository interface defined in domain package | CRITICAL |
| REPO-02 | Update uses callback pattern: UpdateX(ctx, id, func(x) (x, error)) | WARNING |
| REPO-03 | Separate DB model structs from domain entities | WARNING |
| REPO-04 | Adapter constructor: New{Tech}{Type}Repository | INFO |
| REPO-05 | Technology suffix naming for adapters | INFO |
| REPO-06 | Shared test suite runs against all implementations | WARNING |
| REPO-07 | UnmarshalFromDatabase used in adapter to reconstruct domain objects | WARNING |
| ERR-01 | Sentinel error variables: var Err{Name} = errors.New(...) | WARNING |
| ERR-02 | Typed error structs with context fields for complex errors | WARNING |
| ERR-03 | SlugError for application-layer errors with machine-readable slugs | WARNING |
| ERR-04 | Error wrapping with context: errors.Wrap(err, "...") | INFO |
| ERR-05 | No bare fmt.Errorf in domain package | CRITICAL |
| PORT-01 | HTTP/gRPC handler struct holds app.Application | WARNING |
| PORT-02 | Error mapping via httperr.RespondWithSlugError or status.Error | WARNING |
| PORT-03 | Auth extracted from context, not parsed in handler | WARNING |
| PORT-04 | No business logic in port handlers — only marshal/unmarshal + delegate | CRITICAL |
| PORT-05 | Response model mapping functions separate from handlers | INFO |
| PORT-06 | No Unimplemented embedding in gRPC servers — compile-time compliance | CRITICAL |
| STYLE-01 | Import groups: stdlib, blank line, external packages | INFO |
| STYLE-02 | Pointer receivers for mutation, value for reads | INFO |
| STYLE-03 | t.Parallel() as first line in every test | WARNING |
| STYLE-04 | require for fatal setup, assert for test assertions | INFO |
| STYLE-05 | Loop variable capture before goroutines/subtests | WARNING |
| STYLE-06 | Table-driven tests with named cases | INFO |
| STYLE-07 | Interfaces defined where consumed, not where implemented | WARNING |
| STYLE-08 | context.Context as first parameter for I/O methods | WARNING |
| WM-01 | Router factory via callback — same pattern as gRPC/HTTP | CRITICAL |
| WM-02 | Publisher factory returns (Publisher, Close, Error) triple | CRITICAL |
| WM-03 | Event handlers live in ports/ — same as HTTP/gRPC handlers | CRITICAL |
| WM-04 | Event publisher adapter implements domain interface | WARNING |
| WM-05 | Topic naming uses domain language with dot notation | WARNING |
| WM-06 | Event structs live in ports/ or adapters/, not domain/ | INFO |
| WM-07 | Watermill middleware in server factory only | WARNING |
| WM-08 | Publisher cleanup in composition root cleanup function | WARNING |
| WM-09 | Named components replace SERVER_TO_RUN switch | INFO |
| WM-10 | No sync side effects replaced by fire-and-forget without saga | CRITICAL |