vanara 0.1.0 → 0.3.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 (195) hide show
  1. package/README.md +14 -0
  2. package/bin/vanara.js +66 -2
  3. package/catalog/agents/api-designer/AGENT.md +156 -0
  4. package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
  5. package/catalog/agents/api-designer/examples/review-notes.md +61 -0
  6. package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
  7. package/catalog/agents/api-designer/references/design-checklist.md +68 -0
  8. package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
  9. package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
  10. package/catalog/agents/code-reviewer/AGENT.md +184 -0
  11. package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
  12. package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
  13. package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
  14. package/catalog/agents/code-reviewer/references/security-review.md +62 -0
  15. package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
  16. package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
  17. package/catalog/agents/debugger/AGENT.md +128 -0
  18. package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
  19. package/catalog/agents/debugger/examples/repro-template.md +50 -0
  20. package/catalog/agents/debugger/references/bisection.md +63 -0
  21. package/catalog/agents/debugger/references/debugging-method.md +57 -0
  22. package/catalog/agents/debugger/references/observability.md +61 -0
  23. package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
  24. package/catalog/agents/pr-summarizer/AGENT.md +172 -0
  25. package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
  26. package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
  27. package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
  28. package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
  29. package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
  30. package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
  31. package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
  32. package/catalog/agents/refactoring-specialist/AGENT.md +201 -0
  33. package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
  34. package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
  35. package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
  36. package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
  37. package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
  38. package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
  39. package/catalog/agents/security-auditor/AGENT.md +184 -0
  40. package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
  41. package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
  42. package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
  43. package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
  44. package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
  45. package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
  46. package/catalog/agents/technical-writer/AGENT.md +179 -0
  47. package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
  48. package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
  49. package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
  50. package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
  51. package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
  52. package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
  53. package/catalog/agents/test-author/AGENT.md +193 -0
  54. package/catalog/agents/test-author/examples/test-plan.md +54 -0
  55. package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
  56. package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
  57. package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
  58. package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
  59. package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
  60. package/catalog/agents/threat-modeler/AGENT.md +179 -0
  61. package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
  62. package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
  63. package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
  64. package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
  65. package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
  66. package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
  67. package/catalog/agents/vuln-scanner/AGENT.md +162 -0
  68. package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
  69. package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
  70. package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
  71. package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
  72. package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
  73. package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
  74. package/catalog/packs/code-review-pack/PACK.md +100 -0
  75. package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
  76. package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
  77. package/catalog/packs/security-pack/PACK.md +128 -0
  78. package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
  79. package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
  80. package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
  81. package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
  82. package/catalog/packs/security-pack/references/workflow.md +56 -0
  83. package/catalog/skills/api-pagination/SKILL.md +83 -0
  84. package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
  85. package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
  86. package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
  87. package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
  88. package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
  89. package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
  90. package/catalog/skills/caching-strategies/SKILL.md +180 -0
  91. package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
  92. package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
  93. package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
  94. package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
  95. package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
  96. package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
  97. package/catalog/skills/conventional-commits/SKILL.md +135 -0
  98. package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
  99. package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
  100. package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
  101. package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
  102. package/catalog/skills/conventional-commits/references/spec.md +78 -0
  103. package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
  104. package/catalog/skills/database-migrations/SKILL.md +137 -0
  105. package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
  106. package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
  107. package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
  108. package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
  109. package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
  110. package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
  111. package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
  112. package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
  113. package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
  114. package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
  115. package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
  116. package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
  117. package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
  118. package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
  119. package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
  120. package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
  121. package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
  122. package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
  123. package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
  124. package/catalog/skills/owasp-top10/SKILL.md +112 -0
  125. package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
  126. package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
  127. package/catalog/skills/owasp-top10/references/access-control.md +36 -0
  128. package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
  129. package/catalog/skills/owasp-top10/references/injection.md +39 -0
  130. package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
  131. package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
  132. package/catalog/skills/prompt-engineering/SKILL.md +100 -0
  133. package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
  134. package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
  135. package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
  136. package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
  137. package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
  138. package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
  139. package/catalog/skills/readme-writing/SKILL.md +176 -0
  140. package/catalog/skills/readme-writing/examples/README.template.md +73 -0
  141. package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
  142. package/catalog/skills/readme-writing/references/anatomy.md +107 -0
  143. package/catalog/skills/readme-writing/references/maintenance.md +68 -0
  144. package/catalog/skills/readme-writing/references/writing-style.md +79 -0
  145. package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
  146. package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
  147. package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
  148. package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
  149. package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
  150. package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
  151. package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
  152. package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
  153. package/catalog/skills/rest-api-design/SKILL.md +147 -0
  154. package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
  155. package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
  156. package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
  157. package/catalog/skills/rest-api-design/references/pagination.md +41 -0
  158. package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
  159. package/catalog/skills/rest-api-design/references/versioning.md +31 -0
  160. package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
  161. package/catalog/skills/secrets-management/SKILL.md +134 -0
  162. package/catalog/skills/secrets-management/examples/env-example.md +41 -0
  163. package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
  164. package/catalog/skills/secrets-management/references/leak-response.md +47 -0
  165. package/catalog/skills/secrets-management/references/rotation.md +46 -0
  166. package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
  167. package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
  168. package/catalog/skills/secure-auth/SKILL.md +135 -0
  169. package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
  170. package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
  171. package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
  172. package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
  173. package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
  174. package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
  175. package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
  176. package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
  177. package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
  178. package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
  179. package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
  180. package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
  181. package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
  182. package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
  183. package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
  184. package/catalog/skills/test-plan-design/SKILL.md +154 -0
  185. package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
  186. package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
  187. package/catalog/skills/test-plan-design/references/case-design.md +79 -0
  188. package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
  189. package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
  190. package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
  191. package/free-tier.json +15 -0
  192. package/package.json +15 -4
  193. package/premium-index.json +1 -0
  194. package/src/config.js +31 -1
  195. package/src/license.js +143 -0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ // Runnable check: lints a README for the required sections and a runnable
3
+ // quickstart code block. Demonstrates an *executed*, verifiable example asset.
4
+ // Zero dependencies — Node built-ins only.
5
+ //
6
+ // Usage:
7
+ // node lint-readme.mjs README.md # lint a file
8
+ // node lint-readme.mjs --selftest # run built-in test cases (exit 0/1)
9
+ // echo '<markdown>' | node lint-readme.mjs # lint from stdin
10
+ //
11
+ // A README passes when it has a title (# H1), a one-line description, every
12
+ // required section heading, and at least one fenced code block (the quickstart).
13
+
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ // Each rule: [label, predicate(markdown) -> boolean]. Heading matches are
17
+ // case-insensitive and accept common synonyms.
18
+ const REQUIRED_SECTIONS = [
19
+ ['Install', /^#{1,6}\s+(install|installation|setup|getting started)\b/im],
20
+ ['Usage', /^#{1,6}\s+(usage|quick ?start|getting started|examples?)\b/im],
21
+ ['License', /^#{1,6}\s+licen[sc]e\b/im],
22
+ ];
23
+
24
+ const RULES = [
25
+ ['has a top-level title (# H1)', (md) => /^#\s+\S+/m.test(md)],
26
+ ['has a one-line description under the title', (md) => hasDescription(md)],
27
+ ['has at least one fenced code block (quickstart)', (md) => /```[\s\S]*?```/.test(md)],
28
+ ...REQUIRED_SECTIONS.map(([name, re]) => [`has a "${name}" section`, (md) => re.test(md)]),
29
+ ];
30
+
31
+ // A description is a non-empty, non-heading, non-badge line appearing after the
32
+ // H1 title within the first several lines.
33
+ function hasDescription(md) {
34
+ const lines = md.split(/\r?\n/);
35
+ const titleIdx = lines.findIndex((l) => /^#\s+\S+/.test(l));
36
+ if (titleIdx === -1) return false;
37
+ for (let i = titleIdx + 1; i < Math.min(lines.length, titleIdx + 8); i++) {
38
+ const line = lines[i].trim();
39
+ if (!line) continue;
40
+ if (line.startsWith('#')) return false; // hit next heading first
41
+ if (line.startsWith('<!--')) continue; // skip comments
42
+ if (/^[[!]/.test(line) && /\]\(/.test(line)) continue; // skip badge/link-only lines
43
+ return true; // found prose
44
+ }
45
+ return false;
46
+ }
47
+
48
+ export function lintReadme(md) {
49
+ const failures = RULES.filter(([, fn]) => !safe(fn, md)).map(([msg]) => msg);
50
+ return { ok: failures.length === 0, failures };
51
+ }
52
+
53
+ function safe(fn, md) { try { return fn(md); } catch { return false; } }
54
+
55
+ function lint(label, md) {
56
+ const { ok, failures } = lintReadme(md);
57
+ if (ok) console.log(`✓ ${label}: README has all required sections`);
58
+ else { console.error(`✗ ${label}:`); failures.forEach((f) => console.error(` - ${f}`)); }
59
+ return ok;
60
+ }
61
+
62
+ const COMPLETE = `# Acme
63
+
64
+ A fast CLI for doing the thing.
65
+
66
+ ## Install
67
+ \`\`\`bash
68
+ npm install acme
69
+ \`\`\`
70
+
71
+ ## Usage
72
+ \`\`\`bash
73
+ acme run
74
+ \`\`\`
75
+
76
+ ## License
77
+ MIT
78
+ `;
79
+
80
+ const INCOMPLETE = `# Acme
81
+
82
+ Some text but no fenced code block and no license section.
83
+
84
+ ## Install
85
+ Run npm install acme.
86
+ `;
87
+
88
+ function selftest() {
89
+ const cases = {
90
+ valid_complete: COMPLETE,
91
+ invalid_incomplete: INCOMPLETE,
92
+ };
93
+ let allExpected = true;
94
+ for (const [name, md] of Object.entries(cases)) {
95
+ const { ok } = lintReadme(md);
96
+ const shouldPass = name.startsWith('valid');
97
+ const correct = ok === shouldPass;
98
+ allExpected &&= correct;
99
+ console.log(
100
+ `${correct ? '✓' : '✗'} selftest ${name}: ${ok ? 'pass' : 'fail'} (expected ${shouldPass ? 'pass' : 'fail'})`
101
+ );
102
+ }
103
+ process.exit(allExpected ? 0 : 1);
104
+ }
105
+
106
+ const arg = process.argv[2];
107
+ if (arg === '--selftest') selftest();
108
+ else if (arg) {
109
+ let md;
110
+ try { md = readFileSync(arg, 'utf8'); }
111
+ catch { console.error(`✗ cannot read file: ${arg}`); process.exit(1); }
112
+ process.exit(lint(arg, md) ? 0 : 1);
113
+ } else {
114
+ let buf = '';
115
+ process.stdin.on('data', (c) => (buf += c));
116
+ process.stdin.on('end', () => process.exit(lint('stdin', buf) ? 0 : 1));
117
+ }
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: refactoring-patterns
3
+ description: Improve code structure without changing behavior — the discipline of small, named, test-backed moves. Extract function/variable, inline, rename, replace conditional with polymorphism, introduce parameter object, guard clauses. Recognize smells, refactor safely, avoid big-bang rewrites.
4
+ type: skill
5
+ version: 2.0.0
6
+ updated: 2026-06-29
7
+ ---
8
+ # Refactoring Patterns
9
+
10
+ Refactoring is **changing the internal structure of code without changing its observable behavior**. It
11
+ is not "cleanup whenever," and it is not rewriting. It is a disciplined sequence of small, *named*,
12
+ behavior-preserving moves, each verified by tests, each committable on its own. Done right it is nearly
13
+ risk-free; done wrong — without tests, mixed with feature work, or as a big-bang rewrite — it is one of
14
+ the most reliable ways to ship a regression.
15
+
16
+ This skill is the deep reference for that discipline: the safety net that makes it possible, the named
17
+ moves themselves, the smells that signal where to apply them, and the trade-offs of when *not* to. Heavy
18
+ detail lives in `references/`; copy-paste before/after material in `examples/`; a runnable smell detector
19
+ in `scripts/`.
20
+
21
+ ## The mental model
22
+
23
+ Two hats, never worn at once (Kent Beck's rule): you are either **adding behavior** (new tests go red,
24
+ then green) *or* **refactoring** (all tests stay green the whole time). Switching hats mid-edit is the
25
+ root cause of most refactoring disasters, because when a test breaks you can no longer tell whether your
26
+ *restructuring* was wrong or your *new feature* was wrong.
27
+
28
+ | Question | Refactoring answer |
29
+ |---|---|
30
+ | What changes? | structure only — names, shape, location |
31
+ | What stays identical? | observable behavior, the public contract, test results |
32
+ | How do I stay safe? | tests green before, green after, green between every step |
33
+ | How big is a step? | small enough that a broken test points at one change |
34
+ | When do I commit? | after each move that leaves the suite green |
35
+
36
+ ## 1. Tests are the safety net (non-negotiable)
37
+
38
+ You cannot refactor code you cannot verify. Before touching structure, ensure a **green** test suite
39
+ exercises the behavior you're about to move. If coverage is missing, write **characterization tests**
40
+ first — tests that pin down what the code *currently* does (even if that's arguably wrong), so any
41
+ behavior drift during refactoring surfaces immediately. See `references/safe-workflow.md` for the
42
+ red-green-refactor loop, characterization testing, and the strangler-fig pattern for large systems.
43
+
44
+ The loop:
45
+
46
+ 1. Run the suite — confirm green.
47
+ 2. Apply **one** named refactoring.
48
+ 3. Run the suite — confirm still green.
49
+ 4. Commit.
50
+ 5. Repeat.
51
+
52
+ If step 3 goes red, you have exactly one small change to undo. That is the entire value proposition.
53
+
54
+ ## 2. Smells: knowing *where* to refactor
55
+
56
+ Refactoring is demand-driven — you don't refactor everything, you refactor what a **code smell** is
57
+ pointing at. The catalogue in `references/code-smells.md` is the full list; the high-frequency ones:
58
+
59
+ - **Long method / long function** — does too much; the body has comment-delimited "paragraphs."
60
+ - **Large class / god object** — too many fields and responsibilities; low cohesion.
61
+ - **Feature envy** — a method that reaches into another object's data more than its own.
62
+ - **Primitive obsession** — strings/ints standing in for real concepts (`string currency`, `int cents`).
63
+ - **Shotgun surgery** — one conceptual change forces edits in many scattered places.
64
+ - **Duplicated code** — the same logic in three spots (the rule of three: extract on the third copy).
65
+
66
+ ## 3. The named moves
67
+
68
+ Each refactoring has a **name** and a **mechanics** (a precise step sequence). Naming them lets a team
69
+ say "extract a function here" and share an exact, low-risk procedure. The full catalogue with mechanics
70
+ is in `references/refactoring-catalog.md`. The core set:
71
+
72
+ - **Extract Function** — pull a coherent block into its own named function.
73
+ - **Inline Function/Variable** — the inverse, when the indirection earns nothing.
74
+ - **Rename** — the highest-value, lowest-risk move; a precise name deletes a comment.
75
+ - **Extract Variable** — name a sub-expression to explain it.
76
+ - **Replace Conditional with Polymorphism** — swap a type-switch for dispatch on a type.
77
+ - **Introduce Parameter Object** — bundle a clump of args that always travel together.
78
+ - **Replace Magic Literal with Constant**, **Decompose Conditional**, **Replace Nested Conditional with
79
+ Guard Clauses**, **Move Function/Field**, **Separate Query from Modifier**.
80
+
81
+ ### Before / after: Extract Function + guard clauses
82
+
83
+ A long function doing validation, calculation, and formatting at once:
84
+
85
+ ```js
86
+ // before — one function, three jobs, deep nesting
87
+ function invoiceLine(item) {
88
+ if (item) {
89
+ if (item.qty > 0) {
90
+ let total = item.qty * item.price;
91
+ if (item.taxable) { total = total * 1.2; }
92
+ return `${item.name}: $${total.toFixed(2)}`;
93
+ }
94
+ }
95
+ return "invalid";
96
+ }
97
+ ```
98
+
99
+ ```js
100
+ // after — guard clauses flatten nesting; intent-named helpers
101
+ function invoiceLine(item) {
102
+ if (!isValid(item)) return "invalid";
103
+ return format(item.name, totalFor(item));
104
+ }
105
+
106
+ const isValid = (i) => i && i.qty > 0;
107
+ const totalFor = (i) => i.taxable ? i.qty * i.price * 1.2 : i.qty * i.price;
108
+ const format = (name, total) => `${name}: $${total.toFixed(2)}`;
109
+ ```
110
+
111
+ Same inputs, same outputs — tests stay green — but each piece now has one job and a name.
112
+
113
+ ### Before / after: Replace Conditional with Polymorphism
114
+
115
+ ```ts
116
+ // before — the switch will grow with every new type; shotgun surgery waiting to happen
117
+ function area(shape: Shape): number {
118
+ switch (shape.kind) {
119
+ case "circle": return Math.PI * shape.r ** 2;
120
+ case "square": return shape.side ** 2;
121
+ default: throw new Error("unknown shape");
122
+ }
123
+ }
124
+ ```
125
+
126
+ ```ts
127
+ // after — each type owns its behavior; adding a shape is additive, not invasive
128
+ interface Shape { area(): number; }
129
+ class Circle implements Shape { constructor(private r: number) {} area() { return Math.PI * this.r ** 2; } }
130
+ class Square implements Shape { constructor(private side: number) {} area() { return this.side ** 2; } }
131
+ // new shapes add a class; no existing code is touched (Open/Closed)
132
+ ```
133
+
134
+ See `examples/` for both of these as standalone, runnable files.
135
+
136
+ ## 4. Common pitfalls (failure modes)
137
+
138
+ - **Refactoring without tests.** The cardinal sin. With no safety net you are *rewriting and hoping*.
139
+ Write characterization tests first or do not start.
140
+ - **Wearing both hats.** Mixing a refactor with a behavior change in the same commit. When something
141
+ breaks you can't bisect *which* intent caused it, and reviewers can't see the real change in the diff.
142
+ Keep refactor commits and feature commits separate.
143
+ - **Big-bang rewrites.** "Let's just rewrite it cleanly" discards hard-won, battle-tested edge-case
144
+ knowledge and ships with no incremental safety. Prefer the **strangler fig**: grow the new structure
145
+ around the old, route traffic over piece by piece, delete the old when nothing calls it.
146
+ - **Steps too big.** If a "step" touches 30 files before you re-run tests, a red bar tells you nothing
147
+ useful. Shrink the step.
148
+ - **Refactoring on a feature branch for weeks.** Long-lived refactor branches rot against `main` and
149
+ produce merge nightmares. Land small refactors continuously.
150
+ - **Speculative generality** — extracting abstractions "for the future" no caller needs yet. Refactor
151
+ toward concrete, present demands (YAGNI).
152
+
153
+ ## 5. When NOT to refactor / trade-offs
154
+
155
+ - **No tests and no time to write them** for code you don't understand — refactoring here is gambling.
156
+ Add characterization tests first, or leave it alone.
157
+ - **Code you're about to delete** — don't polish a corpse.
158
+ - **Stable code nobody touches and nobody complains about** — clean for clean's sake has no payoff;
159
+ refactor when you're *already* in the area for a feature or fix (the "campsite rule").
160
+ - **A hard deadline shipping today** — note the debt, ship, refactor next iteration. Be honest that this
161
+ is borrowing, not free.
162
+ - **Refactor vs. rewrite:** rewrite only when the design is fundamentally wrong for current
163
+ requirements *and* you can carve off a strangler-fig seam. Otherwise incremental refactoring is almost
164
+ always cheaper and safer than a rewrite.
165
+
166
+ ## Files in this package
167
+
168
+ - `references/code-smells.md` — the smell catalogue (long method, large class, feature envy, primitive
169
+ obsession, shotgun surgery, and more) with the move each one calls for.
170
+ - `references/refactoring-catalog.md` — named refactorings with their step-by-step mechanics.
171
+ - `references/safe-workflow.md` — red-green-refactor, characterization tests, strangler fig, the two hats.
172
+ - `examples/extract-function.before-after.js` — long function → extracted, named helpers.
173
+ - `examples/replace-conditional-with-polymorphism.before-after.ts` — type-switch → dispatch.
174
+ - `scripts/detect-smells.mjs` — runnable Node scanner that flags long functions and deep nesting
175
+ (`--selftest` for built-in cases).
176
+
177
+ Pairs with the `error-handling-patterns` skill (refactor toward explicit error handling), the
178
+ `testing-patterns` skill (the safety net), and the `code-reviewer` and `refactor-cleaner` agents.
@@ -0,0 +1,57 @@
1
+ // Extract Function (+ guard clauses + extract variable)
2
+ // ------------------------------------------------------
3
+ // A single long function doing validation, calculation, tax, and formatting,
4
+ // refactored into small named pieces. Behavior is identical — the demo at the
5
+ // bottom proves before === after for the same inputs.
6
+ //
7
+ // Run: node extract-function.before-after.js
8
+
9
+ // ---- BEFORE: one function, several jobs, nested conditionals -------------------
10
+ function invoiceLineBefore(item) {
11
+ if (item) {
12
+ if (item.qty > 0) {
13
+ let total = item.qty * item.price;
14
+ if (item.taxable) {
15
+ total = total * 1.2; // what's 1.2? magic number
16
+ }
17
+ return item.name + ": $" + total.toFixed(2);
18
+ }
19
+ }
20
+ return "invalid";
21
+ }
22
+
23
+ // ---- AFTER: guard clause + extracted, intent-named helpers ---------------------
24
+ const TAX_RATE = 0.2; // Replace Magic Literal with Symbolic Constant
25
+
26
+ const isValidItem = (item) => Boolean(item) && item.qty > 0; // Decompose Conditional
27
+
28
+ function lineTotal(item) {
29
+ const subtotal = item.qty * item.price; // Extract Variable
30
+ return item.taxable ? subtotal * (1 + TAX_RATE) : subtotal;
31
+ }
32
+
33
+ const formatLine = (name, total) => `${name}: $${total.toFixed(2)}`;
34
+
35
+ function invoiceLineAfter(item) {
36
+ if (!isValidItem(item)) return "invalid"; // guard clause flattens nesting
37
+ return formatLine(item.name, lineTotal(item));
38
+ }
39
+
40
+ // ---- Proof the refactoring preserved behavior ---------------------------------
41
+ const cases = [
42
+ { name: "Widget", qty: 3, price: 10, taxable: true },
43
+ { name: "Gadget", qty: 2, price: 5, taxable: false },
44
+ null,
45
+ { name: "Empty", qty: 0, price: 99, taxable: true },
46
+ ];
47
+
48
+ let allMatch = true;
49
+ for (const c of cases) {
50
+ const before = invoiceLineBefore(c);
51
+ const after = invoiceLineAfter(c);
52
+ const ok = before === after;
53
+ allMatch = allMatch && ok;
54
+ console.log(`${ok ? "✓" : "✗"} ${JSON.stringify(c)} -> before:"${before}" after:"${after}"`);
55
+ }
56
+ console.log(allMatch ? "\nAll outputs identical — behavior preserved." : "\nMISMATCH — refactor changed behavior!");
57
+ process.exit(allMatch ? 0 : 1);
@@ -0,0 +1,63 @@
1
+ // Replace Conditional with Polymorphism
2
+ // -------------------------------------
3
+ // BEFORE: a type-code switch repeated across multiple functions. Every new shape
4
+ // forces edits to area() AND name() AND any future per-type function — the
5
+ // "repeated switch" / shotgun-surgery smell.
6
+ //
7
+ // AFTER: each type owns its behavior behind a common interface. Adding a shape is
8
+ // purely additive (Open/Closed); no existing function is touched.
9
+ //
10
+ // This is illustrative TypeScript. Compile with `tsc` or read as reference.
11
+
12
+ // ===== BEFORE =================================================================
13
+ type ShapeData =
14
+ | { kind: "circle"; r: number }
15
+ | { kind: "square"; side: number }
16
+ | { kind: "rect"; w: number; h: number };
17
+
18
+ function areaBefore(s: ShapeData): number {
19
+ switch (s.kind) {
20
+ case "circle": return Math.PI * s.r ** 2;
21
+ case "square": return s.side ** 2;
22
+ case "rect": return s.w * s.h;
23
+ default: throw new Error("unknown shape");
24
+ }
25
+ }
26
+
27
+ function nameBefore(s: ShapeData): string {
28
+ switch (s.kind) { // the SAME switch, duplicated — change one, hunt the rest
29
+ case "circle": return "Circle";
30
+ case "square": return "Square";
31
+ case "rect": return "Rectangle";
32
+ default: throw new Error("unknown shape");
33
+ }
34
+ }
35
+
36
+ // ===== AFTER ==================================================================
37
+ interface Shape {
38
+ area(): number;
39
+ name(): string;
40
+ }
41
+
42
+ class Circle implements Shape {
43
+ constructor(private readonly r: number) {}
44
+ area(): number { return Math.PI * this.r ** 2; }
45
+ name(): string { return "Circle"; }
46
+ }
47
+
48
+ class Square implements Shape {
49
+ constructor(private readonly side: number) {}
50
+ area(): number { return this.side ** 2; }
51
+ name(): string { return "Square"; }
52
+ }
53
+
54
+ class Rectangle implements Shape {
55
+ constructor(private readonly w: number, private readonly h: number) {}
56
+ area(): number { return this.w * this.h; }
57
+ name(): string { return "Rectangle"; }
58
+ }
59
+
60
+ // Adding a Triangle = add one class. areaBefore/nameBefore would each need a new
61
+ // case; here, no existing class changes. That is the payoff.
62
+
63
+ export { areaBefore, nameBefore, Shape, Circle, Square, Rectangle };
@@ -0,0 +1,92 @@
1
+ # Code Smells — and the move each one calls for
2
+
3
+ A *smell* is a surface symptom that usually (not always) points at a deeper structural problem. Smells
4
+ tell you **where** to refactor; the named refactoring tells you **how**. Use judgment — a smell is a
5
+ prompt to look, not an automatic mandate to change.
6
+
7
+ ## Bloaters
8
+
9
+ ### Long method / long function
10
+ **Symptom:** the body has comment-delimited "paragraphs," scrolls off-screen, mixes levels of
11
+ abstraction (high-level orchestration next to bit-twiddling).
12
+ **Move:** *Extract Function* on each paragraph; *Replace Temp with Query*; *Decompose Conditional*.
13
+ **Heuristic:** if you feel the urge to write a comment explaining a block, extract that block and let the
14
+ function name be the comment.
15
+
16
+ ### Large class / god object
17
+ **Symptom:** dozens of fields, many unrelated responsibilities, low cohesion (fields used by disjoint
18
+ subsets of methods).
19
+ **Move:** *Extract Class* / *Extract Subclass*; group fields that change together into their own type.
20
+
21
+ ### Primitive obsession
22
+ **Symptom:** `string currency`, `int cents`, `string` for a phone number — primitives standing in for
23
+ domain concepts, with validation/formatting scattered around every use site.
24
+ **Move:** *Replace Primitive with Object* (a `Money`, `PhoneNumber`, `EmailAddress` value type that owns
25
+ its validation and behavior). Kills duplicated validation and impossible states.
26
+
27
+ ### Long parameter list
28
+ **Symptom:** five-plus parameters, or the same clump of args passed together everywhere.
29
+ **Move:** *Introduce Parameter Object* / *Preserve Whole Object*.
30
+
31
+ ### Data clumps
32
+ **Symptom:** the same three or four data items travel together through many signatures
33
+ (`x, y, width, height`).
34
+ **Move:** *Introduce Parameter Object* — they want to be a `Rect`.
35
+
36
+ ## Change-preventers (these make change expensive)
37
+
38
+ ### Shotgun surgery
39
+ **Symptom:** one conceptual change (e.g. "add a new tax rule") forces small edits in many scattered
40
+ files/classes.
41
+ **Move:** *Move Function/Field* to pull the scattered logic into one place; *Inline Class* if a class
42
+ adds no value.
43
+
44
+ ### Divergent change
45
+ **Symptom:** the opposite — one class is changed for many *different* reasons (a new report format *and*
46
+ a new DB column both touch it).
47
+ **Move:** *Extract Class* so each axis of change lives in its own type.
48
+
49
+ ## Couplers
50
+
51
+ ### Feature envy
52
+ **Symptom:** a method spends more time reading another object's fields than its own.
53
+ **Move:** *Move Function* to the class whose data it envies; *Extract Function* then move the part that
54
+ envies.
55
+
56
+ ### Inappropriate intimacy
57
+ **Symptom:** two classes reach deep into each other's private parts.
58
+ **Move:** *Move Function/Field*, *Hide Delegate*, or merge if they're really one concept.
59
+
60
+ ### Message chains
61
+ **Symptom:** `a.getB().getC().getD().doThing()` — the caller is coupled to the whole navigation path.
62
+ **Move:** *Hide Delegate* (ask the first object to do the work).
63
+
64
+ ## Dispensables
65
+
66
+ ### Duplicated code
67
+ **Symptom:** the same (or near-same) logic in multiple places.
68
+ **Move:** *Extract Function* and call it from both. **Rule of three:** duplication is acceptable twice;
69
+ on the third occurrence, extract. Extracting too early (on the second) risks a wrong abstraction that
70
+ couples things that only *coincidentally* looked alike.
71
+
72
+ ### Comments (as deodorant)
73
+ **Symptom:** a comment exists to explain *what* confusing code does.
74
+ **Move:** *Extract Function* / *Rename* so the code self-documents; keep comments for *why*, not *what*.
75
+
76
+ ### Dead code / speculative generality
77
+ **Symptom:** unused params, unreachable branches, abstractions with a single implementation "for the
78
+ future."
79
+ **Move:** delete it (YAGNI). Pairs with the `refactor-cleaner` agent.
80
+
81
+ ## Conditionals
82
+
83
+ ### Complex / nested conditional
84
+ **Symptom:** deep `if/else` pyramids; a `switch` on a type code that recurs in several places.
85
+ **Move:** *Replace Nested Conditional with Guard Clauses*; *Decompose Conditional* (extract the
86
+ condition and each leg into named functions); *Replace Conditional with Polymorphism* when the switch is
87
+ on a type.
88
+
89
+ ### Repeated switch on a type
90
+ **Symptom:** the same `switch (kind)` appears in `area()`, `perimeter()`, `draw()`…
91
+ **Move:** *Replace Conditional with Polymorphism* — each type owns all its behaviors; new types become
92
+ additive (Open/Closed).
@@ -0,0 +1,78 @@
1
+ # Refactoring Catalog — named moves with mechanics
2
+
3
+ Each refactoring is a *named* procedure with explicit **mechanics** — a small step sequence designed so
4
+ the suite stays green throughout. Run tests after every numbered step where noted. Most modern IDEs
5
+ automate the safe versions (Rename, Extract Function/Variable, Inline, Move); prefer the automated
6
+ version when available because it preserves references mechanically.
7
+
8
+ ## Extract Function
9
+ **When:** a fragment of a function can be grouped and named for its intent (long method, duplication,
10
+ comment-as-deodorant).
11
+ **Mechanics:**
12
+ 1. Create a new function named for *what it does*, not how.
13
+ 2. Copy the extracted code into it.
14
+ 3. Pass in any local variables it reads as parameters; return any it writes.
15
+ 4. Replace the original fragment with a call.
16
+ 5. Run tests.
17
+
18
+ ## Inline Function / Inline Variable
19
+ **When:** the body is as clear as the name, or a layer of indirection earns nothing.
20
+ **Mechanics:** verify it isn't polymorphically overridden; replace each call with the body; remove the
21
+ declaration; run tests. (The inverse of Extract — use it to undo a premature extraction.)
22
+
23
+ ## Rename (Variable / Function / Field)
24
+ **When:** a name no longer reveals intent. The highest value-to-risk move there is.
25
+ **Mechanics:** prefer the IDE's rename (updates all references atomically). If manual, change the
26
+ declaration, find every reference, update, run tests. Renaming a published API element requires a
27
+ deprecation step — keep the old name delegating for one release.
28
+
29
+ ## Extract Variable
30
+ **When:** a sub-expression is hard to read or repeated within an expression.
31
+ **Mechanics:** introduce a well-named `const` for the sub-expression; replace occurrences; run tests.
32
+ Makes complex booleans and arithmetic self-explaining.
33
+
34
+ ## Replace Nested Conditional with Guard Clauses
35
+ **When:** deep `if` nesting where some branches are exceptional/early-exit cases.
36
+ **Mechanics:** for each "this isn't the normal path" check, return (or throw) early at the top; the happy
37
+ path then drops out of the nesting. Run tests after each guard.
38
+
39
+ ## Decompose Conditional
40
+ **When:** a complex `if (cond) { ... } else { ... }` where the condition and legs are all hard to read.
41
+ **Mechanics:** *Extract Function* on the condition (name it `isEligible(...)`), and on each leg. The
42
+ conditional becomes `if (isEligible(x)) return discounted(x); return standard(x);`.
43
+
44
+ ## Replace Conditional with Polymorphism
45
+ **When:** the same `switch`/`if`-on-type appears in multiple methods (repeated switch smell).
46
+ **Mechanics:**
47
+ 1. Ensure an inheritance/interface structure exists (create it via *Extract Class* / *Replace Type Code
48
+ with Subclasses* if needed).
49
+ 2. Move one `switch` into the superclass/interface as a method.
50
+ 3. For each leg, override the method in the matching subclass with that leg's body.
51
+ 4. Remove the leg from the superclass; leave the default/abstract.
52
+ 5. Run tests after each leg moved. Repeat per method that switched on the same type.
53
+
54
+ ## Introduce Parameter Object
55
+ **When:** a clump of arguments (data clump) recurs across signatures.
56
+ **Mechanics:** create a class/record for the clump; add it as a parameter; one call site at a time, route
57
+ the individual args through the object; remove the originals once all sites are migrated; run tests
58
+ between sites.
59
+
60
+ ## Replace Magic Literal with Symbolic Constant
61
+ **When:** an unexplained literal (`0.2`, `86400`, `"ACTIVE"`) carries meaning.
62
+ **Mechanics:** declare a named constant; replace each occurrence; run tests. Search for the literal value
63
+ elsewhere — duplicated magic numbers often hide.
64
+
65
+ ## Move Function / Move Field
66
+ **When:** feature envy or shotgun surgery — the element lives in the wrong class.
67
+ **Mechanics:** check what it references; create it in the target; adjust references; turn the original
68
+ into a delegating call (or remove if no longer needed); update callers; run tests.
69
+
70
+ ## Separate Query from Modifier
71
+ **When:** a function both returns a value *and* has a side effect (surprising to callers).
72
+ **Mechanics:** create a pure query that returns the value; leave the modifier returning void; update
73
+ callers to call both where they truly need the side effect; run tests.
74
+
75
+ ## Mechanics discipline
76
+ The point of writing mechanics down is that under pressure you follow the *steps*, not your intuition.
77
+ Small steps + green tests after each = you can always revert exactly one move. If a step list feels too
78
+ small, that's the point — speed comes from never having to debug.
@@ -0,0 +1,74 @@
1
+ # Safe Refactoring Workflow
2
+
3
+ Refactoring is only "safe" because of the process around it. The moves themselves are mechanical; the
4
+ discipline is what prevents regressions.
5
+
6
+ ## The two hats (Kent Beck)
7
+
8
+ At any moment you are wearing exactly one hat:
9
+
10
+ - **Adding behavior** — you write a new failing test, then make it pass. The structure may get a little
11
+ worse; that's fine.
12
+ - **Refactoring** — you change structure while **all tests stay green**. You add no tests (except as a
13
+ safety net before starting) and you change no behavior.
14
+
15
+ Never wear both at once. The instant a test breaks, the hat you're wearing tells you what kind of mistake
16
+ you made. Mixing them destroys that signal and pollutes the diff so reviewers can't see the real change.
17
+
18
+ ## Red-Green-Refactor
19
+
20
+ The TDD loop bakes refactoring in as a first-class step:
21
+
22
+ 1. **Red** — write a test for the next behavior; it fails.
23
+ 2. **Green** — write the *simplest* code that passes, even if ugly.
24
+ 3. **Refactor** — now, with a green bar, clean up the code you just wrote *and* the surrounding code your
25
+ change touched. Tests stay green.
26
+
27
+ The refactor step is where design actually happens. Skipping it is how "temporary" green code calcifies
28
+ into permanent debt.
29
+
30
+ ## Characterization tests (the legacy-code entry point)
31
+
32
+ When you must refactor code that has **no tests** and you're unsure what it does:
33
+
34
+ 1. Write a test that calls the code and asserts *whatever it currently returns* — even if that output
35
+ looks wrong. You're not asserting correctness; you're **pinning current behavior**.
36
+ 2. A fast way to discover the expected value: assert something obviously false, run it, and copy the
37
+ actual value from the failure message into the assertion.
38
+ 3. Build up enough of these to cover the branches you're about to touch.
39
+ 4. Now refactor. Any behavior drift breaks a characterization test immediately.
40
+
41
+ Characterization tests document the *as-is* contract. If you later decide some pinned behavior was a bug,
42
+ that's a **separate, behavior-changing** task (other hat) with its own test update and commit.
43
+
44
+ ## Strangler fig (refactoring at system scale)
45
+
46
+ Big-bang rewrites fail because they discard embedded edge-case knowledge and offer no incremental safety.
47
+ The strangler fig grows the new structure *around* the old, like the vine that envelops a tree:
48
+
49
+ 1. Put a **seam** (interface/facade/router) in front of the thing you want to replace.
50
+ 2. Build the new implementation behind the seam for **one** slice of functionality.
51
+ 3. Route that slice's traffic to the new code; keep everything else on the old.
52
+ 4. Verify in production (metrics, parity checks); migrate the next slice.
53
+ 5. When nothing routes to the old code, delete it.
54
+
55
+ At every step the system is fully working and releasable. You can stop, pause, or roll back a single
56
+ slice — none of which a rewrite allows.
57
+
58
+ ## Commit hygiene
59
+
60
+ - **One refactoring move per commit** where practical; at minimum, never mix a refactor with a feature in
61
+ one commit. A reviewer should be able to read a commit message of `refactor: extract totalFor()` and
62
+ trust the behavior is unchanged.
63
+ - Land refactors **continuously** onto `main`. Long-lived refactor branches rot against everyone else's
64
+ work and create merge hell.
65
+ - Keep the suite **fast**. If running tests after every step is painful, you'll stop doing it — and the
66
+ whole safety net collapses. Invest in test speed first.
67
+
68
+ ## Pre-flight checklist
69
+
70
+ - [ ] Green test suite covering the behavior I'm about to move (or characterization tests written).
71
+ - [ ] I know which hat I'm wearing right now.
72
+ - [ ] My next step is small enough that a red bar points at one change.
73
+ - [ ] I will run the suite after this step.
74
+ - [ ] My commit contains only refactoring, no behavior change.