pi-matt-pocock 0.1.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/LICENSE +27 -0
- package/README.md +38 -0
- package/TODO.md +11 -0
- package/index.ts +1 -0
- package/package.json +44 -0
- package/procedures/ADR-FORMAT.md +47 -0
- package/procedures/AGENT-BRIEF.md +207 -0
- package/procedures/CONTEXT-FORMAT.md +60 -0
- package/procedures/DEEPENING.md +37 -0
- package/procedures/DESIGN-IT-TWICE.md +44 -0
- package/procedures/GLOSSARY-FORMAT.md +35 -0
- package/procedures/GLOSSARY.md +201 -0
- package/procedures/HTML-REPORT.md +123 -0
- package/procedures/LEARNING-RECORD-FORMAT.md +46 -0
- package/procedures/LOGIC.md +79 -0
- package/procedures/MISSION-FORMAT.md +31 -0
- package/procedures/OUT-OF-SCOPE.md +105 -0
- package/procedures/RESOURCES-FORMAT.md +32 -0
- package/procedures/SKILL-MECHANICS.md +22 -0
- package/procedures/UI.md +112 -0
- package/procedures/bdd-best-practices.md +68 -0
- package/procedures/bdd.md +98 -0
- package/procedures/code-review.md +111 -0
- package/procedures/codebase-design.md +113 -0
- package/procedures/diagnosing-bugs.md +139 -0
- package/procedures/domain-modeling.md +73 -0
- package/procedures/domain.md +51 -0
- package/procedures/gherkin-guide.md +109 -0
- package/procedures/grill-me.md +5 -0
- package/procedures/grill-with-docs.md +5 -0
- package/procedures/grilling.md +11 -0
- package/procedures/handoff.md +13 -0
- package/procedures/hitl-loop.template.sh +44 -0
- package/procedures/implement.md +13 -0
- package/procedures/improve-codebase-architecture.md +69 -0
- package/procedures/issue-tracker-github.md +45 -0
- package/procedures/issue-tracker-gitlab.md +46 -0
- package/procedures/issue-tracker-local.md +30 -0
- package/procedures/mocking.md +59 -0
- package/procedures/phase-boundaries.md +55 -0
- package/procedures/prototype.md +25 -0
- package/procedures/research.md +11 -0
- package/procedures/resolving-merge-conflicts.md +13 -0
- package/procedures/setup-matt-pocock-skills.md +114 -0
- package/procedures/tdd.md +60 -0
- package/procedures/teach.md +137 -0
- package/procedures/template.sh +204 -0
- package/procedures/testing-anti-patterns.md +37 -0
- package/procedures/tests.md +77 -0
- package/procedures/to-questionnaire.md +47 -0
- package/procedures/to-spec.md +77 -0
- package/procedures/to-tickets.md +97 -0
- package/procedures/triage-labels.md +15 -0
- package/procedures/triage.md +110 -0
- package/procedures/wait-what.md +1 -0
- package/procedures/wayfinder.md +127 -0
- package/procedures/wizard.md +39 -0
- package/procedures/writing-for-agents.md +76 -0
- package/procedures/writing-great-skills.md +81 -0
- package/src/index.ts +485 -0
- package/src/procedures.ts +33 -0
- package/src/workflow.ts +192 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Skill mechanics
|
|
2
|
+
|
|
3
|
+
The skill-specific branch of [`writing-for-agents`](writing-for-agents.md): what changes when the document is a skill — frontmatter, the invocation choice, and router skills. Everything else about writing it is the universal reference in `writing-for-agents.md`.
|
|
4
|
+
|
|
5
|
+
## Invocation
|
|
6
|
+
|
|
7
|
+
Two choices, trading the two loads:
|
|
8
|
+
|
|
9
|
+
- A **model-invoked** skill keeps a `description`, so the agent can fire it autonomously — and other skills can reach it. You can still type its name: model-invocation always _includes_ user reach; a description only ever adds agent discovery, never removes the human's. The description is the skill's top-level context pointer, forced to stay loaded at all times — permanent context load in exchange for discoverability. A model-invoked skill whose content is all reference is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Mechanics: omit `disable-model-invocation`, and write a model-facing description carrying the trigger branches (the pointer-writing rules in `writing-for-agents.md` apply in full).
|
|
10
|
+
- A **user-invoked** skill strips the description from the agent's reach: only the human typing its name can invoke it, and no other skill can. Zero context load, but it spends cognitive load — you are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
|
11
|
+
|
|
12
|
+
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
|
13
|
+
|
|
14
|
+
Shared reference that two user-invoked skills both need can live in neither — with no descriptions, neither can fire the other. Push it to a plain file outside the skill system: external reference any skill can point at.
|
|
15
|
+
|
|
16
|
+
## Splitting by invocation
|
|
17
|
+
|
|
18
|
+
The invocation cut of splitting (the sequence cut lives in `writing-for-agents.md`): split off a model-invoked skill when you have a distinct leading word that should trigger it on its own — a trigger word you actually use in your prompts — or another skill must reach it. You pay context load for the new always-loaded description, so that independent reach has to be worth it.
|
|
19
|
+
|
|
20
|
+
## Router skills
|
|
21
|
+
|
|
22
|
+
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each, so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no description, so nothing but the human can reach them.
|
package/procedures/UI.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# UI Prototype
|
|
2
|
+
|
|
3
|
+
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
|
|
4
|
+
|
|
5
|
+
If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md).
|
|
6
|
+
|
|
7
|
+
## When this is the right shape
|
|
8
|
+
|
|
9
|
+
- "What should this page look like?"
|
|
10
|
+
- "I want to see a few options for this dashboard before committing."
|
|
11
|
+
- "Try a different layout for the settings screen."
|
|
12
|
+
- Any time the user would otherwise spend a day picking between three vague mockups in their head.
|
|
13
|
+
|
|
14
|
+
## Two sub-shapes — strongly prefer sub-shape A
|
|
15
|
+
|
|
16
|
+
A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density. A throwaway route on its own is a vacuum: every variant looks fine in isolation. Default to sub-shape A whenever there's a plausible existing page to host the variants. Only reach for sub-shape B if the prototype genuinely has no nearby home.
|
|
17
|
+
|
|
18
|
+
### Sub-shape A — adjustment to an existing page (preferred)
|
|
19
|
+
|
|
20
|
+
The route already exists. Variants are rendered **on the same route**, gated by a `?variant=` URL search param. The existing data fetching, params, and auth all stay — only the rendering swaps. This is the default; pick it unless there's a specific reason not to.
|
|
21
|
+
|
|
22
|
+
If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A. Mount the variants inside the host page.
|
|
23
|
+
|
|
24
|
+
### Sub-shape B — a new page (last resort)
|
|
25
|
+
|
|
26
|
+
Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible.
|
|
27
|
+
|
|
28
|
+
Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure. Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename). Same `?variant=` pattern.
|
|
29
|
+
|
|
30
|
+
Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in? An empty route hides design problems that a populated one would expose.
|
|
31
|
+
|
|
32
|
+
In both sub-shapes the floating bottom bar is identical.
|
|
33
|
+
|
|
34
|
+
## Process
|
|
35
|
+
|
|
36
|
+
### 1. State the question and pick N
|
|
37
|
+
|
|
38
|
+
Default to **3 variants**. More than 5 stops being radically different and starts being noise — cap there.
|
|
39
|
+
|
|
40
|
+
Write down the plan in one line, in the prototype's location or a top-of-file comment:
|
|
41
|
+
|
|
42
|
+
> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route."
|
|
43
|
+
|
|
44
|
+
This works whether the user is here to push back or not.
|
|
45
|
+
|
|
46
|
+
### 2. Generate radically different variants
|
|
47
|
+
|
|
48
|
+
Draft each variant. Hold each one to:
|
|
49
|
+
|
|
50
|
+
- The page's purpose and the data it has access to.
|
|
51
|
+
- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever).
|
|
52
|
+
- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`.
|
|
53
|
+
|
|
54
|
+
Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours. Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper. If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance.
|
|
55
|
+
|
|
56
|
+
### 3. Wire them together
|
|
57
|
+
|
|
58
|
+
Create a single switcher component on the route:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// pseudo-code — adapt to the project's framework
|
|
62
|
+
const variant = searchParams.get('variant') ?? 'A';
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
{variant === 'A' && <VariantA {...data} />}
|
|
66
|
+
{variant === 'B' && <VariantB {...data} />}
|
|
67
|
+
{variant === 'C' && <VariantC {...data} />}
|
|
68
|
+
<PrototypeSwitcher variants={['A','B','C']} current={variant} />
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For sub-shape A (existing page): keep all the existing data fetching above the switcher; only the rendered subtree changes per variant.
|
|
74
|
+
|
|
75
|
+
For sub-shape B (new page): the throwaway route under `/prototype/<name>` mounts the same switcher.
|
|
76
|
+
|
|
77
|
+
### 4. Build the floating switcher
|
|
78
|
+
|
|
79
|
+
A small fixed-position bar at the bottom-centre of the screen with three pieces:
|
|
80
|
+
|
|
81
|
+
- **Left arrow** — cycles to the previous variant (wraps around).
|
|
82
|
+
- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`.
|
|
83
|
+
- **Right arrow** — cycles forward (wraps around).
|
|
84
|
+
|
|
85
|
+
Behaviour:
|
|
86
|
+
|
|
87
|
+
- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable.
|
|
88
|
+
- Keyboard: `←` and `→` arrow keys also cycle. Don't intercept arrow keys when an `<input>`, `<textarea>`, or `[contenteditable]` is focused.
|
|
89
|
+
- Visually distinct from the page (e.g. high-contrast pill, subtle shadow) so it's obviously not part of the design being evaluated.
|
|
90
|
+
- Hidden in production builds — gate on `process.env.NODE_ENV !== 'production'` or an equivalent check, so a stray prototype merge can't ship the bar to users.
|
|
91
|
+
|
|
92
|
+
Put the switcher in a single shared component so both sub-shapes can reuse it. Locate it wherever shared UI lives in the project.
|
|
93
|
+
|
|
94
|
+
### 5. Hand it over
|
|
95
|
+
|
|
96
|
+
Surface the URL (and the `?variant=` keys). The user will flip through whenever they get to it. The interesting feedback is usually **"I want the header from B with the sidebar from C"** — that's the actual design they want.
|
|
97
|
+
|
|
98
|
+
### 6. Capture the answer and clean up
|
|
99
|
+
|
|
100
|
+
Once a variant has won, capture the answer — which variant and why — then capture the prototype the way the [SKILL](prototype.md) describes. Fold the winner into the real code and move the rest onto the throwaway branch, not into main:
|
|
101
|
+
|
|
102
|
+
- **Sub-shape A** — fold the winner into the existing page; drop the losing variants and the switcher from main.
|
|
103
|
+
- **Sub-shape B** — promote the winning variant to a real route; drop the throwaway route and the switcher from main.
|
|
104
|
+
|
|
105
|
+
The full set of variants is the primary source, so it lands on the throwaway branch, not the bin — variant components and the switcher left in the main branch rot fast and confuse the next reader.
|
|
106
|
+
|
|
107
|
+
## Anti-patterns
|
|
108
|
+
|
|
109
|
+
- **Variants that differ only in colour or copy.** That's a tweak, not a prototype. Real variants disagree about structure.
|
|
110
|
+
- **Sharing too much code between variants.** A shared `<Header>` is fine; a shared `<Layout>` defeats the point. Each variant should be free to throw out the layout.
|
|
111
|
+
- **Wiring variants to real mutations.** Read-only prototypes are fine. If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
|
|
112
|
+
- **Promoting the prototype directly to production.** The variant code was written under prototype constraints (no tests, minimal error handling). Rewrite it properly when you fold it in.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Behavior-Driven Development (BDD) Best Practices
|
|
2
|
+
|
|
3
|
+
Behavior-Driven Development (BDD) is an agile software development process that encourages collaboration among developers, quality assurance testers, and customer representatives in a software project.
|
|
4
|
+
|
|
5
|
+
## 1. The Core Lifecycle: Discovery, Formulation, Automation
|
|
6
|
+
|
|
7
|
+
Modern BDD (2024+) emphasizes that BDD is about *conversations* first, automation second.
|
|
8
|
+
|
|
9
|
+
### Discovery (The "Three Amigos")
|
|
10
|
+
Before writing code or tests, the "Three Amigos" (Business/PO, Developer, Tester) meet to discuss the feature.
|
|
11
|
+
* **Goal:** Shared understanding.
|
|
12
|
+
* **Method:** Structured conversations, often using techniques like **Example Mapping**.
|
|
13
|
+
* **Output:** Concrete examples of how the system should behave.
|
|
14
|
+
|
|
15
|
+
### Formulation (Gherkin)
|
|
16
|
+
Convert those examples into structured scenarios using Gherkin syntax (Given/When/Then).
|
|
17
|
+
* **Goal:** Executable specifications / Living Documentation.
|
|
18
|
+
* **Standard:** Write *declarative* scenarios that describe business behavior, not UI implementation details.
|
|
19
|
+
* **Reference:** See [Gherkin Guide](./gherkin-guide.md).
|
|
20
|
+
|
|
21
|
+
### Automation (BDD-Driven TDD / Red-Green-Refactor)
|
|
22
|
+
Implement the scenarios using the Red-Green-Refactor cycle.
|
|
23
|
+
|
|
24
|
+
## 2. The Red-Green-Refactor Cycle
|
|
25
|
+
|
|
26
|
+
This is the engine of implementation.
|
|
27
|
+
|
|
28
|
+
### RED: Write a Failing Test
|
|
29
|
+
* **The Rule:** No production code is written without a failing test.
|
|
30
|
+
* **The Check:** Run the test. It *must* fail. If it passes, your test is broken or the feature already exists.
|
|
31
|
+
* **Best Practice:** Write small, targeted tests. Focus on one behavior at a time.
|
|
32
|
+
|
|
33
|
+
### GREEN: Make it Pass
|
|
34
|
+
* **The Goal:** Write the *minimal* amount of code to make the test pass.
|
|
35
|
+
* **The Mindset:** "Make it work." Do not optimize yet. Do not over-engineer.
|
|
36
|
+
* **YAGNI:** You Ain't Gonna Need It. Don't add fields or logic not required by the current test.
|
|
37
|
+
|
|
38
|
+
### REFACTOR: Make it Clean
|
|
39
|
+
* **The Goal:** Improve code structure without changing behavior.
|
|
40
|
+
* **The Safety Net:** The green test ensures you don't break functionality while cleaning up.
|
|
41
|
+
* **Actions:** Remove duplication, improve naming, extract methods, apply patterns.
|
|
42
|
+
|
|
43
|
+
## 3. General Best Practices
|
|
44
|
+
|
|
45
|
+
* **Shift Left:** Testing happens *during* development, not after.
|
|
46
|
+
* **Living Documentation:** Your feature files should be readable by business stakeholders and serve as the source of truth.
|
|
47
|
+
* **One Scenario, One Behavior:** Keep scenarios focused.
|
|
48
|
+
* **Integration:** Run BDD scenarios in your CI/CD pipeline.
|
|
49
|
+
* **Test Behavior, Not Implementation:** Tests should survive refactoring. If renaming a private variable breaks a test, the test was too coupled to implementation.
|
|
50
|
+
|
|
51
|
+
## 4. BDD-Driven TDD (Automation)
|
|
52
|
+
|
|
53
|
+
In this plugin, TDD is always **BDD-driven** — the Automation phase of the BDD lifecycle:
|
|
54
|
+
|
|
55
|
+
* **BDD** ([bdd](bdd.md)) — Discovery (conversations → examples) → Formulation (Gherkin scenarios) → Automation (BDD-driven red-green loop).
|
|
56
|
+
* **TDD** ([tdd](tdd.md)) — The BDD-driven Automation phase reference: test quality, seams, mocking, anti-patterns, and the red-green loop rules. It is invoked during [bdd](bdd.md) and [implement](implement.md) for test implementation guidance.
|
|
57
|
+
* **Usage:** BDD defines *what* to build (the Gherkin scenarios). BDD-driven TDD ensures the *implementation* is correct and robust via the red-green loop. They are a unified pipeline, not two practices.
|
|
58
|
+
|
|
59
|
+
## 5. Iron Law Exceptions
|
|
60
|
+
|
|
61
|
+
The Iron Law ("no production code without a failing test first") has exactly three legitimate exceptions — and even these should be raised with the user, not silently assumed:
|
|
62
|
+
|
|
63
|
+
* **One-off prototypes** — throwaway code answering a design question (see [prototype](prototype.md)). It will be deleted, not shipped, so a regression guard adds nothing. If the prototype survives into the real codebase, the Iron Law re-applies.
|
|
64
|
+
* **Generated code** — output of a code generator or scaffolder where the *generator* is the system under test, not the generated artifact. Test the generator's contract; the generated files are downstream artifacts, not hand-written behavior.
|
|
65
|
+
* **Config files** — declarative configuration (`.feature` step definitions aside) that carries no executable behavior of its own. A misconfigured file fails at load time or via an integration test; a unit test against its literal contents would be tautological.
|
|
66
|
+
|
|
67
|
+
Everything else writes a failing test first. "Too simple," "already verified manually," and "pragmatic not dogmatic" are rationalizations, not exceptions — see the Iron Law table in `bdd.md`.
|
|
68
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Behavior-Driven Development (BDD)
|
|
2
|
+
|
|
3
|
+
BDD is not just about tools; it's a methodology for shared understanding and high-quality implementation. This skill governs the **Discovery → Formulation → Automation** lifecycle: Gherkin scenarios define the behavior, and the Automation phase (red-green loop) is delegated to [tdd](tdd.md) (BDD-driven).
|
|
4
|
+
|
|
5
|
+
When exploring the codebase, read `CONTEXT.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
|
|
6
|
+
|
|
7
|
+
## How to Use This Skill
|
|
8
|
+
|
|
9
|
+
When the user asks for a feature, bug fix, or refactor, apply the following mindset:
|
|
10
|
+
|
|
11
|
+
1. **Understand Behavior First:** Do not start coding until you know *what* the system should do.
|
|
12
|
+
2. **Define Scenarios:** Create or ask for concrete examples (Gherkin) of the expected behavior.
|
|
13
|
+
3. **Drive Implementation with Tests:** Use the Red-Green-Refactor cycle.
|
|
14
|
+
|
|
15
|
+
## The BDD Cycle
|
|
16
|
+
|
|
17
|
+
The process flows from requirements to code:
|
|
18
|
+
|
|
19
|
+
* **Discovery:** Clarify requirements through examples (The "Three Amigos").
|
|
20
|
+
* **Formulation:** Write these examples as specific scenarios (Given/When/Then).
|
|
21
|
+
* **Automation:** Implement using the red-green-refactor cycle via [tdd](tdd.md), which governs test quality, seams, mocking, and anti-patterns.
|
|
22
|
+
|
|
23
|
+
See `./references/bdd-best-practices.md` for a detailed guide.
|
|
24
|
+
|
|
25
|
+
## Writing Scenarios (Gherkin)
|
|
26
|
+
|
|
27
|
+
Scenarios are your "Executable Specifications".
|
|
28
|
+
|
|
29
|
+
* Keep them declarative (business focus).
|
|
30
|
+
* Avoid technical jargon and UI details.
|
|
31
|
+
* One behavior per scenario.
|
|
32
|
+
* **Store executable scenarios in `.feature` files or the framework-native executable test format** — not as code comments. Once implementation begins, translate the scenarios that will be automated into `.feature` files or the framework-native executable test format, so they double as living documentation.
|
|
33
|
+
|
|
34
|
+
See `./references/gherkin-guide.md` for syntax and storage structure.
|
|
35
|
+
|
|
36
|
+
## Automation Phase
|
|
37
|
+
|
|
38
|
+
The Automation phase (red-green loop) is governed by the [tdd](tdd.md) skill (BDD-driven). When the Gherkin scenario is defined and the seam is agreed, invoke [tdd](tdd.md) for:
|
|
39
|
+
|
|
40
|
+
- **Seams** — where tests go at the public boundary
|
|
41
|
+
- **Test quality** — what makes a good test, anti-patterns
|
|
42
|
+
- **Mocking** — when to mock and at which boundaries
|
|
43
|
+
- **Red-green loop rules** — one slice at a time, refactor after green
|
|
44
|
+
|
|
45
|
+
## Seams — where tests go
|
|
46
|
+
|
|
47
|
+
Seams are the public boundaries you test at. See [tdd](tdd.md) for the full seam guidance.
|
|
48
|
+
|
|
49
|
+
**Test at the highest established seam.** Before writing a test, inspect the existing public boundaries, record the seam under test, and choose the highest one that expresses the confirmed behavior. Testing everything isn't possible — deliberately choosing the critical public seam is how effort lands on complex behavior instead of every edge case. Ask the user only when the behavior or public-contract choice is genuinely unresolved; otherwise begin the red-green loop immediately.
|
|
50
|
+
|
|
51
|
+
## What a good test is
|
|
52
|
+
|
|
53
|
+
Tests verify behavior through public interfaces, not implementation details. See [tdd](tdd.md) for the full guidance, examples, and anti-patterns.
|
|
54
|
+
|
|
55
|
+
## CRITICAL: The Iron Law
|
|
56
|
+
|
|
57
|
+
> **"No production code is written without a failing test first."**
|
|
58
|
+
|
|
59
|
+
The Red step MUST verify the test fails for the right reason (run the test and read the failure output) before writing any implementation. Skipping or rationalizing this step produces:
|
|
60
|
+
|
|
61
|
+
1. Tests that pass spuriously — you cannot tell if they are capable of failing.
|
|
62
|
+
2. Implementation-biased tests — they reflect the code that was written, not the behavior under contract.
|
|
63
|
+
3. Legacy code from day one — no behavioral safety net catches future regressions.
|
|
64
|
+
|
|
65
|
+
### If Production Code Already Exists
|
|
66
|
+
|
|
67
|
+
Delete it and re-derive it from a failing test — do not keep it "as reference," do not "adapt" it into the test-first version, do not read it while writing the test. Any of those re-introduces the implementation-biased-test failure mode above through the back door: a test written while looking at the code it's meant to constrain will pass on the first try regardless of whether it checks the right thing. Delete means delete.
|
|
68
|
+
|
|
69
|
+
### Common Rationalizations (reject all of these)
|
|
70
|
+
|
|
71
|
+
| Rationalization | Why it fails |
|
|
72
|
+
|---|---|
|
|
73
|
+
| "I'll write the test after — same coverage either way" | A test written against working code always passes on the first run. That proves the test doesn't crash, not that it verifies the right behavior. Only a test that failed first, for the stated reason, has been shown capable of catching a regression. |
|
|
74
|
+
| "I already manually verified it works" | Manual verification is not repeatable and leaves no regression guard. It answers "did this work once," not "will this keep working." |
|
|
75
|
+
| "This is too simple to need a test" | Simple code changes behavior just as easily as complex code. The Iron Law has no complexity threshold — it has the three named exceptions below and nothing else. |
|
|
76
|
+
| "I'll be pragmatic, not dogmatic, about BDD-driven TDD" | This is the rationalization, not an alternative to it. Every one of these tables' entries is someone being "pragmatic" about skipping the Red step. |
|
|
77
|
+
| "I already spent an hour on this, deleting it is wasteful" | Sunk cost. The hour is already spent whether you delete the code or keep it; keeping untested code doesn't recover that hour, it just adds an unverified regression risk on top of it. |
|
|
78
|
+
|
|
79
|
+
The only legitimate exceptions are named in `./references/bdd-best-practices.md` (one-off prototypes, generated code, config files) — and even those should be raised with the user, not silently assumed.
|
|
80
|
+
|
|
81
|
+
### Tests Written After the Fact Answer a Different Question
|
|
82
|
+
|
|
83
|
+
A test-first test encodes "this is what the system is contracted to do." A test-after test encodes "this is what the code I already wrote happens to do" — it will pass even if the code has the wrong behavior, because it was shaped to match that behavior rather than an independent specification. If you catch yourself writing a test against code you can already see, stop, delete the code, and write the test against the *behavior* instead.
|
|
84
|
+
|
|
85
|
+
## Rules of the loop
|
|
86
|
+
|
|
87
|
+
See [tdd](tdd.md) (BDD-driven) for the full red-green loop rules, anti-patterns, and test quality guidance. The key points:
|
|
88
|
+
|
|
89
|
+
- **Red before green.** Write the failing test first, then only enough code to pass it.
|
|
90
|
+
- **One slice at a time.** One seam, one test, one minimal implementation per cycle.
|
|
91
|
+
- **Refactoring is not part of the loop.** It belongs to the review stage (see [code-review](code-review.md)), not the red → green implementation cycle.
|
|
92
|
+
|
|
93
|
+
## References
|
|
94
|
+
|
|
95
|
+
- `./references/bdd-best-practices.md` - BDD methodology: discovery, formulation, automation
|
|
96
|
+
- `./references/gherkin-guide.md` - Gherkin syntax, storage structure, examples
|
|
97
|
+
- `./references/testing-anti-patterns.md` - Mocking pitfalls and vacuous-passing tests
|
|
98
|
+
- [tdd](tdd.md) - BDD-driven test implementation: seams, mocking, test quality, red-green loop
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
|
|
2
|
+
|
|
3
|
+
- **Standards** — does the code conform to this repo's documented coding standards?
|
|
4
|
+
- **Spec** — does the code faithfully implement the originating issue / PRD / spec? If the spec contains Gherkin scenarios (from [bdd](bdd.md)), those are the authoritative acceptance criteria.
|
|
5
|
+
|
|
6
|
+
When the teammate facility is available, delegate the two independent axes to separate teammates and aggregate their findings. Otherwise, perform the Standards review first, preserve its report, then perform the Spec review in the current context so the axes remain separate.
|
|
7
|
+
|
|
8
|
+
The issue tracker should have been provided to you — run [setup-matt-pocock-skills](setup-matt-pocock-skills.md) if `docs/agents/issue-tracker.md` is missing.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
### 1. Pin the fixed point
|
|
13
|
+
|
|
14
|
+
Whatever the user said is the fixed point — a commit SHA, branch name, tag, `main`, `HEAD~5`, etc. If they didn't specify one, ask for it.
|
|
15
|
+
|
|
16
|
+
Capture the diff command once: `git diff <fixed-point>...HEAD` (three-dot, so the comparison is against the merge-base). Also note the list of commits via `git log <fixed-point>..HEAD --oneline`.
|
|
17
|
+
|
|
18
|
+
Before going further, confirm the fixed point resolves (`git rev-parse <fixed-point>`) and the diff is non-empty. A bad ref or empty diff should fail here — not during either review.
|
|
19
|
+
|
|
20
|
+
### 2. Identify the spec source
|
|
21
|
+
|
|
22
|
+
Look for the originating spec, in this order:
|
|
23
|
+
|
|
24
|
+
1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.) — fetch via the workflow in `docs/agents/issue-tracker.md`.
|
|
25
|
+
2. A path the user passed as an argument.
|
|
26
|
+
3. A PRD/spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
|
|
27
|
+
4. If nothing is found, ask the user directly in the conversation where the spec is (in the repo / linked from the issue or PR / external document / no spec exists) and wait for the reply. If there isn't one, skip the **Spec** review and report "no spec available".
|
|
28
|
+
|
|
29
|
+
### 3. Identify the standards sources
|
|
30
|
+
|
|
31
|
+
Anything in the repo that documents how code should be written, such as `CODING_STANDARDS.md` or `CONTRIBUTING.md`.
|
|
32
|
+
|
|
33
|
+
On top of whatever the repo documents, the Standards axis always carries the **smell baseline** below — a fixed set of Fowler code smells (_Refactoring_, ch.3) that applies even when a repo documents nothing. Two rules bind it:
|
|
34
|
+
|
|
35
|
+
- **The repo overrides.** A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell.
|
|
36
|
+
- **Always a judgement call.** Each smell is a labelled heuristic ("possible Feature Envy"), never a hard violation — and, like any standard here, skip anything tooling already enforces.
|
|
37
|
+
|
|
38
|
+
Each smell reads *what it is* → *how to fix*; match it against the diff:
|
|
39
|
+
|
|
40
|
+
- **Mysterious Name** — a function, variable, or type whose name doesn't reveal what it does or holds. → rename it; if no honest name comes, the design's murky.
|
|
41
|
+
- **Duplicated Code** — the same logic shape appears in more than one hunk or file in the change. → extract the shared shape, call it from both.
|
|
42
|
+
- **Feature Envy** — a method that reaches into another object's data more than its own. → move the method onto the data it envies.
|
|
43
|
+
- **Data Clumps** — the same few fields or params keep travelling together (a type wanting to be born). → bundle them into one type, pass that.
|
|
44
|
+
- **Primitive Obsession** — a primitive or string standing in for a domain concept that deserves its own type. → give the concept its own small type.
|
|
45
|
+
- **Repeated Switches** — the same `switch`/`if`-cascade on the same type recurs across the change. → replace with polymorphism, or one map both sites share.
|
|
46
|
+
- **Shotgun Surgery** — one logical change forces scattered edits across many files in the diff. → gather what changes together into one module.
|
|
47
|
+
- **Divergent Change** — one file or module is edited for several unrelated reasons. → split so each module changes for one reason.
|
|
48
|
+
- **Speculative Generality** — abstraction, parameters, or hooks added for needs the spec doesn't have. → delete it; inline back until a real need shows.
|
|
49
|
+
- **Message Chains** — long `a.b().c().d()` navigation the caller shouldn't depend on. → hide the walk behind one method on the first object.
|
|
50
|
+
- **Middle Man** — a class or function that mostly just delegates onward. → cut it, call the real target direct.
|
|
51
|
+
- **Refused Bequest** — a subclass or implementer that ignores or overrides most of what it inherits. → drop the inheritance, use composition.
|
|
52
|
+
|
|
53
|
+
### 4. Run the two independent reviews
|
|
54
|
+
|
|
55
|
+
If the teammate facility is available, give each teammate one brief below and wait for both reports. Otherwise, run the Standards brief and then the Spec brief sequentially in the current context. Keep their notes separate until aggregation.
|
|
56
|
+
|
|
57
|
+
**Standards review brief** — include:
|
|
58
|
+
|
|
59
|
+
- The full diff command and commit list.
|
|
60
|
+
- The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full — the reviewer needs this material to evaluate the diff.
|
|
61
|
+
- The brief: "Report — per file/hunk where relevant — (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls — documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words."
|
|
62
|
+
|
|
63
|
+
**Spec review brief** — include:
|
|
64
|
+
|
|
65
|
+
- The diff command and commit list.
|
|
66
|
+
- The path or fetched contents of the spec.
|
|
67
|
+
- The brief: "Report: (a) Gherkin scenarios (Given/When/Then) in the spec that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. If the spec contains Gherkin scenarios, those are the authoritative acceptance criteria. Quote the spec line for each finding. Under 400 words."
|
|
68
|
+
|
|
69
|
+
If the spec is missing, skip the Spec review and note this in the final report.
|
|
70
|
+
|
|
71
|
+
### 5. Aggregate
|
|
72
|
+
|
|
73
|
+
Present the two reports under `## Standards` and `## Spec` headings, verbatim or lightly cleaned. Do **not** merge or rerank findings — the two axes are deliberately separate (see _Why two axes_).
|
|
74
|
+
|
|
75
|
+
End with a one-line summary: total findings per axis, and the worst issue _within each axis_ (if any). Don't pick a single winner across axes — that's the reranking the separation exists to prevent.
|
|
76
|
+
|
|
77
|
+
### 5.4 Memory read-before (Standards axis)
|
|
78
|
+
|
|
79
|
+
Before scoring, load the project's known traps: `Glob docs/memory/*.md`, then read every file whose frontmatter `category` is `pitfall` or `convention` and whose `summary` is topically related to this review (the diff's files, patterns, or stack). These are evaluator-verified failure modes this repo already paid for.
|
|
80
|
+
|
|
81
|
+
In step 5.5's Standards scoring, treat a read memory file as a red flag: if the diff exhibits a known pitfall, record it as a FAIL in the Standards report with evidence citing the memory file (`docs/memory/pitfall_<slug>.md`). If `docs/memory/` does not exist, skip this step.
|
|
82
|
+
|
|
83
|
+
### 5.5 Binary verdict + refute-before-PASS
|
|
84
|
+
|
|
85
|
+
After presenting the two reports, emit a binary verdict per axis:
|
|
86
|
+
|
|
87
|
+
- **Standards axis**: binary PASS (no unresolved standard violations or smells in the Standards report) / REWORK (any standard violation or listed smell).
|
|
88
|
+
- **Spec axis**: binary PASS (all Gherkin scenarios in the spec demonstrably implemented) / REWORK (any scenario missing/partial/wrong).
|
|
89
|
+
|
|
90
|
+
**Refute-before-PASS (red-team protocol):** Before assigning PASS to either axis, attempt to refute your own PASS:
|
|
91
|
+
|
|
92
|
+
1. State the strongest reason the work might FAIL this axis despite looking green.
|
|
93
|
+
2. Cite the specific evidence (diff hunk or missing scenario) that would confirm that failure.
|
|
94
|
+
3. Only if that failure case is itself refuted by concrete contrary evidence does PASS hold.
|
|
95
|
+
|
|
96
|
+
A PASS without an attempted refutation is invalid — downgrade to REWORK and list the un-refuted risk.
|
|
97
|
+
|
|
98
|
+
**Final verdict:** REWORK if either axis is REWORK; PASS only if both axes PASS (post-refutation).
|
|
99
|
+
|
|
100
|
+
## CRITICAL: Refute-before-PASS
|
|
101
|
+
|
|
102
|
+
A PASS without an attempted refutation is invalid. Before assigning PASS to either axis, state the strongest reason the work might still fail, cite the evidence (diff hunk or missing scenario) that would confirm it, and hold PASS only when concrete contrary evidence refutes it. Never merge or rerank the two axes — the separation exists to stop one axis masking the other.
|
|
103
|
+
|
|
104
|
+
## Why two axes
|
|
105
|
+
|
|
106
|
+
A change can pass one axis and fail the other:
|
|
107
|
+
|
|
108
|
+
- Code that follows every standard but implements the wrong thing → **Standards pass, Spec fail.**
|
|
109
|
+
- Code that does exactly what the issue asked but breaks the project's conventions → **Spec pass, Standards fail.**
|
|
110
|
+
|
|
111
|
+
Reporting them separately stops one axis from masking the other.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Codebase Design
|
|
2
|
+
|
|
3
|
+
Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.
|
|
4
|
+
|
|
5
|
+
## Glossary
|
|
6
|
+
|
|
7
|
+
Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
|
|
8
|
+
|
|
9
|
+
**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service.
|
|
10
|
+
|
|
11
|
+
**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface).
|
|
12
|
+
|
|
13
|
+
**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
|
|
14
|
+
|
|
15
|
+
**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation.
|
|
16
|
+
|
|
17
|
+
**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context).
|
|
18
|
+
|
|
19
|
+
**Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside).
|
|
20
|
+
|
|
21
|
+
**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.
|
|
22
|
+
|
|
23
|
+
**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.
|
|
24
|
+
|
|
25
|
+
## CRITICAL: Use the vocabulary exactly
|
|
26
|
+
|
|
27
|
+
Say **module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality** — never "component", "service", "API", or "boundary". Consistent language is the whole point of this skill; substitution breaks the shared model every other skill relies on.
|
|
28
|
+
|
|
29
|
+
## Deep vs shallow
|
|
30
|
+
|
|
31
|
+
**Deep module** = small interface + lots of implementation:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌─────────────────────┐
|
|
35
|
+
│ Small Interface │ ← Few methods, simple params
|
|
36
|
+
├─────────────────────┤
|
|
37
|
+
│ │
|
|
38
|
+
│ Deep Implementation│ ← Complex logic hidden
|
|
39
|
+
│ │
|
|
40
|
+
└─────────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Shallow module** = large interface + little implementation (avoid):
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌─────────────────────────────────┐
|
|
47
|
+
│ Large Interface │ ← Many methods, complex params
|
|
48
|
+
├─────────────────────────────────┤
|
|
49
|
+
│ Thin Implementation │ ← Just passes through
|
|
50
|
+
└─────────────────────────────────┘
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
When designing an interface, ask:
|
|
54
|
+
|
|
55
|
+
- Can I reduce the number of methods?
|
|
56
|
+
- Can I simplify the parameters?
|
|
57
|
+
- Can I hide more complexity inside?
|
|
58
|
+
|
|
59
|
+
## Principles
|
|
60
|
+
|
|
61
|
+
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
|
|
62
|
+
- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
|
|
63
|
+
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape.
|
|
64
|
+
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
|
|
65
|
+
|
|
66
|
+
## Designing for testability
|
|
67
|
+
|
|
68
|
+
Good interfaces make testing natural:
|
|
69
|
+
|
|
70
|
+
1. **Accept dependencies, don't create them.**
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Testable
|
|
74
|
+
function processOrder(order, paymentGateway) {}
|
|
75
|
+
|
|
76
|
+
// Hard to test
|
|
77
|
+
function processOrder(order) {
|
|
78
|
+
const gateway = new StripeGateway();
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
2. **Return results, don't produce side effects.**
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Testable
|
|
86
|
+
function calculateDiscount(cart): Discount {}
|
|
87
|
+
|
|
88
|
+
// Hard to test
|
|
89
|
+
function applyDiscount(cart): void {
|
|
90
|
+
cart.total -= discount;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup.
|
|
95
|
+
|
|
96
|
+
## Relationships
|
|
97
|
+
|
|
98
|
+
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
|
|
99
|
+
- **Depth** is a property of a **Module**, measured against its **Interface**.
|
|
100
|
+
- A **Seam** is where a **Module**'s **Interface** lives.
|
|
101
|
+
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
|
|
102
|
+
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
|
|
103
|
+
|
|
104
|
+
## Rejected framings
|
|
105
|
+
|
|
106
|
+
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
|
|
107
|
+
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
|
|
108
|
+
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.
|
|
109
|
+
|
|
110
|
+
## Going deeper
|
|
111
|
+
|
|
112
|
+
- **Deepening a cluster given its dependencies** — see [DEEPENING.md](DEEPENING.md): dependency categories, seam discipline, and replace-don't-layer testing.
|
|
113
|
+
- **Exploring alternative interfaces** — see [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md): use teammates when the teammate facility is available, otherwise develop several designs sequentially, then compare depth, locality, and seam placement.
|