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.
Files changed (62) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +38 -0
  3. package/TODO.md +11 -0
  4. package/index.ts +1 -0
  5. package/package.json +44 -0
  6. package/procedures/ADR-FORMAT.md +47 -0
  7. package/procedures/AGENT-BRIEF.md +207 -0
  8. package/procedures/CONTEXT-FORMAT.md +60 -0
  9. package/procedures/DEEPENING.md +37 -0
  10. package/procedures/DESIGN-IT-TWICE.md +44 -0
  11. package/procedures/GLOSSARY-FORMAT.md +35 -0
  12. package/procedures/GLOSSARY.md +201 -0
  13. package/procedures/HTML-REPORT.md +123 -0
  14. package/procedures/LEARNING-RECORD-FORMAT.md +46 -0
  15. package/procedures/LOGIC.md +79 -0
  16. package/procedures/MISSION-FORMAT.md +31 -0
  17. package/procedures/OUT-OF-SCOPE.md +105 -0
  18. package/procedures/RESOURCES-FORMAT.md +32 -0
  19. package/procedures/SKILL-MECHANICS.md +22 -0
  20. package/procedures/UI.md +112 -0
  21. package/procedures/bdd-best-practices.md +68 -0
  22. package/procedures/bdd.md +98 -0
  23. package/procedures/code-review.md +111 -0
  24. package/procedures/codebase-design.md +113 -0
  25. package/procedures/diagnosing-bugs.md +139 -0
  26. package/procedures/domain-modeling.md +73 -0
  27. package/procedures/domain.md +51 -0
  28. package/procedures/gherkin-guide.md +109 -0
  29. package/procedures/grill-me.md +5 -0
  30. package/procedures/grill-with-docs.md +5 -0
  31. package/procedures/grilling.md +11 -0
  32. package/procedures/handoff.md +13 -0
  33. package/procedures/hitl-loop.template.sh +44 -0
  34. package/procedures/implement.md +13 -0
  35. package/procedures/improve-codebase-architecture.md +69 -0
  36. package/procedures/issue-tracker-github.md +45 -0
  37. package/procedures/issue-tracker-gitlab.md +46 -0
  38. package/procedures/issue-tracker-local.md +30 -0
  39. package/procedures/mocking.md +59 -0
  40. package/procedures/phase-boundaries.md +55 -0
  41. package/procedures/prototype.md +25 -0
  42. package/procedures/research.md +11 -0
  43. package/procedures/resolving-merge-conflicts.md +13 -0
  44. package/procedures/setup-matt-pocock-skills.md +114 -0
  45. package/procedures/tdd.md +60 -0
  46. package/procedures/teach.md +137 -0
  47. package/procedures/template.sh +204 -0
  48. package/procedures/testing-anti-patterns.md +37 -0
  49. package/procedures/tests.md +77 -0
  50. package/procedures/to-questionnaire.md +47 -0
  51. package/procedures/to-spec.md +77 -0
  52. package/procedures/to-tickets.md +97 -0
  53. package/procedures/triage-labels.md +15 -0
  54. package/procedures/triage.md +110 -0
  55. package/procedures/wait-what.md +1 -0
  56. package/procedures/wayfinder.md +127 -0
  57. package/procedures/wizard.md +39 -0
  58. package/procedures/writing-for-agents.md +76 -0
  59. package/procedures/writing-great-skills.md +81 -0
  60. package/src/index.ts +485 -0
  61. package/src/procedures.ts +33 -0
  62. package/src/workflow.ts +192 -0
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # A wizard — walks a human through a manual procedure step by step.
4
+ # Generated by the /wizard skill.
5
+ #
6
+ # Everything above the "STAGES" marker is the wizard library: do not hand-edit
7
+ # it. Author the per-step stages below the marker.
8
+
9
+ set -euo pipefail
10
+
11
+ # ──────────────────────────────────────────────────────────────────────────
12
+ # Wizard library — delightful, consistent UX. Identical across every wizard.
13
+ # ──────────────────────────────────────────────────────────────────────────
14
+
15
+ if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
16
+ BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
17
+ BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
18
+ else
19
+ BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
20
+ fi
21
+
22
+ # Author sets this at the top of the stages section.
23
+ TOTAL_STAGES=0
24
+
25
+ _STAGE_INDEX=0
26
+ ENV_FILE="${ENV_FILE:-.env}"
27
+ WRITTEN_ENV=() # KEYs written to ENV_FILE this run
28
+ WRITTEN_SECRET=() # secret NAMEs set this run
29
+ SKIPPED=() # things we couldn't do (e.g. gh missing)
30
+
31
+ # _clear — wipe the terminal so only the current step is on screen. No-op when
32
+ # output isn't a terminal, so piped logs stay readable.
33
+ _clear() {
34
+ [[ -t 1 ]] || return 0
35
+ if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
36
+ }
37
+
38
+ # banner "Title" — opening frame: what this wizard does.
39
+ banner() {
40
+ _clear
41
+ printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
42
+ printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
43
+ printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
44
+ printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
45
+ printf ' later — it remembers values already saved.%s\n' "$RESET"
46
+ pause "Ready to start?"
47
+ }
48
+
49
+ # stage "Name" — clear the screen, then announce a stage and show progress.
50
+ # Clearing keeps only the current step on screen.
51
+ stage() {
52
+ _clear
53
+ _STAGE_INDEX=$((_STAGE_INDEX + 1))
54
+ printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
55
+ "$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
56
+ }
57
+
58
+ # say "..." — a plain instruction line.
59
+ say() { printf ' %s\n' "$1"; }
60
+ # step "..." — a numbered-feeling action the human takes in the browser.
61
+ step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
62
+ note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
63
+ warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
64
+
65
+ # open_url URL — open in the human's browser, cross-platform incl. WSL.
66
+ open_url() {
67
+ local url="$1"
68
+ printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
69
+ { if command -v wslview >/dev/null 2>&1; then wslview "$url"
70
+ elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
71
+ elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
72
+ elif command -v open >/dev/null 2>&1; then open "$url"
73
+ else warn "couldn't open a browser — visit it manually: $url"; fi
74
+ } >/dev/null 2>&1 || warn "couldn't open a browser — visit it manually: $url"
75
+ }
76
+
77
+ # pause "msg" — wait for the human to confirm they've done the manual part.
78
+ pause() {
79
+ printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
80
+ read -r _ || true
81
+ }
82
+
83
+ # confirm "question" — y/N gate; returns success on yes.
84
+ confirm() {
85
+ local reply=""
86
+ printf ' %s? %s [y/N] ' "$YELLOW" "$1"
87
+ read -r reply || true
88
+ [[ "$reply" =~ ^[Yy] ]]
89
+ }
90
+
91
+ # _existing KEY — current value of KEY in ENV_FILE, if any.
92
+ _existing() {
93
+ [[ -f "$ENV_FILE" ]] || return 1
94
+ local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
95
+ printf '%s' "${line#*=}"
96
+ }
97
+
98
+ # ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
99
+ # a default on re-runs (Enter keeps it). Visible input (non-secret).
100
+ ask() {
101
+ local key="$1" prompt="$2" current input
102
+ current=$(_existing "$key" || true)
103
+ if [[ -n "$current" ]]; then
104
+ printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
105
+ else
106
+ printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
107
+ fi
108
+ read -r input || true
109
+ [[ -z "$input" && -n "$current" ]] && input="$current"
110
+ printf -v "$key" '%s' "$input"
111
+ }
112
+
113
+ # ask_secret KEY "Prompt" — like ask, but input is hidden.
114
+ ask_secret() {
115
+ local key="$1" prompt="$2" current input
116
+ current=$(_existing "$key" || true)
117
+ if [[ -n "$current" ]]; then
118
+ printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
119
+ else
120
+ printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
121
+ fi
122
+ read -rs input || true
123
+ printf '\n'
124
+ [[ -z "$input" && -n "$current" ]] && input="$current"
125
+ printf -v "$key" '%s' "$input"
126
+ }
127
+
128
+ # write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
129
+ # any existing line). Idempotent.
130
+ write_env() {
131
+ local key="$1" value="$2" tmp
132
+ touch "$ENV_FILE"
133
+ tmp=$(mktemp)
134
+ grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
135
+ printf '%s=%s\n' "$key" "$value" >> "$tmp"
136
+ mv "$tmp" "$ENV_FILE"
137
+ WRITTEN_ENV+=("$key")
138
+ printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
139
+ }
140
+
141
+ # set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
142
+ # to a warning (and records it) if gh is unavailable or unauthenticated.
143
+ set_secret() {
144
+ local name="$1" value="$2"
145
+ if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
146
+ if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
147
+ WRITTEN_SECRET+=("$name")
148
+ printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
149
+ return
150
+ fi
151
+ fi
152
+ SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
153
+ warn "skipped GitHub secret $name — gh not ready; set it later"
154
+ }
155
+
156
+ # set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
157
+ set_var() {
158
+ local name="$1" value="$2"
159
+ if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
160
+ if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
161
+ printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
162
+ return
163
+ fi
164
+ fi
165
+ SKIPPED+=("GitHub variable $name")
166
+ warn "skipped GitHub variable $name — gh not ready; set it later"
167
+ }
168
+
169
+ # finish — clear, then a closing summary of everything configured.
170
+ finish() {
171
+ _clear
172
+ printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
173
+ (( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
174
+ (( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
175
+ if (( ${#SKIPPED[@]} )); then
176
+ printf '\n'; warn "still to do by hand:"
177
+ for s in "${SKIPPED[@]}"; do note " - $s"; done
178
+ fi
179
+ printf '\n'
180
+ }
181
+
182
+ # ──────────────────────────────────────────────────────────────────────────
183
+ # STAGES — author this section. One stage() per step the human takes.
184
+ # Replace the example below. Set TOTAL_STAGES to match the stages you write.
185
+ # ──────────────────────────────────────────────────────────────────────────
186
+
187
+ TOTAL_STAGES=1
188
+
189
+ banner "Stripe setup"
190
+
191
+ # ── Example stage: replace with your real steps ───────────────────────────
192
+ stage "Stripe — API keys"
193
+ say "We'll grab your Stripe test keys and store them for local dev + CI."
194
+ open_url "https://dashboard.stripe.com/test/apikeys"
195
+ step "On the API keys page, copy the Publishable key (starts pk_test_)."
196
+ ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
197
+ step "Click 'Reveal test key' on the Secret key row, then copy it."
198
+ ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
199
+ write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
200
+ write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
201
+ set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
202
+ # ──────────────────────────────────────────────────────────────────────────
203
+
204
+ finish
@@ -0,0 +1,37 @@
1
+ # Testing Anti-Patterns
2
+
3
+ A test can pass and still verify nothing. This reference lists the ways that happens, so a passing test suite isn't mistaken for evidence of correctness.
4
+
5
+ ## Mocking the Thing Under Test
6
+
7
+ If the test mocks the exact function or method whose behavior is in question, the test can only ever prove the mock was called — not that the real implementation does the right thing. Mock the *collaborators* of the unit under test (the database call, the network request, the clock), never the unit itself.
8
+
9
+ | Symptom | Fix |
10
+ |---|---|
11
+ | Test asserts `mockAuthenticate.calledWith(...)` and nothing else | Call the real `authenticate()` against a real or fake-but-faithful collaborator and assert on its return value / side effect |
12
+ | Test mocks the repository method that the task is implementing | Use an in-memory or test-double repository that behaves like the real one, not a mock of the method being written |
13
+
14
+ ## Testing Implementation Details Instead of Behavior
15
+
16
+ A test that asserts on private internals, call counts to helper functions, or the exact sequence of internal steps breaks every time the implementation is refactored — even when the observable behavior is unchanged. This punishes REFACTOR-step cleanup and creates pressure to skip it.
17
+
18
+ | Symptom | Fix |
19
+ |---|---|
20
+ | Test asserts `helperFn` was called exactly twice | Assert on the caller's return value or externally observable side effect instead |
21
+ | Test reaches into a private field to check intermediate state | Assert on the public API's output for a given input |
22
+
23
+ ## Over-Mocking Collapses the Test to a Tautology
24
+
25
+ When every collaborator is mocked to return canned values that exactly match what the assertion expects, the test verifies that the mocks were configured correctly — a tautology — not that the code under test does anything with them. A rule of thumb: if you can't state a way the test could fail short of a syntax error, it's testing the mocks, not the code.
26
+
27
+ ## Mocking Something You Don't Understand
28
+
29
+ Do not mock a dependency's interface from memory or guesswork. An incorrect mock (wrong return shape, wrong error type, wrong async behavior) makes the test pass against a fictional version of the dependency, hiding real integration bugs until production. Read the real dependency's interface/contract before mocking it, or prefer a real instance / official test double when one exists.
30
+
31
+ ## Trivial or Vacuous Assertions
32
+
33
+ A test with no assertion, or an assertion that is always true (`assert true`, `expect(x).toBeDefined()` on a value that's always defined), passes regardless of behavior. This is functionally equivalent to no test at all, but it inflates coverage numbers and gives false confidence. Every test must assert a specific, falsifiable expected value.
34
+
35
+ ## Relationship to the Iron Law
36
+
37
+ All of the above can slip through the Red step if the failing-test check is shallow — a test can "fail" for the wrong reason (e.g. a mock isn't wired up yet) and still be vacuous once the wiring is in place. Verifying RED means confirming the failure message describes the *missing behavior*, not just that the test runner reported non-zero.
@@ -0,0 +1,77 @@
1
+ # Good and Bad Tests
2
+
3
+ ## Good Tests
4
+
5
+ **Integration-style**: Test through real interfaces, not mocks of internal parts.
6
+
7
+ ```typescript
8
+ // GOOD: Tests observable behavior
9
+ test("user can checkout with valid cart", async () => {
10
+ const cart = createCart();
11
+ cart.add(product);
12
+ const result = await checkout(cart, paymentMethod);
13
+ expect(result.status).toBe("confirmed");
14
+ });
15
+ ```
16
+
17
+ Characteristics:
18
+
19
+ - Tests behavior users/callers care about
20
+ - Uses public API only
21
+ - Survives internal refactors
22
+ - Describes WHAT, not HOW
23
+ - One logical assertion per test
24
+
25
+ ## Bad Tests
26
+
27
+ **Implementation-detail tests**: Coupled to internal structure.
28
+
29
+ ```typescript
30
+ // BAD: Tests implementation details
31
+ test("checkout calls paymentService.process", async () => {
32
+ const mockPayment = jest.mock(paymentService);
33
+ await checkout(cart, payment);
34
+ expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
35
+ });
36
+ ```
37
+
38
+ Red flags:
39
+
40
+ - Mocking internal collaborators
41
+ - Testing private methods
42
+ - Asserting on call counts/order
43
+ - Test breaks when refactoring without behavior change
44
+ - Test name describes HOW not WHAT
45
+ - Verifying through external means instead of interface
46
+
47
+ ```typescript
48
+ // BAD: Bypasses interface to verify
49
+ test("createUser saves to database", async () => {
50
+ await createUser({ name: "Alice" });
51
+ const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
52
+ expect(row).toBeDefined();
53
+ });
54
+
55
+ // GOOD: Verifies through interface
56
+ test("createUser makes user retrievable", async () => {
57
+ const user = await createUser({ name: "Alice" });
58
+ const retrieved = await getUser(user.id);
59
+ expect(retrieved.name).toBe("Alice");
60
+ });
61
+ ```
62
+
63
+ **Tautological tests**: Expected value restates the implementation, so the test passes by construction.
64
+
65
+ ```typescript
66
+ // BAD: Expected value is recomputed the way the code computes it
67
+ test("calculateTotal sums line items", () => {
68
+ const items = [{ price: 10 }, { price: 5 }];
69
+ const expected = items.reduce((sum, i) => sum + i.price, 0);
70
+ expect(calculateTotal(items)).toBe(expected);
71
+ });
72
+
73
+ // GOOD: Expected value is an independent, known literal
74
+ test("calculateTotal sums line items", () => {
75
+ expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
76
+ });
77
+ ```
@@ -0,0 +1,47 @@
1
+ Turn something the user can't answer alone into a **questionnaire** — a Markdown document they hand to one person to fill in async, or fill out together over a meeting. The recipient holds knowledge the user lacks; the questionnaire pulls it out of them.
2
+
3
+ **Grill the send, not the subject.** Interview the user only about the _send_, which they can always answer: who it goes to, and what they need back. The questions in the document then target the **gap** between what the recipient knows and what the user needs.
4
+
5
+ 1. **Who is it going to?** Ask, in one exchange, the recipient's role, expertise, and relationship to the user. This fixes the questionnaire's tone and how much context it must carry. Done when you know who the recipient is and what they know that the user doesn't.
6
+
7
+ 2. **What do you need back?** Ask, in one exchange, the specific decisions or facts the user can't resolve alone and needs from this person. Done when you have a concrete list of what the user must walk away able to do or decide.
8
+
9
+ 3. **Write the questionnaire.** Draft questions aimed at the gap from steps 1–2, following the Document structure below. Write it to `to-questionnaire-<slug>.md` in the current directory (slug from the topic) and report the path. Done when the file exists and every item the user named in step 2 is covered by a question.
10
+
11
+ ## Document structure
12
+
13
+ Frame the document as a **discovery questionnaire**: the user lacks context, the recipient holds it. Order questions most-important-first — async means you may only get one pass — and group them under `##` headings by theme once there are more than a handful. Write it using the template below.
14
+
15
+ <questionnaire-template>
16
+
17
+ # <Questionnaire title>
18
+
19
+ **Purpose:** why this questionnaire exists and the decision riding on it.
20
+
21
+ **From:** <the user> — **To:** <the recipient> — **How your answers will be used:** <where they go>
22
+
23
+ ## Context
24
+
25
+ One paragraph orienting a recipient who wasn't in the user's head. Enough to answer well, not a page.
26
+
27
+ ## How to answer
28
+
29
+ Deadline and rough effort. Partial answers and "I don't know" are useful — flag anything you're unsure of rather than skipping it.
30
+
31
+ ## <Theme heading>
32
+
33
+ One `##` section per theme. Under each, its questions, most-important-first. Every question is one idea — never compound — with an answer stub directly beneath, and a one-line _why this matters_ only where the question could be misread or invite a throwaway answer.
34
+
35
+ <question-example>
36
+ ### What load is the system expected to handle at launch?
37
+
38
+ _Why this matters: it decides whether we provision for burst traffic now or defer it._
39
+
40
+ >
41
+ </question-example>
42
+
43
+ ## Anything else?
44
+
45
+ A closing catch-all: anything we didn't ask that we should know?
46
+
47
+ </questionnaire-template>
@@ -0,0 +1,77 @@
1
+ This skill takes the current conversation context and codebase understanding and produces a spec (you may know this document as a PRD). Do NOT interview the user — just synthesize what you already know.
2
+
3
+ The issue tracker and triage label vocabulary should have been provided to you — run [setup-matt-pocock-skills](setup-matt-pocock-skills.md) if not.
4
+
5
+ ## CRITICAL: No interview — synthesize what you already know
6
+
7
+ Do NOT interview the user; the spec is a synthesis of the conversation and codebase understanding already in context. Test at the highest existing seam, preferring one seam to many. Ask only if the existing context leaves the public contract genuinely ambiguous.
8
+
9
+ ## Process
10
+
11
+ 1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the spec, and respect any ADRs in the area you're touching.
12
+
13
+ 2. Sketch out the seams at which you're going to test the feature. Existing seams should be preferred to new ones. Use the highest seam possible. If new seams are needed, propose them at the highest point you can. The fewer seams across the codebase, the better - the ideal number is one.
14
+
15
+ Record the selected seams in the spec and continue directly. Ask only if the public contract remains genuinely ambiguous after exploration.
16
+
17
+ 3. Write the spec using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage.
18
+
19
+ <spec-template>
20
+
21
+ ## Problem Statement
22
+
23
+ The problem that the user is facing, from the user's perspective.
24
+
25
+ ## Solution
26
+
27
+ The solution to the problem, from the user's perspective.
28
+
29
+ ## User Stories
30
+
31
+ A LONG, numbered list of user stories. Each user story should be in the format of:
32
+
33
+ 1. As an <actor>, I want a <feature>, so that <benefit>
34
+
35
+ <user-story-example>
36
+ 1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
37
+ </user-story-example>
38
+
39
+ This list of user stories should be extremely extensive and cover all aspects of the feature.
40
+
41
+ ## Scenarios
42
+
43
+ Executable scenarios derived from the user stories above, written as Gherkin (Given/When/Then). Each scenario pins one behavior; collectively they are the executable specification the implementation is verified against. See [bdd](bdd.md) for the scenario authoring guide and the `.feature` storage convention.
44
+
45
+ ## Implementation Decisions
46
+
47
+ A list of implementation decisions that were made. This can include:
48
+
49
+ - The modules that will be built/modified
50
+ - The interfaces of those modules that will be modified
51
+ - Technical clarifications from the developer
52
+ - Architectural decisions
53
+ - Schema changes
54
+ - API contracts
55
+ - Specific interactions
56
+
57
+ Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
58
+
59
+ Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
60
+
61
+ ## Testing Decisions
62
+
63
+ A list of testing decisions that were made. Include:
64
+
65
+ - A description of what makes a good test (only test external behavior, not implementation details)
66
+ - Which modules will be tested
67
+ - Prior art for the tests (i.e. similar types of tests in the codebase)
68
+
69
+ ## Out of Scope
70
+
71
+ A description of the things that are out of scope for this spec.
72
+
73
+ ## Further Notes
74
+
75
+ Any further notes about the feature.
76
+
77
+ </spec-template>
@@ -0,0 +1,97 @@
1
+ # To Tickets
2
+
3
+ Break a plan, spec, or conversation into a set of **tickets** — tracer-bullet vertical slices, each declaring the tickets that **block** it. If the spec contains Gherkin scenarios (from [bdd](bdd.md)), each ticket should map to one or more scenarios; the scenarios are the authoritative acceptance criteria.
4
+
5
+ The issue tracker and triage label vocabulary should have been provided to you — run [setup-matt-pocock-skills](setup-matt-pocock-skills.md) if not.
6
+
7
+ ## Process
8
+
9
+ ### 1. Gather context
10
+
11
+ Work from whatever is already in the conversation context. If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.
12
+
13
+ ### 2. Explore the codebase (optional)
14
+
15
+ If you have not already explored the codebase, do so to understand the current state of the code. Ticket titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
16
+
17
+ Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
18
+
19
+ ### 3. Draft vertical slices
20
+
21
+ Break the work into **tracer bullet** tickets.
22
+
23
+ <vertical-slice-rules>
24
+
25
+ - Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) — vertical, NOT a horizontal slice of one layer
26
+ - A completed slice is demoable or verifiable on its own
27
+ - Each slice is sized to fit in a single fresh context window
28
+ - Any prefactoring should be done first
29
+
30
+ </vertical-slice-rules>
31
+
32
+ ## CRITICAL: Vertical slices only
33
+
34
+ Each ticket cuts a narrow but COMPLETE path through every layer — schema, API, UI, tests — sized to fit one fresh context window, and demoable on its own. A wide refactor is the exception: sequence it as expand–contract, never force it into a tracer bullet.
35
+
36
+ Give each ticket its **blocking edges** — the other tickets that must complete before it can start. A ticket with no blockers can start immediately.
37
+
38
+ **Wide refactors are the exception to vertical slicing.** A **wide refactor** is one mechanical change — rename a column, retype a shared symbol — whose **blast radius** fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green. Don't force it into a tracer bullet; sequence it as **expand–contract**. First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by blast radius (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains, in a ticket blocked by every migrate batch. When even the batches can't stay green alone, keep the sequence but let them share an integration branch that all block a final integrate-and-verify ticket — green is promised only there.
39
+
40
+ ### 4. Quiz the user
41
+
42
+ Present the proposed breakdown as a numbered list. For each ticket, show:
43
+
44
+ - **Title**: short descriptive name
45
+ - **Blocked by**: which other tickets (if any) must complete first
46
+ - **What it delivers**: the end-to-end behaviour this ticket makes work
47
+
48
+ Use `matt_pocock_ask` to ask the user for the genuinely user-owned design decision in one structured exchange: whether the breakdown and blocking edges are acceptable, and any required merge or split. Wait only for that answer; then apply it and continue directly to publication. Do not ask a separate permission-to-proceed question after approval.
49
+
50
+ ### 5. Publish the tickets to the configured tracker
51
+
52
+ Publish the approved tickets. **How** depends on the tracker [setup-matt-pocock-skills](setup-matt-pocock-skills.md) configured — the tickets are the same either way, only the shape of the blocking edges changes:
53
+
54
+ - **Local files** → write one file per ticket under `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01` in dependency order (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below — one ticket per file, never a single combined file.
55
+ - **A real issue tracker (GitHub, Linear, …)** → publish one issue per ticket in dependency order (blockers first) so each ticket's blocking edges can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the `ready-for-agent` triage label unless instructed otherwise — the tickets are agent-grabbable by construction.
56
+
57
+ Work the **frontier**: any ticket whose blockers are all done. For a purely linear chain that means top to bottom. Once tickets are published, immediately claim and execute the next unblocked AFK ticket; after each closure, re-query the frontier and continue through newly unblocked AFK work. Do not wait for the user simply because ticket publication or an earlier ticket completed.
58
+
59
+ Do NOT close or modify any parent issue.
60
+
61
+ <local-ticket-template>
62
+
63
+ # <NN> — <Ticket title>
64
+
65
+ **What to build:** the end-to-end behaviour this ticket makes work, from the user's perspective — not a layer-by-layer implementation list.
66
+
67
+ **Blocked by:** the numbers/titles of the tickets that gate this one, or "None — can start immediately".
68
+
69
+ **Status:** ready-for-agent
70
+
71
+ - [ ] Acceptance criterion 1
72
+ - [ ] Acceptance criterion 2
73
+
74
+ </local-ticket-template>
75
+
76
+ <issue-template>
77
+
78
+ ## Parent
79
+
80
+ A reference to the parent issue on the tracker (if the source was an existing issue, otherwise omit this section).
81
+
82
+ ## What to build
83
+
84
+ The end-to-end behaviour this ticket makes work, from the user's perspective — not layer-by-layer implementation.
85
+
86
+ ## Acceptance criteria
87
+
88
+ - [ ] Criterion 1
89
+ - [ ] Criterion 2
90
+
91
+ ## Blocked by
92
+
93
+ - A reference to each blocking ticket, or "None — can start immediately".
94
+
95
+ </issue-template>
96
+
97
+ In either form, avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
@@ -0,0 +1,15 @@
1
+ # Triage Labels
2
+
3
+ The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
4
+
5
+ | Label in mattpocock/skills | Label in our tracker | Meaning |
6
+ | -------------------------- | -------------------- | ---------------------------------------- |
7
+ | `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
8
+ | `needs-info` | `needs-info` | Waiting on reporter for more information |
9
+ | `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
10
+ | `ready-for-human` | `ready-for-human` | Requires human implementation |
11
+ | `wontfix` | `wontfix` | Will not be actioned |
12
+
13
+ When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
14
+
15
+ Edit the right-hand column to match whatever vocabulary you actually use.