@ai-agent-lead/skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/bin/install.js +272 -0
- package/package.json +34 -0
- package/skills/LANGUAGE.md +72 -0
- package/skills/README.md +156 -0
- package/skills/SKILL-TEMPLATE.md +120 -0
- package/skills/TRIGGERS.md +64 -0
- package/skills/WORKFLOWS.md +369 -0
- package/skills/bench/SKILL.md +40 -0
- package/skills/bench/templates/benchmark-report.md +26 -0
- package/skills/bootstrap/BOOTSTRAP.md +13 -0
- package/skills/bootstrap/SKILL.md +47 -0
- package/skills/code-hygiene/SKILL.md +92 -0
- package/skills/debug/SKILL.md +122 -0
- package/skills/design/DEEP-MODULES.md +76 -0
- package/skills/design/FUNCTIONAL-CORE.md +121 -0
- package/skills/design/ILLEGAL-STATES.md +102 -0
- package/skills/design/OBSERVABILITY.md +49 -0
- package/skills/design/PERSONAS.md +41 -0
- package/skills/design/SKILL.md +139 -0
- package/skills/design/TESTABILITY.md +84 -0
- package/skills/feature-doc/SKILL.md +113 -0
- package/skills/feature-doc/templates/feature-template.md +52 -0
- package/skills/formats/ADR-FORMAT.md +51 -0
- package/skills/formats/CONTEXT-FORMAT.md +109 -0
- package/skills/formats/CONTEXT-MAP-FORMAT.md +6 -0
- package/skills/grill-plan/SKILL.md +112 -0
- package/skills/improve-codebase-architecture/DEEPENING.md +37 -0
- package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +41 -0
- package/skills/improve-codebase-architecture/SKILL.md +115 -0
- package/skills/investigate/SKILL.md +97 -0
- package/skills/investigate/templates/research-note.md +84 -0
- package/skills/pr-review/SKILL.md +197 -0
- package/skills/prod-ready/SKILL.md +88 -0
- package/skills/security-review/SKILL.md +145 -0
- package/skills/simplify/SKILL.md +105 -0
- package/skills/sync-check/SKILL.md +69 -0
- package/skills/system-design/SKILL.md +160 -0
- package/skills/tdd/SKILL.md +121 -0
- package/skills/tdd/TESTS.md +93 -0
- package/skills/tdd-rounds/COMMITS.md +122 -0
- package/skills/tdd-rounds/SKILL.md +96 -0
- package/skills/tdd-rounds/templates/builder-brief.md +73 -0
- package/skills/tdd-rounds/templates/builder-report.md +21 -0
- package/skills/verify-real-deps/MOTIVATION.md +18 -0
- package/skills/verify-real-deps/SKILL.md +118 -0
- package/skills/verify-real-deps/templates/known-issues.md +45 -0
- package/skills/zoom-out/SKILL.md +104 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design
|
|
3
|
+
description: Module and interface design principles — deep modules and testable interfaces. Use when designing a NEW module, class, or public API; deciding what to expose vs hide; reviewing an interface before implementation; or when the user asks "how should I structure this", mentions "deep modules", "testability", or "API design". Use for NEW code shape; for finding deepening opportunities in EXISTING code, use `improve-codebase-architecture`. Skip for trivial glue, getters/setters, or single-call wrappers. Pairs with the tdd skill — good design is what makes TDD pleasant.
|
|
4
|
+
complexity: medium
|
|
5
|
+
expected_duration: 20 minutes
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Module and Interface Design
|
|
9
|
+
|
|
10
|
+
Principles that make code easier to understand, change, and test.
|
|
11
|
+
|
|
12
|
+
1. **Deep modules** — small interface, lots hidden behind it.
|
|
13
|
+
2. **Testable interfaces** — accept dependencies, return results, keep the surface small.
|
|
14
|
+
3. **Illegal states unrepresentable** — encode runtime invariants in the type system.
|
|
15
|
+
4. **Functional core, imperative shell** — pure logic at the center, side effects at the edges.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
- Before writing a new module, class, or public function.
|
|
20
|
+
- When refactoring something that's painful to test.
|
|
21
|
+
- When reviewing an API surface (PRs, design docs).
|
|
22
|
+
- When a test is hard to write — usually the design is wrong, not the test.
|
|
23
|
+
|
|
24
|
+
## When to skip
|
|
25
|
+
|
|
26
|
+
- Trivial glue, getters/setters, single-call wrappers — design is overkill.
|
|
27
|
+
- Existing code that needs deepening — use [`improve-codebase-architecture`](../improve-codebase-architecture/SKILL.md).
|
|
28
|
+
- Whole-system topology (which modules should exist) — use [`system-design`](../system-design/SKILL.md).
|
|
29
|
+
|
|
30
|
+
## Principle 1: Deep modules
|
|
31
|
+
|
|
32
|
+
A **deep module** has a small, simple interface that hides a lot of complexity.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌──────────────────┐
|
|
36
|
+
│ Small interface │ few methods, simple params
|
|
37
|
+
├──────────────────┤
|
|
38
|
+
│ │
|
|
39
|
+
│ Deep │ complex logic, hidden
|
|
40
|
+
│ implementation │
|
|
41
|
+
│ │
|
|
42
|
+
└──────────────────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A **shallow module** has a wide interface and thin implementation — it just passes through. Avoid.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
┌──────────────────────────────┐
|
|
49
|
+
│ Large interface │ many methods, leaky params
|
|
50
|
+
├──────────────────────────────┤
|
|
51
|
+
│ Thin implementation │ mostly pass-through
|
|
52
|
+
└──────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When designing a module, ask:
|
|
56
|
+
|
|
57
|
+
- Can I reduce the number of methods?
|
|
58
|
+
- Can I simplify the parameters?
|
|
59
|
+
- Can I hide more complexity behind the interface?
|
|
60
|
+
|
|
61
|
+
See [DEEP-MODULES.md](DEEP-MODULES.md) for examples.
|
|
62
|
+
|
|
63
|
+
## Principle 2: Design for testability
|
|
64
|
+
|
|
65
|
+
Good interfaces make testing natural. Three rules:
|
|
66
|
+
|
|
67
|
+
1. **Accept dependencies, don't create them.** Pass them in.
|
|
68
|
+
2. **Return results, don't produce side effects.** Pure functions are trivially testable.
|
|
69
|
+
3. **Small surface area.** Fewer methods, fewer params.
|
|
70
|
+
|
|
71
|
+
See [TESTABILITY.md](TESTABILITY.md) for examples and counter-examples.
|
|
72
|
+
|
|
73
|
+
## Principle 3: Make illegal states unrepresentable
|
|
74
|
+
|
|
75
|
+
Push runtime invariants into the type system. If a state can't exist, the compiler should reject it — not a runtime check, not a comment.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
WEAK — both fields can be set or neither; runtime has to validate
|
|
79
|
+
type User = { email?: string; verifiedAt?: Date }
|
|
80
|
+
|
|
81
|
+
STRONG — the type enforces the invariant; the bad state can't compile
|
|
82
|
+
type User =
|
|
83
|
+
| { kind: "unverified", email: string }
|
|
84
|
+
| { kind: "verified", email: string, verifiedAt: Date }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The difference: *"this can't happen"* in a comment (hope) vs *"this can't compile"* in the type system (guarantee). The second eliminates whole classes of bugs at compile time.
|
|
88
|
+
|
|
89
|
+
Applies in any language with sum types or discriminated unions (TS, Rust, Swift, OCaml, Kotlin, Python with `Literal`-tagged unions). In languages without them, approximate with enums + private constructors + factory methods, or builders that only expose `build()` once required fields are set.
|
|
90
|
+
|
|
91
|
+
See [ILLEGAL-STATES.md](ILLEGAL-STATES.md) for more examples and the limits of the pattern.
|
|
92
|
+
|
|
93
|
+
## Principle 4: Functional core, imperative shell
|
|
94
|
+
|
|
95
|
+
Push pure logic to the center; keep side effects (HTTP, DB, file I/O, time, randomness) at the edges. The functional core becomes trivially testable; the shell stays small enough to verify by inspection.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
┌────────────────────────────────┐
|
|
99
|
+
│ Imperative shell │ ← side effects live here
|
|
100
|
+
│ (small, hard to test fully) │
|
|
101
|
+
│ │
|
|
102
|
+
│ ┌─────────────────────────┐ │
|
|
103
|
+
│ │ Functional core │ │ ← same input → same output
|
|
104
|
+
│ │ (large, pure, trivial │ │ no side effects
|
|
105
|
+
│ │ to test) │ │
|
|
106
|
+
│ └─────────────────────────┘ │
|
|
107
|
+
└────────────────────────────────┘
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Composes with Principle 2 — the core is what tests target, so mocks shrink to a handful at the shell boundary. Composes with Principle 3 — the core often returns Result-style values that the shell pattern-matches on.
|
|
111
|
+
|
|
112
|
+
See [FUNCTIONAL-CORE.md](FUNCTIONAL-CORE.md) for a concrete refactor example.
|
|
113
|
+
|
|
114
|
+
## How this connects to TDD
|
|
115
|
+
|
|
116
|
+
If TDD feels painful — tests need elaborate setup, mocks of internals, or peeking at private state — the design is wrong. Fix the interface, not the test. The two skills work as a pair:
|
|
117
|
+
|
|
118
|
+
- `design` decides *what the interface should be*.
|
|
119
|
+
- `tdd` decides *how to build it test-first*.
|
|
120
|
+
|
|
121
|
+
## Done when
|
|
122
|
+
|
|
123
|
+
The interface is small enough that you can describe it in one sentence, and the testability rules hold:
|
|
124
|
+
|
|
125
|
+
- Dependencies are accepted, not created internally.
|
|
126
|
+
- The module returns results; side effects are isolated.
|
|
127
|
+
- Surface area (method count + parameter count) is minimized.
|
|
128
|
+
|
|
129
|
+
If TDD then feels painful, return here — the design needs another pass.
|
|
130
|
+
|
|
131
|
+
## Optional artifact: design note
|
|
132
|
+
|
|
133
|
+
Most of the time `design` is guidance only — the shape lives in the code that follows. **Capture a sibling design note when**:
|
|
134
|
+
|
|
135
|
+
- The module shape is non-trivial enough that a future contributor would benefit from seeing the reasoning (interface options considered, why one was picked, what's hidden behind the seam).
|
|
136
|
+
- The interface decisions are load-bearing for downstream rounds of `tdd-rounds`.
|
|
137
|
+
- A `prod-ready` reviewer will need to verify "module map / public-interface signatures / test boundaries" against something explicit (per [prod-ready Section 7](../prod-ready/SKILL.md)).
|
|
138
|
+
|
|
139
|
+
When captured, save as a sibling to the feature doc: `docs/features/<feature>.design.md`. Skip when the interface is small enough that the code is the design.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Designing for Testability
|
|
2
|
+
|
|
3
|
+
Three rules that make code testable without sacrificing clarity.
|
|
4
|
+
|
|
5
|
+
> Examples below use TypeScript for readability. The rules apply identically in Python (constructor injection + `Protocol`), Go (interface as parameter, fake struct in tests), Rust (trait object, mock impl), Kotlin / Java (interface + DI), and any other language with first-class function or interface values. Translate the syntax; the shape stays.
|
|
6
|
+
|
|
7
|
+
## 1. Accept dependencies, don't create them
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// HARD TO TEST — gateway is hardcoded
|
|
11
|
+
function processOrder(order: Order) {
|
|
12
|
+
const gateway = new StripeGateway(process.env.STRIPE_KEY);
|
|
13
|
+
return gateway.charge(order.total);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// EASY TO TEST — gateway is passed in
|
|
17
|
+
function processOrder(order: Order, gateway: PaymentGateway) {
|
|
18
|
+
return gateway.charge(order.total);
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
In tests, pass a fake or stub gateway. In production, pass the real one. No mocking framework needed.
|
|
23
|
+
|
|
24
|
+
## 2. Return results, don't produce side effects
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// HARD TO TEST — mutates the cart, returns nothing
|
|
28
|
+
function applyDiscount(cart: Cart): void {
|
|
29
|
+
cart.total -= computeDiscount(cart);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// EASY TO TEST — pure function
|
|
33
|
+
function calculateDiscount(cart: Cart): Discount {
|
|
34
|
+
return { amount: computeDiscount(cart) };
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Pure functions are the easiest things in software to test — same input, same output, no setup, no teardown.
|
|
39
|
+
|
|
40
|
+
## 3. Keep the surface area small
|
|
41
|
+
|
|
42
|
+
- Fewer methods → fewer tests needed.
|
|
43
|
+
- Fewer parameters → simpler test setup.
|
|
44
|
+
- Each method should do one thing.
|
|
45
|
+
|
|
46
|
+
## Mocking guidance
|
|
47
|
+
|
|
48
|
+
Mock only at **system boundaries**:
|
|
49
|
+
|
|
50
|
+
- External APIs (payment, email, third-party services)
|
|
51
|
+
- Time and randomness
|
|
52
|
+
- File system (sometimes — prefer a temp dir)
|
|
53
|
+
- Databases (sometimes — prefer a real test DB)
|
|
54
|
+
|
|
55
|
+
**Don't mock your own code.** If you find yourself mocking an internal class to test another internal class, the design is wrong — the two are too tightly coupled. Fix the interface so they can be tested independently, or test them together as one unit.
|
|
56
|
+
|
|
57
|
+
## SDK-style over generic fetchers
|
|
58
|
+
|
|
59
|
+
When wrapping an external API, prefer named methods over one generic call:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// GOOD — each call is independently mockable, type-safe per endpoint
|
|
63
|
+
const api = {
|
|
64
|
+
getUser: (id: string) => fetch(`/users/${id}`),
|
|
65
|
+
createOrder: (data: OrderInput) => fetch("/orders", { method: "POST", body: data }),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// BAD — mocking requires conditional logic in the mock
|
|
69
|
+
const api = {
|
|
70
|
+
call: (endpoint: string, options?: object) => fetch(endpoint, options),
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The SDK style means each test sets up exactly the calls it needs, and you can see at a glance which endpoints a test exercises.
|
|
75
|
+
|
|
76
|
+
## The test-difficulty signal
|
|
77
|
+
|
|
78
|
+
If a test is hard to write, the design is probably wrong. Common smells:
|
|
79
|
+
|
|
80
|
+
- Test needs to mock five things → too many dependencies, or wrong dependencies.
|
|
81
|
+
- Test needs to peek at private state → behavior isn't observable through the interface.
|
|
82
|
+
- Test setup is longer than the test itself → constructor or factory is doing too much.
|
|
83
|
+
|
|
84
|
+
Treat test pain as design feedback. Fix the code, not the test.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-doc
|
|
3
|
+
description: One-page contract for a non-trivial feature or bug fix — Problem, User Story, Acceptance Criteria, Non-Goals. The ACs become the test list for `tdd`. Use before any non-trivial feature or bug fix; when the user mentions "spec this out", "write a feature doc", "before any non-trivial feature", or describes a feature without listing ACs. Skip for typo fixes, dependency bumps, or pure refactors. Pairs with `tdd` / `tdd-rounds` (downstream — ACs feed the test list), `investigate` (upstream — when direction itself is unclear), and `grill-plan` (when the chosen plan needs stress-testing against existing model).
|
|
4
|
+
complexity: low
|
|
5
|
+
expected_duration: 10 minutes
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Feature Doc
|
|
9
|
+
|
|
10
|
+
Every non-trivial change starts with a one-page doc in `docs/features/<short-name>.md`. The doc is the **contract**: ACs become tests, Non-Goals prevent scope creep, and reviewers check the PR against this — not against memory.
|
|
11
|
+
|
|
12
|
+
## Why this skill exists
|
|
13
|
+
|
|
14
|
+
Without a contract, three failure modes are common:
|
|
15
|
+
|
|
16
|
+
- **Scope creep.** "While I'm in here…" turns a 1-AC change into a 4-AC change with no review of the new scope.
|
|
17
|
+
- **Untestable claims.** "Improve X" can't fail; ACs forced into Given/When/Then can.
|
|
18
|
+
- **Silent regressions.** Behavior added or dropped during implementation never surfaces in review because the diff is the only thing being read.
|
|
19
|
+
|
|
20
|
+
The doc is short by design — one page. If it grows, the feature is too big.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- Any non-trivial feature or bug fix where the ACs aren't already pinned somewhere.
|
|
25
|
+
- After `investigate` chooses a direction (the research note triggers a feature doc).
|
|
26
|
+
- After `debug` names a root cause and the fix is more than a one-liner.
|
|
27
|
+
|
|
28
|
+
## When to skip
|
|
29
|
+
|
|
30
|
+
- Typo fixes, dependency bumps, lint-only / formatter-only diffs.
|
|
31
|
+
- Pure refactors with no AC change — use `improve-codebase-architecture` instead.
|
|
32
|
+
- One-line config tweaks with no behavior change.
|
|
33
|
+
- The change already has a feature doc; you're iterating, not starting fresh.
|
|
34
|
+
|
|
35
|
+
## Steps
|
|
36
|
+
|
|
37
|
+
1. Copy [`templates/feature-template.md`](templates/feature-template.md) to `docs/features/<short-name>.md`.
|
|
38
|
+
2. Fill in **Problem**, **User Story**, **Acceptance Criteria**, **Non-Goals**.
|
|
39
|
+
3. Get one round of review on the doc **before** writing code.
|
|
40
|
+
4. Update the doc if behavior changes during implementation — stale docs are worse than none.
|
|
41
|
+
|
|
42
|
+
## Rules
|
|
43
|
+
|
|
44
|
+
- **One page max.** If it grows longer, split the feature.
|
|
45
|
+
- **Each AC must be testable** — Given / When / Then. "Improves performance" is not an AC; "p99 of `/api/orders` is < 200ms under 100 RPS" is.
|
|
46
|
+
- **Non-Goals is not optional.** Empty Non-Goals usually means unclear scope. Even "none — see scope in Problem" is better than missing.
|
|
47
|
+
- **Doc lives in the repo, versioned with the code.** Not in a wiki, not in a Notion page, not in a Linear ticket alone.
|
|
48
|
+
- **Tick AC boxes only when the test is green AND merged** — not when implementation starts.
|
|
49
|
+
|
|
50
|
+
## Example: weak vs strong ACs
|
|
51
|
+
|
|
52
|
+
```md
|
|
53
|
+
WEAK
|
|
54
|
+
- [ ] Users can reset their password.
|
|
55
|
+
- [ ] Reset link should expire.
|
|
56
|
+
- [ ] Improve email deliverability.
|
|
57
|
+
|
|
58
|
+
STRONG
|
|
59
|
+
- [ ] Given a registered user, when they POST `/auth/reset` with their email,
|
|
60
|
+
then the system emails a single-use token valid for 15 minutes.
|
|
61
|
+
- [ ] Given an expired or already-consumed token, when the user POSTs
|
|
62
|
+
`/auth/reset/confirm` with it, then the response is `410 Gone` and
|
|
63
|
+
no password change occurs.
|
|
64
|
+
- [ ] Given a valid token, when the user POSTs a new password,
|
|
65
|
+
then the user can authenticate with the new password and the token
|
|
66
|
+
is marked consumed.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The strong list translates 1:1 into tests. The weak list translates into vibes.
|
|
70
|
+
|
|
71
|
+
## Definition of Done for the doc
|
|
72
|
+
|
|
73
|
+
- [ ] Problem stated in 2–3 sentences.
|
|
74
|
+
- [ ] At least one acceptance criterion in Given / When / Then form.
|
|
75
|
+
- [ ] Non-goals listed (or explicitly "none — see scope in Problem").
|
|
76
|
+
- [ ] Reviewed by at least one other person.
|
|
77
|
+
|
|
78
|
+
## Anti-patterns
|
|
79
|
+
|
|
80
|
+
- **AC list as task list.** "Implement the login form" is a task; "Given a user with valid credentials, when they POST `/login`, then they receive a session cookie" is an AC. Tasks belong in your TODO; ACs belong in the contract.
|
|
81
|
+
- **Aspirational ACs.** "System is highly performant" — not testable, not falsifiable. Pin it: latency / throughput / error-rate threshold.
|
|
82
|
+
- **Empty Non-Goals as a habit.** "We'll figure out scope later" hides the disagreement; surface it now.
|
|
83
|
+
- **Spec written after the code.** The doc retrofits whatever shipped. The discipline is lost — at that point the diff is the contract, not the doc.
|
|
84
|
+
- **Doc lives in a wiki.** Drift starts immediately. The doc must live with the code.
|
|
85
|
+
- **Hidden behavior in the diff.** A feature doc with 3 ACs but a PR that ships 5 changes — the silent two are the most likely place a regression hides.
|
|
86
|
+
|
|
87
|
+
## Pairing with other skills
|
|
88
|
+
|
|
89
|
+
- **`investigate`** — runs *before* when direction is unclear. The research note's "Decided" recommendation triggers the feature doc.
|
|
90
|
+
- **`grill-plan`** — runs *after* when the chosen plan needs stress-testing against existing CONTEXT.md / ADRs. Or in **bootstrap mode** when the vocabulary itself is fuzzy.
|
|
91
|
+
- **`tdd`** — runs *next* for single-package, manageable AC count. Each AC becomes one TDD slice.
|
|
92
|
+
- **`tdd-rounds`** — runs *next* when the AC count is large (≥10) or multi-package. The AC list maps to round splits.
|
|
93
|
+
- **`security-review`** — runs *alongside* `tdd` when the feature is surface-changing (new entry point, identity flow, sensitive data path).
|
|
94
|
+
- **`prod-ready`** — runs after green, against this doc. Section 7 catches doc-drift if behavior shifted during implementation.
|
|
95
|
+
|
|
96
|
+
## Handoff
|
|
97
|
+
|
|
98
|
+
Once the doc is reviewed and ACs are stable:
|
|
99
|
+
|
|
100
|
+
- **Single-feature delivery** (one package, manageable AC count) → run [`tdd`](../tdd/SKILL.md). Each AC becomes one TDD slice.
|
|
101
|
+
- **Larger delivery** (≥10 ACs or multi-package) → run [`tdd-rounds`](../tdd-rounds/SKILL.md). The AC list maps to round splits.
|
|
102
|
+
- **Direction still feels uncertain after writing the doc**:
|
|
103
|
+
- Project has `CONTEXT.md` and/or ADRs → run [`grill-plan`](../grill-plan/SKILL.md) to stress-test against the existing model.
|
|
104
|
+
- No `CONTEXT.md` or ADRs yet → run [`investigate`](../investigate/SKILL.md) to map options first; or, if the plan is chosen but vocabulary is fuzzy, run `grill-plan` in [bootstrap mode](../grill-plan/BOOTSTRAP.md).
|
|
105
|
+
- **Hard-to-reverse / surface-changing** (new auth flow, public API, sensitive data flow) → run [`security-review`](../security-review/SKILL.md) alongside `tdd` / `tdd-rounds`.
|
|
106
|
+
|
|
107
|
+
## Done when
|
|
108
|
+
|
|
109
|
+
- `docs/features/<short-name>.md` exists with all four required sections.
|
|
110
|
+
- ACs are testable Given / When / Then statements.
|
|
111
|
+
- Non-Goals is non-empty (or explicitly "none — see scope in Problem").
|
|
112
|
+
- One reviewer has signed off.
|
|
113
|
+
- Status is `Approved` and the next skill (`tdd` / `tdd-rounds` / `grill-plan` / `security-review`) is named.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# <Feature Name>
|
|
2
|
+
|
|
3
|
+
**Status:** Draft | Approved | In Progress | Shipped
|
|
4
|
+
**Owner:** <name — primary author and point of contact for follow-up>
|
|
5
|
+
**Date:** YYYY-MM-DD
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
Status values:
|
|
9
|
+
- Draft — being written; not yet reviewed
|
|
10
|
+
- Approved — reviewed; ready to start implementation
|
|
11
|
+
- In Progress — implementation underway
|
|
12
|
+
- Shipped — all acceptance criteria checked AND merged to main
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
## Problem
|
|
16
|
+
|
|
17
|
+
What user pain or business need does this solve? (2-3 sentences.)
|
|
18
|
+
|
|
19
|
+
## User Story
|
|
20
|
+
|
|
21
|
+
As a <role>, I want <capability> so that <outcome>.
|
|
22
|
+
|
|
23
|
+
## Acceptance Criteria
|
|
24
|
+
|
|
25
|
+
Each bullet must be testable and map to at least one test. Tick a box only once the test covering it is green and merged — not when implementation starts.
|
|
26
|
+
|
|
27
|
+
- [ ] Given <context>, when <action>, then <result>.
|
|
28
|
+
- [ ] Given <context>, when <action>, then <result>.
|
|
29
|
+
|
|
30
|
+
## Non-Goals
|
|
31
|
+
|
|
32
|
+
What this feature explicitly does NOT do. Prevents scope creep.
|
|
33
|
+
|
|
34
|
+
This is about *capabilities deliberately not built*. (A research note's "Out of Scope" is about *decisions deliberately deferred* — different concept.)
|
|
35
|
+
|
|
36
|
+
- ...
|
|
37
|
+
|
|
38
|
+
## Related
|
|
39
|
+
|
|
40
|
+
Cross-links to other artifacts. Omit any subsection that doesn't apply.
|
|
41
|
+
|
|
42
|
+
- **ADRs:** [`ADR-NNNN <title>`](../adr/NNNN-slug.md) — why this one matters here
|
|
43
|
+
- **Research notes:** [`<topic>`](../research/<topic>.md)
|
|
44
|
+
- **Design note:** [`<short-name>.design.md`](./<short-name>.design.md) — when module shape was decided before code
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
|
|
48
|
+
Open questions, links, or design tradeoffs. Optional.
|
|
49
|
+
|
|
50
|
+
## Sign-off
|
|
51
|
+
|
|
52
|
+
- [ ] Reviewed by <name>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ADR Format
|
|
2
|
+
|
|
3
|
+
ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
|
|
4
|
+
|
|
5
|
+
Create the `docs/adr/` directory lazily — only when the first ADR is needed.
|
|
6
|
+
|
|
7
|
+
## Template
|
|
8
|
+
|
|
9
|
+
```md
|
|
10
|
+
# {Short title of the decision}
|
|
11
|
+
|
|
12
|
+
**Status:** accepted
|
|
13
|
+
**Date:** YYYY-MM-DD
|
|
14
|
+
|
|
15
|
+
{1-3 sentences: what's the context, what did we decide, and why.}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That's it. An ADR can be a single paragraph plus the two header lines. The value is in recording *that* a decision was made and *why* — not in filling out sections.
|
|
19
|
+
|
|
20
|
+
## Optional sections
|
|
21
|
+
|
|
22
|
+
Only include these when they add genuine value. Most ADRs won't need them. **Optional sections amplify a decision; they do not replace it.** Keep them terse — if a section grows past a short bullet list, ask whether the content belongs in a feature doc or design note instead. An ADR is not a feature spec.
|
|
23
|
+
|
|
24
|
+
- **Status** values: `proposed | accepted | deprecated | superseded by ADR-NNNN`. Default to `accepted` on creation; flip to `deprecated` or `superseded` when revisited.
|
|
25
|
+
- **Considered Options** — only when the rejected alternatives are worth remembering. One line per option, plus the reason for rejection.
|
|
26
|
+
- **Consequences** — only when non-obvious downstream effects need to be called out. Things the *code* will not make obvious.
|
|
27
|
+
- **Related** — cross-link to the feature-doc or research-note that triggered this ADR (recommended when one exists), and to peer ADRs it interacts with.
|
|
28
|
+
|
|
29
|
+
## Numbering
|
|
30
|
+
|
|
31
|
+
Scan `docs/adr/` for the highest existing number and increment by one. Numbers are monotonic — never reuse a retired number, even if the ADR is deleted, deprecated, or superseded. Gaps are acceptable; reuse causes confusion.
|
|
32
|
+
|
|
33
|
+
## When to offer an ADR
|
|
34
|
+
|
|
35
|
+
All three of these must be true:
|
|
36
|
+
|
|
37
|
+
1. **Hard to reverse** — the cost of changing your mind later is meaningful
|
|
38
|
+
2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
|
|
39
|
+
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
|
|
40
|
+
|
|
41
|
+
If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
|
|
42
|
+
|
|
43
|
+
### What qualifies
|
|
44
|
+
|
|
45
|
+
- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
|
|
46
|
+
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
|
|
47
|
+
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
|
|
48
|
+
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
|
|
49
|
+
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
|
|
50
|
+
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
|
|
51
|
+
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# CONTEXT.md Format
|
|
2
|
+
|
|
3
|
+
## Structure
|
|
4
|
+
|
|
5
|
+
```md
|
|
6
|
+
# {Context Name}
|
|
7
|
+
|
|
8
|
+
{One or two sentence description of what this context is and why it exists.}
|
|
9
|
+
|
|
10
|
+
## Language
|
|
11
|
+
|
|
12
|
+
**Order**:
|
|
13
|
+
{A concise description of the term}
|
|
14
|
+
_Avoid_: Purchase, transaction
|
|
15
|
+
|
|
16
|
+
**Invoice**:
|
|
17
|
+
A request for payment sent to a customer after delivery.
|
|
18
|
+
_Avoid_: Bill, payment request
|
|
19
|
+
|
|
20
|
+
**Customer**:
|
|
21
|
+
A person or organization that places orders.
|
|
22
|
+
_Avoid_: Client, buyer, account
|
|
23
|
+
|
|
24
|
+
## Relationships
|
|
25
|
+
|
|
26
|
+
- An **Order** produces one or more **Invoices**
|
|
27
|
+
- An **Invoice** belongs to exactly one **Customer**
|
|
28
|
+
|
|
29
|
+
## Example dialogue
|
|
30
|
+
|
|
31
|
+
> **Dev:** "When a **Customer** places an **Order**, do we create the **Invoice** immediately?"
|
|
32
|
+
> **Domain expert:** "No — an **Invoice** is only generated once a **Fulfillment** is confirmed."
|
|
33
|
+
|
|
34
|
+
## Flagged ambiguities
|
|
35
|
+
|
|
36
|
+
- "account" was used to mean both **Customer** and **User** — resolved: these are distinct concepts.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Rules
|
|
40
|
+
|
|
41
|
+
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others as aliases under `_Avoid_:`.
|
|
42
|
+
- **Flag conflicts explicitly.** If a term is used ambiguously, call it out in "Flagged ambiguities" with a clear resolution.
|
|
43
|
+
- **Keep definitions tight.** One sentence max. Define what it IS, not what it does.
|
|
44
|
+
- **Show relationships.** Use bold term names and express cardinality where obvious.
|
|
45
|
+
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
|
46
|
+
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
|
47
|
+
- **Write an example dialogue.** A conversation between a dev and a domain expert that demonstrates how the terms interact naturally and clarifies boundaries between related concepts.
|
|
48
|
+
- **Edit inline as terminology shifts.** `CONTEXT.md` is updated whenever a term is renamed, retired, or newly disambiguated. The `grill-plan` skill will offer to update it during planning sessions, but anyone touching the vocabulary can edit it directly — stale terminology here is worse than missing terminology.
|
|
49
|
+
|
|
50
|
+
## Minimal example (single domain)
|
|
51
|
+
|
|
52
|
+
The opening example uses an Ordering/Billing domain to show cross-context structure. For a smaller project a `CONTEXT.md` may be just a handful of terms and no relationships:
|
|
53
|
+
|
|
54
|
+
```md
|
|
55
|
+
# Todo App
|
|
56
|
+
|
|
57
|
+
Vocabulary used across `internal/auth` and `internal/todos`.
|
|
58
|
+
|
|
59
|
+
## Language
|
|
60
|
+
|
|
61
|
+
**User**:
|
|
62
|
+
A person identified by an email address; owns todos and sessions.
|
|
63
|
+
_Avoid_: account, profile
|
|
64
|
+
|
|
65
|
+
**Sign-in token**:
|
|
66
|
+
A single-use, time-limited token emailed to a User to prove ownership of an email address.
|
|
67
|
+
_Avoid_: magic link (the *URL* containing the token), OTP, code
|
|
68
|
+
|
|
69
|
+
**Session**:
|
|
70
|
+
A long-lived bearer token issued after a sign-in token is consumed; authenticates subsequent requests.
|
|
71
|
+
_Avoid_: cookie, JWT (the format is a random bearer string, not either of those)
|
|
72
|
+
|
|
73
|
+
**Todo**:
|
|
74
|
+
An item on a User's personal list — text plus a done flag. Owned by exactly one User.
|
|
75
|
+
|
|
76
|
+
## Flagged ambiguities
|
|
77
|
+
|
|
78
|
+
- "token" alone is ambiguous — always qualify as **sign-in token** or **session** (bearer).
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Single vs multi-context repos
|
|
82
|
+
|
|
83
|
+
**Single context (most repos):** One `CONTEXT.md` at the repo root.
|
|
84
|
+
|
|
85
|
+
**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
86
|
+
|
|
87
|
+
```md
|
|
88
|
+
# Context Map
|
|
89
|
+
|
|
90
|
+
## Contexts
|
|
91
|
+
|
|
92
|
+
- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
|
|
93
|
+
- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
|
|
94
|
+
- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
|
|
95
|
+
|
|
96
|
+
## Relationships
|
|
97
|
+
|
|
98
|
+
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
|
99
|
+
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
|
100
|
+
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The skill infers which structure applies:
|
|
104
|
+
|
|
105
|
+
- If `CONTEXT-MAP.md` exists, read it to find contexts
|
|
106
|
+
- If only a root `CONTEXT.md` exists, single context
|
|
107
|
+
- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
|
|
108
|
+
|
|
109
|
+
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grill-plan
|
|
3
|
+
description: Grilling session that stress-tests a chosen plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use AFTER a direction has been picked (post-`investigate` or post-`feature-doc`), when the user wants to pressure-test the plan — triggered by phrases like "grill me on this", "stress-test this plan", "walk me through this", "is this consistent with our model". Skip if the direction is still being explored — use `investigate` instead.
|
|
4
|
+
complexity: medium
|
|
5
|
+
expected_duration: 20 minutes
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- A direction has been picked (post-`investigate` or post-`feature-doc`) and you want to pressure-test it against the existing model.
|
|
11
|
+
- Mid-`improve-codebase-architecture` — the grilling loop borrows this skill's discipline.
|
|
12
|
+
|
|
13
|
+
## When to skip
|
|
14
|
+
|
|
15
|
+
- The direction is still being explored — use [`investigate`](../investigate/SKILL.md) instead.
|
|
16
|
+
- The plan is fully exploratory and no decisions are being committed — also `investigate`.
|
|
17
|
+
- The decision is trivially reversible — there's nothing to stress-test.
|
|
18
|
+
- Greenfield repo with no `CONTEXT.md` / ADRs yet → run [`bootstrap`](../bootstrap/SKILL.md) first.
|
|
19
|
+
|
|
20
|
+
## What to do
|
|
21
|
+
|
|
22
|
+
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
|
|
23
|
+
|
|
24
|
+
Ask the questions one at a time, waiting for feedback on each question before continuing.
|
|
25
|
+
|
|
26
|
+
If a question can be answered by exploring the codebase, explore the codebase instead.
|
|
27
|
+
|
|
28
|
+
## Domain awareness
|
|
29
|
+
|
|
30
|
+
During codebase exploration, also look for existing documentation:
|
|
31
|
+
|
|
32
|
+
### File structure
|
|
33
|
+
|
|
34
|
+
Most repos have a single context:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
/
|
|
38
|
+
├── CONTEXT.md
|
|
39
|
+
├── docs/
|
|
40
|
+
│ └── adr/
|
|
41
|
+
│ ├── 0001-event-sourced-orders.md
|
|
42
|
+
│ └── 0002-postgres-for-write-model.md
|
|
43
|
+
└── src/
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
/
|
|
50
|
+
├── CONTEXT-MAP.md
|
|
51
|
+
├── docs/
|
|
52
|
+
│ └── adr/ ← system-wide decisions
|
|
53
|
+
├── src/
|
|
54
|
+
│ ├── ordering/
|
|
55
|
+
│ │ ├── CONTEXT.md
|
|
56
|
+
│ │ └── docs/adr/ ← context-specific decisions
|
|
57
|
+
│ └── billing/
|
|
58
|
+
│ ├── CONTEXT.md
|
|
59
|
+
│ └── docs/adr/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.
|
|
63
|
+
|
|
64
|
+
## During the session
|
|
65
|
+
|
|
66
|
+
### Challenge against the glossary
|
|
67
|
+
|
|
68
|
+
When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"
|
|
69
|
+
|
|
70
|
+
### Sharpen fuzzy language
|
|
71
|
+
|
|
72
|
+
When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."
|
|
73
|
+
|
|
74
|
+
### Discuss concrete scenarios
|
|
75
|
+
|
|
76
|
+
When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
|
|
77
|
+
|
|
78
|
+
### Cross-reference with code
|
|
79
|
+
|
|
80
|
+
When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"
|
|
81
|
+
|
|
82
|
+
### Update CONTEXT.md inline
|
|
83
|
+
|
|
84
|
+
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [`../formats/CONTEXT-FORMAT.md`](../formats/CONTEXT-FORMAT.md).
|
|
85
|
+
|
|
86
|
+
Don't couple `CONTEXT.md` to implementation details. Only include terms that are meaningful to domain experts.
|
|
87
|
+
|
|
88
|
+
### Offer ADRs sparingly
|
|
89
|
+
|
|
90
|
+
Only offer to create an ADR when all three are true:
|
|
91
|
+
|
|
92
|
+
1. **Hard to reverse** — the cost of changing your mind later is meaningful
|
|
93
|
+
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
|
|
94
|
+
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
|
|
95
|
+
|
|
96
|
+
If any of the three is missing, skip the ADR. Use the format in [`../formats/ADR-FORMAT.md`](../formats/ADR-FORMAT.md).
|
|
97
|
+
|
|
98
|
+
## Pairing with other skills
|
|
99
|
+
|
|
100
|
+
- **[`investigate`](../investigate/SKILL.md)** — runs *before* when direction is unclear. Once a direction is chosen, hand off here.
|
|
101
|
+
- **[`sync-check`](../sync-check/SKILL.md)** — escalates here when a terminology collision or ADR contradiction is found in existing code.
|
|
102
|
+
- **[`feature-doc`](../feature-doc/SKILL.md)** — runs *before* when a feature doc is the input to grilling, or *after* when grilling refines the plan into a feature doc.
|
|
103
|
+
- **[`system-design`](../system-design/SKILL.md)** — defers here for hard-to-reverse topology decisions.
|
|
104
|
+
- **[`improve-codebase-architecture`](../improve-codebase-architecture/SKILL.md)** — borrows this skill's grilling discipline in its own grilling loop.
|
|
105
|
+
- **[`tdd-rounds`](../tdd-rounds/SKILL.md)** — escalates here mid-round when an ADR is wobbling.
|
|
106
|
+
|
|
107
|
+
## Done when
|
|
108
|
+
|
|
109
|
+
- No open questions remain on the chosen plan.
|
|
110
|
+
- Every term used during the session has been resolved into `CONTEXT.md` (or explicitly flagged as ambiguous).
|
|
111
|
+
- Any decision that meets all three ADR criteria has been offered (and either accepted or declined).
|
|
112
|
+
- The user explicitly says "this is enough" or "let's move on" — don't keep grilling past their patience.
|