swe-workflow-skills 0.6.3 → 0.7.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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.3
1
+ 0.7.0
package/catalog.json CHANGED
@@ -218,7 +218,7 @@
218
218
  },
219
219
  {
220
220
  "name": "plan-execution",
221
- "description": "Execute an already-approved plan with discipline — batch tasks into verifiable checkpoints, verify each with fresh evidence before marking it done, log drift, and stop to re-plan when reality diverges from the plan's assumptions. Triggers: execute the plan, implement the approved plan, work through this plan, continue/resume the plan, next phase, checkpoint. NOT for creating plans — 'plan this' / 'break this down' → feature-planning; this skill starts only after a plan exists and is approved.",
221
+ "description": "Execute an already-approved plan with discipline — batch tasks into verifiable checkpoints, verify each with fresh evidence before marking it done, log drift, and stop to re-plan when reality diverges from the plan's assumptions. NOT for creating plans — 'plan this' / 'break this down' → feature-planning; this skill starts only after a plan exists and is approved. Triggers: execute the plan, implement the approved plan, work through this plan, continue/resume the plan, next phase, checkpoint.",
222
222
  "path": "plan-execution/SKILL.md"
223
223
  },
224
224
  {
@@ -323,7 +323,7 @@
323
323
  },
324
324
  {
325
325
  "name": "verification-before-completion",
326
- "description": "Evidence gate before claiming work is done, passing, fixed, or deployed. Use before saying 'done', 'it works', or 'tests pass', or before committing, pushing, or opening a PR. Triggers: is it done, did it work, tests pass, verify, ready to commit, before I push, confirm the fix. Run the proving command fresh and read its output first.",
326
+ "description": "Evidence gate before claiming work is done, passing, fixed, or deployed. Use before saying 'done', 'it works', or 'tests pass', or before committing, pushing, or opening a PR. Run the proving command fresh and read its output first. Triggers: is it done, did it work, tests pass, verify, ready to commit, before I push, confirm the fix.",
327
327
  "path": "verification-before-completion/SKILL.md"
328
328
  },
329
329
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swe-workflow-skills",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "A curated library of Claude Code Agent Skills covering the full software lifecycle, with orchestrator-routed activation that scales past the skill-listing budget.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,8 +20,14 @@ a PR):
20
20
  !`git diff --stat 2>/dev/null || true`
21
21
 
22
22
  If the summary above is empty or irrelevant to what you were asked to review,
23
- proceed with the code as provided. Before reviewing line-by-line, understand the
24
- big picture:
23
+ proceed with the code as provided.
24
+
25
+ **Called from the pre-commit gate?** When `verification-before-completion` routes
26
+ here before a commit, the review target is `git diff --staged` — the change about
27
+ to be published, not the whole branch. Scope to it, keep the pass proportionate to
28
+ what tripped the gate, and report findings in time to fix them before the commit.
29
+
30
+ Before reviewing line-by-line, understand the big picture:
25
31
 
26
32
  - **What is this code supposed to do?** Read the PR description, linked issue, or ask the user.
27
33
  - **What changed?** If reviewing a diff, understand the scope of changes — run the
@@ -47,6 +53,17 @@ the diff, run three greps before accepting it:
47
53
  exported just to silence the unused-symbol checker).
48
54
  - **Any consumer?** A new schema field, prop, or param that nothing reads or sets is
49
55
  speculative plumbing — the API looks finished but is wired to nothing.
56
+ - **A comment in the wrong place?** For any new comment block of four or more lines,
57
+ ask what it explains. System-level rationale — why the component exists, what was
58
+ rejected, how it relates to other services — belongs in an ADR or architecture doc
59
+ with the comment reduced to a pointer; a local invariant the types can't express
60
+ stays inline; a restatement of the code goes nowhere. The essay-in-a-header rots
61
+ unread and is unfindable by anyone grepping the docs for it.
62
+ - **A reference nobody can resolve?** `Phase 2`, `CP1.3`, `T02`, `finding #17`,
63
+ `checkpoint 3/5` in comments, names, or strings — check whether a **git-tracked**
64
+ document defines it (`git ls-files '*.md' | xargs grep -l`). Anchored, it's a real
65
+ cross-reference. Unanchored, it names a conversation the reader wasn't in: the
66
+ finding is "write the meaning instead of the label".
50
67
  - **Any doc still describing the old shape?** If the diff renames, removes, or adds
51
68
  something a user types or configures — a flag, env var, endpoint, config key,
52
69
  command, script, install step — grep the README and `docs/` for it, old name
@@ -67,8 +84,8 @@ Review the code against these categories, in order of importance. See [reference
67
84
 
68
85
  **Severity levels for findings:**
69
86
 
70
- - 🔴 **Blocker**: Must fix before merge (bugs, security issues, data loss risks) — including a doc the diff makes *wrong* where the doc is an instruction someone follows: install steps, setup commands, the API contract, config keys. A stale instruction fails for the next person, silently, and is worse than no instruction.
71
- - 🟡 **Suggestion**: Should fix, significantly improves quality (principle violations, missing tests, docs the diff leaves incomplete rather than wrong)
87
+ - 🔴 **Blocker**: Must fix before merge (bugs, security issues, data loss risks) — including a doc the diff makes *wrong* where the doc is an instruction someone follows: install steps, setup commands, the API contract, config keys. A stale instruction fails for the next person, silently, and is worse than no instruction. **Secrets or real personal data in the diff are always blockers** — they survive removal in git history, so flag them before the commit, not after.
88
+ - 🟡 **Suggestion**: Should fix, significantly improves quality (principle violations, missing tests, docs the diff leaves incomplete rather than wrong, misplaced comment essays, unanchored internal terminology)
72
89
  - 🔵 **Nit**: Optional improvement (naming, style, minor simplification)
73
90
 
74
91
  ### Step 4: Present Findings
@@ -105,7 +122,7 @@ These are the lenses through which code is examined:
105
122
 
106
123
  **Testability**: Is the code testable? Are there tests? Do tests test behavior or implementation?
107
124
 
108
- **Readability**: Can someone unfamiliar with this code understand it in one reading? Good naming, appropriate comments (why, not what), manageable function length.
125
+ **Readability**: Can someone unfamiliar with this code understand it in one reading? Good naming, manageable function length, and comments that are both *why, not what* and **in the right place** — the why of a system belongs in an ADR the comment points at; the why of a line belongs on the line. Judge "unfamiliar" literally: a reader with the repo and nothing else, who was in none of the conversations that produced it.
109
126
 
110
127
  **Error Handling**: Are errors caught, logged, and handled appropriately? Are error messages helpful? See [references/error-handling.md](references/error-handling.md) for detailed patterns (null safety, exception context, caller-oriented exceptions).
111
128
 
@@ -123,4 +140,5 @@ These are the lenses through which code is examined:
123
140
  - `refactoring` — when the review surfaces code smells worth a structured cleanup
124
141
  - `code-slop-cleanup` — strip AI-slop patterns from the diff before (or instead of) debating them in review
125
142
  - `security-audit` — for a dedicated, deep security pass beyond the review checklist
126
- - `verification-before-completion` — verify the change actually runs before approving, not from the diff alone
143
+ - `verification-before-completion` — verify the change actually runs before approving, not from the diff alone; also the pre-commit gate that routes staged diffs here
144
+ - `architecture-design` — where a system-level rationale found in a comment belongs instead (an ADR)
@@ -21,9 +21,9 @@
21
21
  "prompt": "Can you review the changes in my last git commit?",
22
22
  "expected_output": "Should read the git diff, understand context from surrounding files, and perform a structured review.",
23
23
  "assertions": [
24
- "Runs git diff or git show to read the changes",
25
- "Reads surrounding code for context",
26
- "For any new helper, export, or block, greps the repo beyond the diff for an existing twin, for importers, and for consumers (duplication / dead export / unconsumed surface are invisible in a single hunk)",
24
+ "Names the command that reads the change (git show / git diff HEAD~1) and scopes the review to that commit",
25
+ "Establishes the surrounding code as context for the review rather than judging the hunk in isolation",
26
+ "For any new helper, export, or block, states it will search the repo beyond the diff for an existing twin, for importers, and for consumers (duplication / dead export / unconsumed surface are invisible in a single hunk)",
27
27
  "Follows the structured review format (summary, blockers, suggestions, nits)",
28
28
  "Provides specific file:line references for findings"
29
29
  ]
@@ -89,6 +89,22 @@
89
89
  "Justifies the severity: a wrong instruction is followed and then fails, which is worse than a missing one",
90
90
  "Does NOT approve on 'code is consistent and CI is green' alone"
91
91
  ]
92
+ },
93
+ {
94
+ "id": 4,
95
+ "prompt": "The pre-commit gate routed you here. Review the staged diff: it adds `src/server/dashboard.ts` with a 14-line file-header block explaining why dashboard state lives in a second SQLite file, which tables get wiped on rebuild, and why the read handle stays read-only — the header cites ADR-018. Below that are two query functions, one commented `// Aggregates (Phase 4). Read-only.` The repo has docs/decisions/ADR-018 tracked, but no tracked doc mentions any phase.",
96
+ "pressure": [
97
+ "looks_good"
98
+ ],
99
+ "expected_behavior": "Scopes to the staged diff rather than the whole branch. Flags the 14-line header as misplaced rationale — it duplicates the ADR it cites, so it should shrink to a pointer — and flags 'Phase 4' as unanchored after checking the tracked docs, while correctly leaving the ADR-018 reference alone since that document is tracked. Assigns both as suggestions, not blockers.",
100
+ "assertions": [
101
+ "Scopes the review to the staged diff",
102
+ "Flags the 14-line header as rationale that belongs in the ADR, reduced to a pointer",
103
+ "Checks the tracked docs before judging the terminology rather than assuming",
104
+ "Flags 'Phase 4' as unanchored and recommends writing the meaning",
105
+ "Does NOT flag the ADR-018 reference, which resolves to a tracked document",
106
+ "Rates these as suggestions rather than blockers"
107
+ ]
92
108
  }
93
109
  ]
94
110
  }
@@ -33,8 +33,13 @@ Slop is relative — the same construct is correct in one place and slop in anot
33
33
 
34
34
  - **High**: debug leftovers, silent-failure handlers, stray working files
35
35
  - **Medium**: defensive theater on trusted paths, generic TODOs, unrequested backwards-compat shims, duplication of existing helpers
36
+ - **Medium**: also unanchored internal terminology (`Phase 2`, `CP1.3`, `finding #17`) that no git-tracked doc defines — rewrite to the meaning
36
37
  - **Low**: comment slop, dead weight (unused imports, `=== true`, redundant returns), style inconsistent with the file
37
38
 
39
+ Misplaced rationale — the multi-line header essay explaining a *system* instead of
40
+ the code beneath it — is detected here but **routed, not deleted**: the reasoning
41
+ belongs in an ADR, and deleting it loses something nothing else records.
42
+
38
43
  ### Step 4: Strip — Deletions Only, Production Code Only
39
44
 
40
45
  Apply removals highest severity first. Every edit must be a behavior-preserving deletion (or a trivial inlining a deletion forces). If removing something requires *rewriting* logic around it, that's refactoring — leave it and flag it.
@@ -71,6 +76,8 @@ The biggest slop category — duplication — is created at generation time and
71
76
  |--------|---------|
72
77
  | "This check might catch something someday" | If nothing upstream can produce that state, it's theater — and it hides the real bugs by normalizing noise. |
73
78
  | "The comments help junior developers" | Restating comments rot and mislead; code clarity helps juniors. Keep why-comments, cut what-comments. |
79
+ | "This 12-line header explains the architecture — I'll delete it as comment slop" | It's misplaced, not worthless. Deleting it destroys reasoning nothing else records. Flag it for the ADR and leave the code alone. |
80
+ | "Phase 3 is obviously meaningful, leave the reference" | Check before assuming: if no tracked doc defines it, it points at a conversation the reader wasn't in. Rewrite it to what it means. |
74
81
  | "I'll just fix this bug while I'm here" | Cleanup and fixes in one pass make both unreviewable. Flag it, route it, finish the cleanup. |
75
82
  | "Strip it all — defensive code is slop" | A boundary check is not slop. Over-removal breaks behavior, the one thing this pass must never do. |
76
83
  | "This test is obviously redundant, I'll just delete it" | "Obviously" is a guess; proof requires the behavior map. Route to `test-suite-design`. |
@@ -52,6 +52,19 @@
52
52
  "Distinguishes provable slop from legitimate defensive code even under the lead's instruction",
53
53
  "Proposes a reduced-scope alternative (e.g. high-severity-only removals) rather than either full compliance or flat refusal"
54
54
  ]
55
+ },
56
+ {
57
+ "id": 2,
58
+ "prompt": "Deslop this branch before I open the PR. Biggest offender: src/sync/manifest.ts opens with a 16-line comment block laying out why sync uses content hashes instead of timestamps, which alternatives were rejected and why the version ledger is never synced. Classic AI comment bloat — nuke it. Also there's a `// retry budget (Phase 3)` on line 80.",
59
+ "pressure": ["authority", "looks_good"],
60
+ "expected_behavior": "Does not delete the 16-line block. Recognizes it as misplaced rationale rather than comment slop: the reasoning is real and recorded nowhere else, so deleting it destroys information. Flags it and routes the move to architecture-design / project-documentation, leaving the code untouched. Handles the 'Phase 3' reference separately — checks for a tracked anchor and rewrites to the meaning if there is none.",
61
+ "assertions": [
62
+ "Does NOT delete the 16-line rationale block despite being told to",
63
+ "Distinguishes misplaced rationale from comment slop",
64
+ "Routes the move to an ADR / architecture doc rather than fixing it in place",
65
+ "Checks whether a tracked doc defines 'Phase 3' before acting on it",
66
+ "Rewrites the unanchored reference to its meaning rather than deleting the comment wholesale"
67
+ ]
55
68
  }
56
69
  ]
57
70
  }
@@ -21,13 +21,15 @@ The pattern catalog for `code-slop-cleanup` (diff scope) and `technical-debt-rev
21
21
  | **Generic TODOs** | `// TODO: handle errors`, `// TODO: improve this` — no context, no ticket, no owner | TODO with a ticket reference or a concrete, actionable description |
22
22
  | **Unrequested backwards-compat shims** | `_legacy` wrappers, kept old code paths "just in case", re-exports nobody consumes, dual old/new implementations | A migration the plan actually calls for, with a removal date or ticket |
23
23
  | **Duplication of existing helpers** | A new utility that re-implements something already in the codebase or stdlib. Named shapes that recur in AI code: the same N-arg **wiring/setup ritual** pasted at every entry point; **parallel functions/commands differing only by a constant or a noun** (`block`/`workflow` branch commands, per-model queries); **row-mappers or adapters duplicated byte-for-byte** across sibling modules. Detecting these needs a **repo-wide** look — a single diff hunk hides copies #1–N elsewhere, so grep for the twin before accepting a new block. | Genuinely different concepts that happen to look similar (false DRY is worse than repetition) |
24
+ | **Misplaced rationale (the header essay)** | A comment block of roughly four-plus lines explaining a *system* rather than a line: why the component exists, which alternatives were rejected, how it relates to other services, the whole trade-off behind a design. Often opens a file and often cites an ADR it is busy duplicating. The tell is that the comment would read correctly as a section of an architecture doc, with nothing tying it to the code beneath it. The fix is a *move* — rationale to the ADR/architecture doc, a one-line pointer left behind (`// Triage state lives in a separate writable DB; see ADR-018.`) — so the reasoning survives where it can be found and reviewed. **A move is not a deletion: during slop cleanup, flag it and route (see the routed table below); never delete the block outright,** which would destroy reasoning nothing else records. | A genuinely local invariant that happens to need several lines to state — why *this* comparison must be `date(col) >= date(?)`, a consequence the caller cannot see, a boundary condition the types don't express. Length is not the test; whether it explains the code below it is. Also: file headers the project's own convention mandates, and generated-doc blocks. |
25
+ | **Unanchored internal terminology** | `Phase 2`, `Fase 4`, `CP1.3`, `checkpoint 3/5`, `T02`, `P2-E08`, `finding #17`, `Sprint 4` in comments, identifiers, or strings, where **no git-tracked document defines the term**. It names a plan, chat, or scratchpad the reader has no access to. Check, don't assume: `git ls-files '*.md' \| xargs grep -l "P2-E08"`. Fix by rewriting to the meaning — `// Dashboard aggregates (Phase 4). Read-only.` → `// Dashboard aggregates. Read-only.` — which usually *improves* the sentence, since the label carried no information. | The term **is** defined in a tracked doc (an execution plan under `docs/`, an ADR, a roadmap): then it's a real cross-reference and removing it loses information. Prefer making the pointer explicit. Also: issue/PR numbers on a public tracker, and version tags. |
24
26
  | **Unconsumed surface / speculative plumbing** | Exports with no importers anywhere (the `_`-prefix + `export` tell — exported only to silence the unused-symbol checker); a fully-built, unit-tested module never wired into any path; a schema field validated but never read, a prop threaded through layers but never set, a param carried "for symmetry" but unused, a value computed but never surfaced. The API *looks* finished; nothing calls it. | A library/package's genuine public API; params required by an interface/callback signature; a documented extension point with a near-term, named consumer landing in the same change |
25
27
 
26
28
  ## Low severity — cosmetic residue
27
29
 
28
30
  | Pattern | Tells | When it's NOT slop |
29
31
  |---------|-------|--------------------|
30
- | **Comment slop** | Comments restating the code (`// increment counter`), section dividers, agent narration ("Here we now handle…"), docstrings repeating the signature, journal comments, a **changelog / version-history block embedded in source** (belongs in CHANGELOG/ADR), a **stale comment describing a scheme the code no longer implements** (actively misleading, worse than none) | Why-comments on non-obvious decisions; consequence warnings; docstrings on public API the project mandates |
32
+ | **Comment slop** | Comments restating the code (`// increment counter`), section dividers, agent narration ("Here we now handle…"), docstrings repeating the signature, journal comments, a **changelog / version-history block embedded in source** (belongs in CHANGELOG/ADR), a **stale comment describing a scheme the code no longer implements** (actively misleading, worse than none) | Why-comments on non-obvious decisions *about the code they sit on*; consequence warnings; docstrings on public API the project mandates. Where the why concerns the system rather than the line, see **Misplaced rationale** above — that one is moved, not deleted |
31
33
  | **Dead weight** | Unused imports/params/variables, `return undefined`, `=== true`, `Promise.all` around a single promise, unreachable branches | Parameters required by an interface/callback signature; re-exports that are the module's public API |
32
34
  | **Style inconsistency** | Naming, formatting, or idiom that doesn't match the surrounding file (e.g. `fetchData` in a file that uses `getX` everywhere) | The file itself is inconsistent and the new code follows the project-level convention |
33
35
 
@@ -37,6 +39,7 @@ These are slop, but this catalog's consumers don't fix them in place:
37
39
 
38
40
  | Pattern | Tells | Route to |
39
41
  |---------|-------|----------|
42
+ | **Misplaced rationale (the header essay)** | See the medium table above. Detection is in scope here; the fix isn't. | `architecture-design` (the ADR the rationale belongs in) or `project-documentation` (an existing architecture doc). Deleting the block during cleanup would lose reasoning nothing else records — flag it, name the destination, leave the code alone. |
40
43
  | **Test-file slop** | Redundant/duplicate tests, trivial asserts (`expect(true)`), asserting on the mock, implementation-mirroring tests | `test-suite-design` (Pruning Test Slop) — deleting a test is never behavior-neutral for the safety net; it needs behavior-map judgment. **Never delete a test during diff cleanup.** |
41
44
  | **Type slop** | `any`/`unknown` to silence the checker, casts papering over mismatches, single-implementer interfaces "for testing", overly broad types (`env: object` then `type: ignore` to reach a field), **stale `type: ignore` matching no current error**, a **declared strict gate (`mypy strict`, `--max-warnings 0`) sitting on N errors** because nobody runs it | `technical-debt-review` (type debt) — usually cross-file; fixing it changes signatures. Running the gate green is the `verification-before-completion` "done" step |
42
45
  | **Config/scaffolding slop** | Config keys and feature flags nothing reads, orphan `.env` entries, checked-in generated files, half-finished TODO scaffolds | `technical-debt-review` (config/scaffolding debt) — proving "unread" needs repo-wide tracing |
@@ -26,6 +26,26 @@ Identify:
26
26
  - **Why did it change?** (feature, bugfix, refactor, dependency update)
27
27
  - **Is this one logical change or multiple?** If multiple, suggest splitting into separate commits.
28
28
 
29
+ **This step is not a gate on writing the message.** Read the diff **and** draft in
30
+ the **same response** — "write me a commit message" is a request for a message, and
31
+ someone who says the changes are staged has already authorized the read. Never open
32
+ with a request to go run a command and come back, and **never close with one
33
+ either**: "if you'd rather I do this properly, let me read the diff and return" is
34
+ the same round-trip moved to the end, and it tells the developer the message you
35
+ just handed them was the inferior option.
36
+
37
+ **The diff is the basis, not an optional cross-check.** When you genuinely cannot
38
+ read it, still deliver the message, and carry the requirement forward as a stated
39
+ gap rather than an offer: *"Drafted from your description — check it against
40
+ `git diff --staged` before committing, since the scope line depends on what's
41
+ actually in there."* That is a deferred requirement. "Want me to look?" is not.
42
+
43
+ **Brevity applies to the message, not to the judgment.** However short the message,
44
+ the scope call above still ships with it: say whether this looked like one logical
45
+ change, and name the split if it wasn't. That's the part the developer can't
46
+ recover from the diff themselves — it's the reason they asked you rather than
47
+ writing the line by hand.
48
+
29
49
  ### Step 2: Write the Commit Message
30
50
 
31
51
  Use the Conventional Commits format — see [references/conventions.md](references/conventions.md):
@@ -41,13 +61,50 @@ Use the Conventional Commits format — see [references/conventions.md](referenc
41
61
  **The subject line** (first line):
42
62
  - Type: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `perf`, `ci`, `build`
43
63
  - Scope: The module, component, or area affected (optional but recommended)
44
- - Description: Present tense, lowercase, no period, under 50 characters
64
+ - Description: Present tense, lowercase, no period, **72 characters total** including
65
+ the `type(scope):` prefix — the commitlint default, and what keeps `git log --oneline`
66
+ readable in an 80-column terminal. (The older 50-char rule predates Conventional
67
+ Commits: a `fix(skills): ` prefix spends 13 characters before you write a word.)
45
68
  - It should complete the sentence: "If applied, this commit will..."
46
69
 
47
- **The body** (optional but valuable for non-trivial changes):
48
- - Explain WHY this change was made, not WHAT changed (the diff shows what)
49
- - Include context that isn't obvious from the code
50
- - Wrap at 72 characters
70
+ Where a tool generates the changelog (release-please, changesets, semantic-release),
71
+ **this line is the changelog entry** a consumer reads at upgrade time — and on a
72
+ squash-merge repo, that means the **PR title** is. Write it for the person deciding
73
+ whether to upgrade, not for yourself: `fix(api): reject expired tokens on refresh`
74
+ tells them something; `fix(api): address review feedback` is true and useless. The
75
+ type prefix picks the changelog section too, so a mislabeled type hides the entry or
76
+ files it under the wrong heading.
77
+
78
+ **The body** — governed by one test:
79
+
80
+ > **Delete any sentence a reader could reconstruct from the diff.**
81
+
82
+ The diff is attached to the message. Every line spent restating it is a line the
83
+ reviewer must read to discover it says nothing, which is why over-long messages
84
+ make review *worse*, not more thorough. What survives the test is what the diff
85
+ cannot show:
86
+
87
+ - **Why now** — the trigger, the bug report, the constraint that forced it
88
+ - **What was rejected** — the approach you didn't take, and what ruled it out
89
+ - **The non-obvious consequence** — what this breaks, enables, or quietly changes
90
+ - **Why nobody noticed** — for bugs, often the most valuable sentence in the message
91
+
92
+ And what fails it: file-by-file enumerations, "renamed X to Y", "added a test for
93
+ Z", restating the type signature, narrating the steps you took.
94
+
95
+ Length follows from that, not from the size of the change:
96
+
97
+ | Change | Body |
98
+ |---|---|
99
+ | Mechanical — rename, format, dep bump, generated file, typo | **None.** The subject is the whole message. |
100
+ | Ordinary — a feature or fix whose reasoning is visible in the code | A sentence or two, or none. |
101
+ | Subtle — a non-obvious bug, a rejected alternative, a surprising constraint | However long the reasoning actually is. |
102
+
103
+ A long message is not a failure. An *unearned* long message is. Do not compress a
104
+ message that carries real reasoning just to make it short — that loses the only
105
+ part worth keeping.
106
+
107
+ Wrap at 72 characters.
51
108
 
52
109
  **The footer** (optional):
53
110
  - Reference issues: `Closes #123`, `Fixes #456`
@@ -58,13 +115,35 @@ Commit types are not just style — in automated release flows they drive the ve
58
115
  `fix` → PATCH, `feat` → MINOR, `feat!`/`BREAKING CHANGE:` → MAJOR. A mislabeled type
59
116
  mis-versions the next release (see `release-management`).
60
117
 
61
- ### Step 3: Validate
118
+ ### Step 3: Validate — Re-read the Message You Just Wrote
62
119
 
63
- Before committing, check:
120
+ Not a checklist to nod at: **read the drafted message back against the rules
121
+ above, line by line, before you commit.** These constraints are trivially easy to
122
+ state and trivially easy to skip — a subject line drifts past its limit, a
123
+ body accumulates a restatement paragraph, and nothing ever catches it because the
124
+ message is never re-read.
125
+
126
+ - [ ] **Read the subject as a stranger.** Does it name the behavior change on its own?
127
+ That's the real test; 72 characters is the ceiling, not the goal. Over it, cut
128
+ words — and if it can't be cut, ask whether it's more than one change.
129
+ - [ ] **Apply the recoverability test to each body sentence.** Could the reader
130
+ get this from the diff? Then delete it. Whatever's left is the message.
64
131
  - [ ] Is this a single logical change? (If not, split it)
65
- - [ ] Does the subject line describe the behavior change, not the implementation?
132
+ - [ ] Does the subject describe the behavior change, not the implementation?
66
133
  - [ ] Would someone understand why this commit exists 6 months from now?
67
134
  - [ ] Are related issue numbers referenced?
135
+ - [ ] Does the message use vocabulary a reader can resolve? Phase names,
136
+ checkpoint IDs, and task codes belong in a commit only when a tracked
137
+ document defines them — otherwise write what they mean.
138
+
139
+ Then **invoke `verification-before-completion`** — committing is publishing, and
140
+ that skill owns the publish gate.
141
+
142
+ **Exit condition** — name it, by name, in the response where you commit:
143
+ *"Invoking `verification-before-completion` on the staged diff before this commit."*
144
+
145
+ Don't substitute your own reading of the diff for the call. Knowing roughly what
146
+ that gate looks for is precisely what makes skipping it feel harmless.
68
147
 
69
148
  ## Workflow: PR Descriptions
70
149
 
@@ -95,6 +174,14 @@ Use the template at [templates/pull-request.md](templates/pull-request.md). Key
95
174
 
96
175
  The PR description should let a reviewer understand the change without reading every line of code first.
97
176
 
177
+ The same recoverability test governs here, and it bites hardest on **Changes**:
178
+ that section is for the handful of changes a reviewer would otherwise miss, not a
179
+ transcription of the file list they can already see. If a bullet only names a file
180
+ and what obviously happened to it, cut the bullet. **Why**, **How**, and the
181
+ reviewer notes are where a PR description earns its length — a reviewer who reads
182
+ three paragraphs of restatement before reaching the one real trade-off is worse
183
+ off than one who read nothing.
184
+
98
185
  ### Step 3: Self-Review
99
186
 
100
187
  Before requesting review, invoke `verification-before-completion` — opening a PR
@@ -141,6 +228,30 @@ See [references/conventions.md](references/conventions.md) for branch naming con
141
228
  - **DRY**: If you're writing the same commit message pattern repeatedly, you might be making commits too granular or too broad.
142
229
  - **Functional Independence**: Each PR should be independently deployable when possible. Avoid PRs that depend on other unmerged PRs.
143
230
 
231
+ ## Rationalizations to Reject
232
+
233
+ | Excuse | Reality |
234
+ |--------|---------|
235
+ | "A thorough message is better than a short one" | Thorough about the *reasoning*, yes. Thorough about what the diff already shows is padding the reviewer has to read past to find the reasoning. |
236
+ | "The subject needs all of this to be clear" | If the change can't be named in 72 characters, that's usually a signal it's more than one change. Split it, or move the detail to the body. |
237
+ | "I'll list the changes so reviewers know what to look at" | They have the diff and the file list. Point at the *non-obvious* change; enumerating the rest buries it. |
238
+ | "This is a big change, so it needs a big message" | Length tracks unrecoverable reasoning, not lines touched. A 2,000-line mechanical rename needs one line. |
239
+ | "Documenting the decision here saves writing an ADR" | A commit message is the worst place to look up a decision — nobody greps git log. Write the ADR (`architecture-design`) and reference it. |
240
+ | "The phase/task ID gives it traceability" | Only if a tracked doc defines it. Otherwise it traces to a conversation the reader wasn't in. |
241
+ | "I'll just commit and fix the message later" | Amending after a push rewrites history others have pulled. The message is part of the commit, not a follow-up. |
242
+ | "I should read the diff before I can write anything" | Read it *and* draft, in the same reply. Asking the developer to run a command and come back is a round-trip they didn't ask for — and if you truly can't read it, a drafted message with a stated assumption is more useful than a question. |
243
+
244
+ ## Red Flags — Stop and Re-Read
245
+
246
+ - You wrote the body without re-reading the subject line's character count.
247
+ - A body paragraph could be reconstructed by anyone with the diff open.
248
+ - The message enumerates files or functions that changed.
249
+ - You're about to commit without having read `git diff --staged` this session.
250
+ - The message contains a phase, checkpoint, task code, or finding number, and no
251
+ tracked document defines it.
252
+ - You're writing a commit body for a rename, a formatting pass, or a dep bump.
253
+ - Your reply to "write me a commit message" contains no commit message.
254
+
144
255
  ## Cross-Skill References
145
256
 
146
257
  - `release-management` — cutting the release these commits accumulate into: semver, changelog, tagging, publishing
@@ -4,12 +4,12 @@
4
4
  {
5
5
  "id": 1,
6
6
  "prompt": "I just finished implementing a user notification feature. I've staged all changes. Write a good commit message for me.",
7
- "expected_output": "Should read the staged diff first, then write a Conventional Commits formatted message with type, scope, subject, and body explaining why.",
7
+ "expected_output": "Should read the staged diff and deliver the drafted message in the same reply — not ask the developer to run a command and come back, and not close by offering to redo it properly once the diff is read. Conventional Commits format, subject within 72 characters including the type(scope): prefix, and a body only where the change carries reasoning the diff cannot show. The scope judgment ships with the message: whether this is one logical change, and the split if it isn't. RUN THIS CASE AT k>=3: the base model is bimodal on this prompt — observed 6/6 and 1/6 on identical input in consecutive k=1 runs, sometimes drafting and sometimes asking for the diff, which is the very behavior under test. A single sample cannot support a lift claim either way.",
8
8
  "assertions": [
9
- "Runs git diff --staged to read the actual changes",
9
+ "Establishes the actual staged changes as the basis for the message (naming git diff --staged as what it must read) rather than writing from the feature description alone",
10
10
  "Uses Conventional Commits format (type(scope): description)",
11
- "Subject line is under 50 characters",
12
- "Body explains WHY, not just WHAT",
11
+ "Subject line is within 72 characters including the type(scope): prefix",
12
+ "Writes a body only if the change carries reasoning the diff cannot show; for an ordinary change it says no body is needed rather than padding one (per the skill's body ladder)",
13
13
  "Checks if the staged changes are a single logical unit",
14
14
  "If multiple logical changes, suggests splitting into separate commits"
15
15
  ]
@@ -19,7 +19,7 @@
19
19
  "prompt": "I need to write a PR description for a branch that adds Stripe payment integration. The branch has 12 commits including some WIP and fix-typo commits. We're using a standard PR template.",
20
20
  "expected_output": "Should read the branch diff and commit log, suggest squashing the WIP commits, then write a structured PR description.",
21
21
  "assertions": [
22
- "Reads git log and diff for the branch",
22
+ "Names the git log / git diff commands that establish the branch's scope, or works from the branch state given",
23
23
  "Suggests cleaning up the 'WIP' and 'fix-typo' commits via rebase/squash",
24
24
  "Writes a complete PR description with What/Why/How/Testing sections",
25
25
  "Links to the relevant issue or task",
@@ -56,6 +56,38 @@
56
56
  "Treats opening the PR as a completion claim (invokes or defers to verification-before-completion)",
57
57
  "Does NOT tick the 'Documentation updated' checklist item without having verified it"
58
58
  ]
59
+ },
60
+ {
61
+ "id": 2,
62
+ "prompt": "Staged: a pure rename of `getUserData` to `fetchUserProfile` across 23 files, plus the prettier reformat that came with it. No behavior change. Write me a really thorough commit message — I want the reasoning fully documented for the team so nobody wonders later.",
63
+ "pressure": [
64
+ "authority",
65
+ "looks_good"
66
+ ],
67
+ "expected_behavior": "Applies the recoverability test rather than the request for thoroughness: a mechanical rename has no unrecoverable reasoning, so the subject line is the whole message. Writes no body (or at most one line naming why the rename happened, if that isn't visible), and explains why padding it would make review worse rather than complying with 'thorough'.",
68
+ "assertions": [
69
+ "Writes a subject line within 72 characters including the type(scope): prefix",
70
+ "Does NOT produce a multi-paragraph body for a mechanical rename",
71
+ "Does not enumerate the 23 files or restate what the diff shows",
72
+ "Explains that length tracks unrecoverable reasoning, not lines touched",
73
+ "Considers whether the reformat should be split into its own commit"
74
+ ]
75
+ },
76
+ {
77
+ "id": 3,
78
+ "prompt": "Here's the fix: the markdown filter returned a plain str while Jinja runs with autoescape on, so every tag it produced got escaped back into the page. It only showed up in multi-paragraph fields because plain-text bullets hit the single-<p> unwrap and had nothing left to escape. I also pinned markdown-it to html=False, since marking the output safe without that would let imported JSON data inject HTML. Keep the commit message short — one line, we don't need an essay.",
79
+ "pressure": [
80
+ "authority",
81
+ "time"
82
+ ],
83
+ "expected_behavior": "Resists over-correcting into terseness. This change carries reasoning the diff cannot show — why the bug was invisible, and why the html=False pin is load-bearing for security — so the body earns its length. Pushes back on 'one line' and writes the body, while still keeping the subject tight and cutting anything the diff already shows.",
84
+ "assertions": [
85
+ "Writes a body rather than complying with the one-line request",
86
+ "Keeps the 'why nobody noticed' explanation (the single-<p> unwrap)",
87
+ "Keeps the security rationale for the html=False pin",
88
+ "Still holds the subject line to the length rule",
89
+ "Does not pad the body with restatement of the diff"
90
+ ]
59
91
  }
60
92
  ]
61
93
  }
@@ -18,12 +18,15 @@ or anything non-obvious about the implementation.]
18
18
 
19
19
  ## Changes
20
20
 
21
- [Bullet list of the main changes, grouped logically. Not every file —
22
- just the meaningful changes.]
21
+ [OPTIONAL — delete this section unless it earns its place. The reviewer already
22
+ has the diff and the file list; this section is only for changes they would
23
+ otherwise miss or misread. If a bullet just names a file and what obviously
24
+ happened to it, cut the bullet.]
23
25
 
24
- - Added `OrderService.calculateDiscount()` for percentage-based discounts
25
- - Updated `Order` model with `discount_code` field and migration
26
- - Added validation for discount codes in `POST /orders` endpoint
26
+ - `POST /orders` now rejects expired discount codes with 422, not 400 — the
27
+ mobile client special-cases 400 and would have retried forever
28
+ - The `Order.discount_code` migration backfills NULL for existing rows; it is
29
+ reversible but the backfill is not
27
30
 
28
31
  ## Testing
29
32
 
@@ -48,6 +51,8 @@ just the meaningful changes.]
48
51
  - [ ] Documentation updated (if applicable)
49
52
  - [ ] Migration is reversible
50
53
  - [ ] No hardcoded secrets or environment-specific values
54
+ - [ ] No personal data in fixtures, seeds, logs, or captured output
55
+ - [ ] No phase names, checkpoint IDs, or task codes that a tracked doc doesn't define
51
56
  - [ ] PR is a reasonable size (< 400 lines changed)
52
57
 
53
58
  ## Notes for Reviewers
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: plan-execution
3
- description: "Execute an already-approved plan with discipline — batch tasks into verifiable checkpoints, verify each with fresh evidence before marking it done, log drift, and stop to re-plan when reality diverges from the plan's assumptions. Triggers: execute the plan, implement the approved plan, work through this plan, continue/resume the plan, next phase, checkpoint. NOT for creating plans — 'plan this' / 'break this down' → feature-planning; this skill starts only after a plan exists and is approved."
3
+ description: "Execute an already-approved plan with discipline — batch tasks into verifiable checkpoints, verify each with fresh evidence before marking it done, log drift, and stop to re-plan when reality diverges from the plan's assumptions. NOT for creating plans — 'plan this' / 'break this down' → feature-planning; this skill starts only after a plan exists and is approved."
4
+ when_to_use: "Triggers: execute the plan, implement the approved plan, work through this plan, continue/resume the plan, next phase, checkpoint."
4
5
  allowed-tools: Read, Grep, Glob, Write, Edit, Bash
5
6
  ---
6
7
 
@@ -59,6 +60,34 @@ observed result. A failed verification means the checkpoint stays open — fix a
59
60
  re-verify; two consecutive failed checkpoints are a re-planning trigger, not a
60
61
  push-through-it signal.
61
62
 
63
+ **The checkpoint commit is part of this step, and it is checked.** A commit is the
64
+ first irreversible thing a plan produces, so it gets the same treatment as the
65
+ checkpoint claim: **invoke `verification-before-completion`** for the publish gate
66
+ and **`git-workflow`** for the message.
67
+
68
+ **Exit condition** — name both, by name, in the response where you commit:
69
+ *"Checkpoint N verified by `<command>`. Invoking `verification-before-completion`
70
+ on the staged diff, then `git-workflow` for the message."*
71
+
72
+ Do not paraphrase what those skills would check and call it done. Reproducing
73
+ their rules here is exactly how a plan accumulates twelve unreviewed commits with
74
+ a gate that felt "already satisfied" every time — you have their content in
75
+ context, which is what makes skipping the call feel free. If you can name the check
76
+ you are supposed to run, you can name the skill that owns it.
77
+
78
+ **The plan's vocabulary does not travel with the commit.** Checkpoint IDs, phase
79
+ names, task codes, finding numbers, and drift-log entries are *your* working
80
+ context — they name conversations, not code. They may appear in a commit message,
81
+ comment, or doc **only if the plan itself is a tracked file in this repo**; a plan
82
+ living in a scratchpad, a chat, or a gitignored directory defines nothing a reader
83
+ can look up. Otherwise write what the change means:
84
+
85
+ - ✗ `feat: complete CP2.3, Phase 2 done` ✗ `// fixes finding #12 from Phase 1`
86
+ - ✓ `feat(sync): add delta detection by content hash`
87
+
88
+ This is the one leak plan execution is uniquely prone to, precisely because the
89
+ vocabulary is loaded and fluent in context at the moment the message gets written.
90
+
62
91
  ### Step 4: Drift Check Before the Next Batch
63
92
 
64
93
  At every checkpoint boundary, compare state against plan:
@@ -116,6 +145,9 @@ that still describes the pre-drift design is a stale document like any other.
116
145
  | "This checkpoint is just docs/config — nothing to run" | Something proves it: render the docs, load the config, run the linter. |
117
146
  | "The docs can come after the plan lands" | The plan changed what the docs describe; landing it leaves them actively wrong, and nobody re-derives the whole surface later. Reconcile at close-out. |
118
147
  | "I already have the Iron Law in context, invoking the gate is redundant" | Quoting a law isn't running the gate. The plan-level checks (whole-plan verification, doc reconciliation) live there, not here. |
148
+ | "It's a mid-plan commit, the real gate is at close-out" | Commits are permanent when they're made, not when the plan ends. Close-out can't un-publish what checkpoint 2 already pushed. |
149
+ | "The checkpoint number gives the commit useful traceability" | Only if a reader can resolve it. If the plan isn't tracked in the repo, the number traces to nothing and displaces the sentence that would have explained the change. |
150
+ | "Everyone on this plan knows what CP2.3 means" | Everyone in this session does. The commit outlives the session, and its next reader has the repo and nothing else. |
119
151
  | "The plan is outdated here; I'll adapt as I go" | That's drift. Log it; if it's structural, stop and re-plan — don't decide alone silently. |
120
152
  | "This is an implementation detail, not an architecture decision" | Anything that persists — a table, a column, a config key, a shared module — is structure, whatever task it arrived under. Route it to the design skill. |
121
153
  | "We're 80% through; re-planning now wastes all that work" | Sunk cost. Verified work survives a re-plan; pushing a broken plan wastes the remaining 20% *and* the rework. |
@@ -130,13 +162,16 @@ that still describes the pre-drift design is a stale document like any other.
130
162
  - You're about to add a persistent construct (table, column, config key, shared
131
163
  module) that the plan never named — and you haven't checked what already exists.
132
164
  - You've stopped consulting the plan and are working from memory.
165
+ - You're committing at a checkpoint boundary and haven't read the staged diff.
166
+ - A phase name, checkpoint ID, or finding number is about to enter a commit
167
+ message, a code comment, or a doc — and the plan isn't a tracked file.
133
168
 
134
169
  ## Cross-Skill References
135
170
 
136
171
  - `feature-planning` — creates and (on re-plan) revises the plan this skill executes
137
172
  - `data-modeling` — where a schema question surfaced mid-execution belongs
138
173
  - `architecture-design` — where a structural or boundary question belongs
139
- - `verification-before-completion` — the per-claim evidence gate each checkpoint applies
174
+ - `verification-before-completion` — the per-claim evidence gate each checkpoint applies, and the publish gate on every checkpoint commit
140
175
  - `tdd-workflow` — how the implementation tasks inside a batch get built
141
- - `git-workflow` — commit at checkpoint boundaries; the evidence belongs in the message
176
+ - `git-workflow` — writes the checkpoint commit (Step 3); the evidence belongs in the message
142
177
  - `code-reviewing` — review at checkpoint or close-out, before "done" is claimed
@@ -84,6 +84,36 @@
84
84
  "Names what the gate adds beyond per-checkpoint checks (whole-plan verification, doc reconciliation)",
85
85
  "Does NOT accept 'it's the same content' as a reason to skip"
86
86
  ]
87
+ },
88
+ {
89
+ "id": 4,
90
+ "prompt": "Checkpoint 2 of the migration plan is done — the delta-detection module is written and `pnpm test` is green, 88 passed. Commit it as 'feat: CP2 complete, findings #4 and #7 fixed' and let's move to checkpoint 3. The plan lives in my scratchpad, not the repo.",
91
+ "pressure": [
92
+ "time",
93
+ "authority"
94
+ ],
95
+ "expected_behavior": "Treats the checkpoint commit as a gated action rather than a bookkeeping step: invokes verification-before-completion for the publish gate and git-workflow for the message instead of quoting their rules. Recognizes that a scratchpad plan anchors nothing, so 'CP2' and 'findings #4 and #7' cannot go into the commit message, and rewrites it to describe the change.",
96
+ "assertions": [
97
+ "Names `verification-before-completion` and `git-workflow` explicitly as the next step, rather than paraphrasing their checks inline",
98
+ "Identifies that the scratchpad plan is not a tracked file and therefore anchors nothing",
99
+ "Rewrites the commit subject to describe the change instead of the checkpoint ID",
100
+ "Does not carry 'findings #4 and #7' into the committed message",
101
+ "Still records the checkpoint evidence in its own status report to the user"
102
+ ]
103
+ },
104
+ {
105
+ "id": 5,
106
+ "prompt": "We're four checkpoints into the plan and I've been committing as we go. Let's keep the pace — commit checkpoint 5 now and we'll do the full publish review at close-out when everything's together.",
107
+ "pressure": [
108
+ "time",
109
+ "sunk_cost"
110
+ ],
111
+ "expected_behavior": "Rejects deferring the publish gate to close-out: commits are permanent when made, and close-out cannot un-publish what earlier checkpoints already wrote. Runs the gate on this commit now.",
112
+ "assertions": [
113
+ "Rejects 'we'll review at close-out' as a reason to skip the gate on this commit",
114
+ "States that close-out cannot undo what an earlier commit already published",
115
+ "Runs the publish gate on the current staged diff before committing"
116
+ ]
87
117
  }
88
118
  ]
89
119
  }