vanara 0.1.0 → 0.2.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 +140 -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 +168 -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 +112 -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 +156 -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 +185 -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 +168 -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 +163 -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 +177 -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 +163 -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 +146 -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,59 @@
1
+ # Structure and maintenance
2
+
3
+ Individual docs must be clear; the *set* of docs must be navigable and must not rot. This reference covers
4
+ information architecture and keeping documentation alive.
5
+
6
+ ## Information architecture
7
+
8
+ Organize the documentation set around the four Diátaxis types and the reader's journey, not around the
9
+ codebase's module layout:
10
+
11
+ ```text
12
+ docs/
13
+ ├── tutorials/ # learning-oriented, linear
14
+ │ └── getting-started.md
15
+ ├── how-to/ # task-oriented
16
+ │ ├── rotate-signing-key.md
17
+ │ └── configure-timeouts.md
18
+ ├── reference/ # lookup-oriented, exhaustive
19
+ │ ├── cli.md
20
+ │ └── config.md
21
+ └── explanation/ # understanding-oriented
22
+ └── why-cursor-pagination.md
23
+ ```
24
+
25
+ - A reader who knows their *intent* should land in the right folder immediately.
26
+ - Name files by the reader's task ("rotate-signing-key"), not the implementation ("key-service-internals").
27
+
28
+ ## Linking
29
+
30
+ - Link generously between types (tutorial → how-to → reference → explanation), but never inline another
31
+ type's full content. A link keeps each page single-purpose.
32
+ - Use descriptive link text ("see the CLI reference"), never "click here."
33
+ - Keep a single entry point (an index/README) that routes by reader intent.
34
+
35
+ ## Keeping docs from rotting
36
+
37
+ Documentation rots when it duplicates a source of truth that changes without it. Defenses, in order of
38
+ preference:
39
+
40
+ 1. **Generate from source.** CLI help, config schemas, API specs, and route tables should be generated
41
+ (or extracted) so they can't drift. Pair with the `update-docs` skill.
42
+ 2. **Link to source.** When you can't generate, link to the authoritative file instead of copying values.
43
+ 3. **Single source per fact.** A given default/flag/limit is documented in exactly one place; everything
44
+ else links to it.
45
+ 4. **Date and version.** Stamp docs with the version they describe so readers can judge staleness.
46
+
47
+ ## Review and verification cadence
48
+
49
+ - Re-run every example when the documented surface changes (a flag rename, a new required arg).
50
+ - Treat a failed copy-paste as a P1 doc bug — it destroys trust instantly.
51
+ - On each release, diff the changelog against the docs: any user-visible change needs a doc change.
52
+
53
+ ## Anti-patterns
54
+
55
+ - **Docs as a dumping ground:** an "FAQ" or "notes" page that accumulates everything no one filed properly.
56
+ - **Mirror-the-code structure:** folders named after services, so readers must understand the architecture
57
+ to find a task.
58
+ - **Copy-pasted constants:** the same timeout value written in five guides; change one, the rest lie.
59
+ - **Orphan pages:** docs with no inbound links, found only by search, never maintained.
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+ // Runnable readability signal for prose. Computes two cheap proxies for
3
+ // hard-to-read writing and flags text that trips either threshold:
4
+ // 1. average sentence length (words per sentence)
5
+ // 2. long-word ratio (share of words with >= LONG_WORD_LEN letters)
6
+ //
7
+ // These are intentionally simple, dependency-free heuristics — not a true
8
+ // Flesch score — meant to catch the dense, run-on prose the clarity pass exists
9
+ // to remove (see references/clarity-and-style.md).
10
+ //
11
+ // Usage:
12
+ // node readability.mjs "Some prose to score." # score a literal string
13
+ // echo "Some prose" | node readability.mjs # score from stdin
14
+ // node readability.mjs --selftest # run built-in test cases
15
+ //
16
+ // Exit code: 0 if the text reads clearly, 1 if it is flagged as hard to read.
17
+
18
+ const MAX_AVG_SENTENCE_WORDS = 25; // longer average => likely run-on prose
19
+ const MAX_LONG_WORD_RATIO = 0.2; // more than 20% long words => dense
20
+ const LONG_WORD_LEN = 12; // letters; "configuration" (13) counts
21
+
22
+ export function analyze(text) {
23
+ const sentences = splitSentences(text);
24
+ const words = (text.match(/[A-Za-z][A-Za-z'-]*/g) || []);
25
+ const wordCount = words.length;
26
+ const sentenceCount = Math.max(sentences.length, 1);
27
+ const avgSentenceWords = wordCount / sentenceCount;
28
+ const longWords = words.filter((w) => w.replace(/[^A-Za-z]/g, '').length >= LONG_WORD_LEN);
29
+ const longWordRatio = wordCount === 0 ? 0 : longWords.length / wordCount;
30
+
31
+ const reasons = [];
32
+ if (avgSentenceWords > MAX_AVG_SENTENCE_WORDS) {
33
+ reasons.push(`avg sentence length ${avgSentenceWords.toFixed(1)} words > ${MAX_AVG_SENTENCE_WORDS}`);
34
+ }
35
+ if (longWordRatio > MAX_LONG_WORD_RATIO) {
36
+ reasons.push(`long-word ratio ${(longWordRatio * 100).toFixed(0)}% > ${MAX_LONG_WORD_RATIO * 100}%`);
37
+ }
38
+
39
+ return {
40
+ ok: reasons.length === 0,
41
+ wordCount,
42
+ sentenceCount,
43
+ avgSentenceWords,
44
+ longWordRatio,
45
+ reasons,
46
+ };
47
+ }
48
+
49
+ function splitSentences(text) {
50
+ return text
51
+ .split(/[.!?]+/)
52
+ .map((s) => s.trim())
53
+ .filter((s) => s.length > 0);
54
+ }
55
+
56
+ function report(label, text) {
57
+ const r = analyze(text);
58
+ const stats = `avg ${r.avgSentenceWords.toFixed(1)} w/sentence, ${(r.longWordRatio * 100).toFixed(0)}% long words`;
59
+ if (r.ok) {
60
+ console.log(`OK ${label}: reads clearly (${stats})`);
61
+ } else {
62
+ console.error(`FLAG ${label}: hard to read (${stats})`);
63
+ r.reasons.forEach((reason) => console.error(` - ${reason}`));
64
+ }
65
+ return r.ok;
66
+ }
67
+
68
+ function selftest() {
69
+ const simple = 'Set the timeout. Raise it if requests stall. The default is thirty seconds.';
70
+ const dense =
71
+ 'Notwithstanding the aforementioned considerations regarding configuration, the implementation ' +
72
+ 'necessitates comprehensive understanding of the interdependent architectural characteristics ' +
73
+ 'whose interrelationships fundamentally determine the operational behaviour exhibited throughout ' +
74
+ 'the entirety of the distributed infrastructure under consideration herein presently.';
75
+
76
+ const cases = [
77
+ ['simple_text_passes', simple, true],
78
+ ['dense_text_flags', dense, false],
79
+ ];
80
+
81
+ let allExpected = true;
82
+ for (const [name, text, shouldPass] of cases) {
83
+ const { ok } = analyze(text);
84
+ const correct = ok === shouldPass;
85
+ allExpected = allExpected && correct;
86
+ console.log(
87
+ `${correct ? 'OK' : 'FAIL'} selftest ${name}: ${ok ? 'clear' : 'flagged'} ` +
88
+ `(expected ${shouldPass ? 'clear' : 'flagged'})`
89
+ );
90
+ }
91
+ process.exit(allExpected ? 0 : 1);
92
+ }
93
+
94
+ const arg = process.argv[2];
95
+ if (arg === '--selftest') {
96
+ selftest();
97
+ } else if (arg) {
98
+ process.exit(report('text', arg) ? 0 : 1);
99
+ } else {
100
+ let buf = '';
101
+ process.stdin.on('data', (c) => (buf += c));
102
+ process.stdin.on('end', () => process.exit(report('stdin', buf) ? 0 : 1));
103
+ }
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: test-author
3
+ description: Use PROACTIVELY when adding a feature or fixing a bug — writes tests FIRST (TDD red-green-refactor). Detects the repo's existing framework, captures the spec as failing AAA tests, drives implementation to green, and reports coverage against the 80% target with gaps called out.
4
+ tools: Read, Write, Edit, Bash, Grep
5
+ model: claude-sonnet-4-6
6
+ type: agent
7
+ version: 2.0.0
8
+ updated: 2026-06-29
9
+ ---
10
+ # Test Author
11
+
12
+ You are a disciplined test-driven-development engineer. Your job is to turn a described
13
+ behavior into **executable specification** — tests written *before* the implementation
14
+ exists — and then to drive that implementation to green without ever weakening the tests
15
+ to make them pass. You produce tests that fail for the right reason, pass once the code is
16
+ correct, and keep failing if the code regresses. You treat a test that cannot fail as worse
17
+ than no test at all.
18
+
19
+ You operate in any language. You never introduce a new test framework, runner, assertion
20
+ library, or fixture convention; you detect what the repository already uses and conform to
21
+ it exactly. Heavy reference detail lives in `references/`; copy-ready material in `examples/`;
22
+ a runnable coverage gate in `scripts/`.
23
+
24
+ ## Role and mandate
25
+
26
+ - **Tests come first.** No production code is written or accepted until a failing test
27
+ captures the intended behavior. If implementation already exists, you still write the
28
+ characterization or regression test first, observe it pass/fail honestly, and proceed.
29
+ - **Behavior over implementation.** You assert on observable outcomes — return values,
30
+ emitted events, persisted state, HTTP responses — never on private internals, call order,
31
+ or incidental structure that refactoring would break.
32
+ - **You are the spec's lawyer.** When a requirement is ambiguous, you enumerate the cases
33
+ explicitly (happy path, boundaries, error modes) and surface the ambiguity rather than
34
+ guessing silently.
35
+ - **Determinism is non-negotiable.** Every test must produce the same result on every run,
36
+ in any order, on any machine. Time, randomness, network, and filesystem are controlled.
37
+
38
+ ## TDD workflow: red → green → refactor
39
+
40
+ Follow this loop for every unit of behavior. Do not batch many behaviors into one giant test.
41
+
42
+ 1. **Detect conventions.** `Grep`/`Read` the repo to find the test runner, file naming
43
+ (`*.test.ts`, `*_test.go`, `test_*.py`), assertion style, and fixture/mocking patterns.
44
+ Match them. Detecting the framework wrong is the most common failure of this agent.
45
+ 2. **Decompose the behavior** into concrete cases: the happy path, every boundary
46
+ (empty, single, max, off-by-one), and every failure mode (invalid input, missing
47
+ dependency, thrown error). Write the list before writing code.
48
+ 3. **RED — write the test(s) first.** Use Arrange-Act-Assert. Give each test a name that
49
+ states the behavior under test. Run the suite and confirm it **fails for the expected
50
+ reason** (assertion failure or missing symbol — not a syntax error or import typo).
51
+ 4. **GREEN — minimal implementation.** Write the least code that makes the test pass.
52
+ Resist gold-plating; unneeded code has no test and is a liability.
53
+ 5. **REFACTOR — improve with the net up.** Clean up names, extract helpers, remove
54
+ duplication, while the suite stays green. Tests are the safety net that makes this safe.
55
+ 6. **Report.** Show the run output (RED first, then GREEN), the coverage delta against the
56
+ 80% target, and any remaining gaps or design smells you uncovered.
57
+
58
+ ## Arrange-Act-Assert structure
59
+
60
+ Every test has three visually separated phases and **one logical assertion target**:
61
+
62
+ - **Arrange** — build inputs, fixtures, and doubles. No assertions here.
63
+ - **Act** — invoke exactly one behavior, the thing under test.
64
+ - **Assert** — verify the observable outcome. Prefer one concept per test; multiple
65
+ `expect` lines are fine if they describe one behavior.
66
+
67
+ ```ts
68
+ // Vitest / Jest — a focused unit test written BEFORE the implementation
69
+ import { describe, it, expect } from 'vitest';
70
+ import { applyDiscount } from '../src/pricing';
71
+
72
+ describe('applyDiscount', () => {
73
+ it('subtracts a percentage discount and rounds to 2 decimals', () => {
74
+ // Arrange
75
+ const price = 49.99;
76
+ const percentOff = 10;
77
+
78
+ // Act
79
+ const result = applyDiscount(price, percentOff);
80
+
81
+ // Assert
82
+ expect(result).toBe(44.99);
83
+ });
84
+
85
+ it('throws RangeError when the discount exceeds 100 percent', () => {
86
+ // Arrange
87
+ const price = 10;
88
+
89
+ // Act + Assert (the throw IS the observable behavior)
90
+ expect(() => applyDiscount(price, 150)).toThrow(RangeError);
91
+ });
92
+
93
+ it('returns the original price when the discount is zero', () => {
94
+ expect(applyDiscount(20, 0)).toBe(20); // boundary: identity
95
+ });
96
+ });
97
+ ```
98
+
99
+ ## Output format
100
+
101
+ Deliver, in order:
102
+
103
+ 1. **Case list** — the behaviors you are about to test (happy / boundary / failure).
104
+ 2. **Test files** — full contents, matching repo conventions, written first.
105
+ 3. **A test plan** — what is covered, what is intentionally out of scope, and how to run it.
106
+ 4. **Run evidence** — the RED output, then the GREEN output after implementation.
107
+ 5. **Coverage summary** — percentage vs. the 80% target and named gaps.
108
+
109
+ ```md
110
+ ## Test plan: refund eligibility
111
+
112
+ | Case | Type | Expected |
113
+ |---|---|---|
114
+ | order within 30 days, unused | happy | eligible = true |
115
+ | order on day 30 exactly | boundary | eligible = true |
116
+ | order on day 31 | boundary | eligible = false |
117
+ | already refunded order | failure | throws AlreadyRefunded |
118
+ | missing order id | failure | throws NotFound |
119
+
120
+ Out of scope: partial refunds (tracked separately).
121
+ Run: `npm test -- refund` · Coverage gate: `node scripts/check-coverage.mjs coverage/coverage-summary.json`
122
+ ```
123
+
124
+ ## Self-check before you finish
125
+
126
+ - [ ] Did each test **fail first** for the right reason? (A test never seen red is suspect.)
127
+ - [ ] Does every test assert a real observable outcome — not "it ran without throwing"?
128
+ - [ ] Are boundaries and error modes covered, not just the happy path?
129
+ - [ ] Is every test deterministic and isolated (no shared mutable state, no real I/O)?
130
+ - [ ] Did I avoid asserting on private internals that refactoring would break?
131
+ - [ ] Does coverage meet 80%, and are remaining gaps named rather than hidden?
132
+
133
+ ## Common pitfalls (failure modes to avoid)
134
+
135
+ - **Testing implementation, not behavior.** Asserting that a private method was called, or
136
+ snapshotting internal structure, couples the test to the code's shape. Refactoring then
137
+ breaks green tests even though behavior is unchanged. Assert on outputs and effects.
138
+ - **Asserting nothing.** A test that calls a function and never checks the result only
139
+ proves "it didn't throw." It passes for broken code. Every test needs a real assertion.
140
+ - **No edge cases.** Happy-path-only suites give false confidence. The bugs live at the
141
+ boundaries — empty collections, zero, negative, max, off-by-one, null — and in error
142
+ paths. Enumerate them explicitly.
143
+ - **Flaky tests.** Dependence on wall-clock time, real network, random seeds, ordering, or
144
+ unawaited async makes a test pass-then-fail at random. A flaky test is noise that trains
145
+ the team to ignore red. Freeze time, seed RNG, mock the boundary, await everything.
146
+ - **Over-mocking.** Mocking the thing under test, or mocking so deeply the test only
147
+ verifies the mock, tests nothing real. Mock at the system boundary only.
148
+ - **Tests that never went red.** If you write a test against already-working code without
149
+ watching it fail, you can't trust it catches the bug. Break the code or invert the assert
150
+ once to confirm the test bites.
151
+
152
+ ## When NOT to use / boundaries
153
+
154
+ - **Not for exhaustive E2E or browser flows** — for full user-journey testing through a real
155
+ browser, hand off to the **e2e-playwright** skill. This agent owns unit and integration
156
+ tests close to the code.
157
+ - **Not a code reviewer** — it writes and runs tests; it does not perform security or
158
+ architecture review. Pair it with the **code-reviewer** agent afterward.
159
+ - **Not for throwaway spikes** — during a genuine exploratory prototype that will be
160
+ deleted, strict TDD can be deferred; write tests before the code is promoted to keep.
161
+ - **It will not weaken a test to force green.** If a test is hard to write, that signals a
162
+ design problem; it surfaces the smell instead of deleting the assertion.
163
+
164
+ ## Files in this package
165
+
166
+ - `AGENT.md` — this system prompt
167
+ - `references/test-types-and-pyramid.md` — unit vs integration vs E2E, the test pyramid, what to put where
168
+ - `references/aaa-and-naming.md` — Arrange-Act-Assert in depth and behavior-driven test names
169
+ - `references/mocking-and-fixtures.md` — test doubles, fixtures, and controlling time/randomness/I/O
170
+ - `examples/unit-test-example.ts` — a complete, runnable-shaped AAA unit test
171
+ - `examples/test-plan.md` — a filled-in test plan template
172
+ - `scripts/check-coverage.mjs` — Node coverage gate that fails below a threshold (default 80%); `--selftest` exits 0/1
173
+
174
+ Pairs with the **e2e-playwright** skill (full browser journeys) and the **code-reviewer**
175
+ agent (quality and security pass after tests are green). See
176
+ [references/test-types-and-pyramid.md](references/test-types-and-pyramid.md) for where each
177
+ layer belongs.
@@ -0,0 +1,54 @@
1
+ # Test Plan: Refund Eligibility
2
+
3
+ A filled-in example of the test plan this agent produces before writing code. Copy the shape
4
+ for any new behavior: enumerate cases by type, mark scope, and state how to run + gate.
5
+
6
+ ## Behavior under test
7
+
8
+ `isRefundEligible(order, now)` decides whether an order can be refunded. An order is eligible
9
+ when it was placed within the last 30 days, is unused, and has not already been refunded.
10
+
11
+ ## Cases
12
+
13
+ | # | Case | Type | Expected |
14
+ |---|---|---|---|
15
+ | 1 | order placed 5 days ago, unused | happy | `true` |
16
+ | 2 | order placed exactly 30 days ago | boundary | `true` (inclusive) |
17
+ | 3 | order placed 31 days ago | boundary | `false` |
18
+ | 4 | order placed 0 days ago (today) | boundary | `true` |
19
+ | 5 | order already marked refunded | failure | throws `AlreadyRefunded` |
20
+ | 6 | order is `null` / missing | failure | throws `NotFound` |
21
+ | 7 | order used (redeemed) | edge | `false` |
22
+
23
+ ## Determinism notes
24
+
25
+ - `now` is **injected**, not read from the system clock, so the 30-day boundary tests are
26
+ stable. The suite freezes `now` to `2026-06-29T12:00:00Z`.
27
+ - No network or DB access — `order` is built by a `makeOrder(overrides)` factory.
28
+
29
+ ## Out of scope (tracked separately)
30
+
31
+ - Partial refunds and refund *amount* calculation — separate behavior, separate plan.
32
+ - Payment-provider interaction — covered by an integration test, not this unit.
33
+
34
+ ## Coverage target
35
+
36
+ 80% minimum (branch coverage emphasized — every `true`/`false`/throw path above is exercised).
37
+
38
+ ## How to run
39
+
40
+ ```bash
41
+ # Run just this behavior's tests
42
+ npm test -- refund
43
+
44
+ # Gate coverage in CI (fails below 80%)
45
+ node scripts/check-coverage.mjs coverage/coverage-summary.json --min=80
46
+ ```
47
+
48
+ ## Red → Green evidence (to be filled at implementation time)
49
+
50
+ ```text
51
+ RED: 5 failing — isRefundEligible is not defined
52
+ GREEN: 7 passing — statements 92% · branches 88% · target 80% met
53
+ Gaps: none for this behavior; partial-refund path intentionally deferred
54
+ ```
@@ -0,0 +1,55 @@
1
+ // A complete, AAA-structured unit test written BEFORE the implementation.
2
+ // Framework-agnostic shape shown with Vitest/Jest-style globals — adapt imports to the
3
+ // runner the repository already uses. The point is the structure, not the library.
4
+ //
5
+ // Behavior under test: applyDiscount(price, percentOff)
6
+ // - subtracts a percentage discount and rounds to 2 decimal places
7
+ // - returns the original price when percentOff is 0 (boundary: identity)
8
+ // - returns 0 when percentOff is 100 (boundary: full discount)
9
+ // - throws RangeError when percentOff < 0 or > 100 (failure mode)
10
+
11
+ import { describe, it, expect } from 'vitest';
12
+ import { applyDiscount } from '../src/pricing';
13
+
14
+ // A small factory keeps tests readable and resilient to signature changes.
15
+ const cents = (n: number) => Math.round(n * 100) / 100;
16
+
17
+ describe('applyDiscount', () => {
18
+ // --- Happy path ---------------------------------------------------------
19
+ it('subtracts a percentage discount and rounds to two decimals', () => {
20
+ // Arrange
21
+ const price = 49.99;
22
+ const percentOff = 10;
23
+
24
+ // Act
25
+ const result = applyDiscount(price, percentOff);
26
+
27
+ // Assert
28
+ expect(result).toBe(cents(44.991)); // 44.99
29
+ });
30
+
31
+ // --- Boundaries ---------------------------------------------------------
32
+ it('returns the original price when the discount is zero', () => {
33
+ expect(applyDiscount(20, 0)).toBe(20);
34
+ });
35
+
36
+ it('returns zero when the discount is one hundred percent', () => {
37
+ expect(applyDiscount(20, 100)).toBe(0);
38
+ });
39
+
40
+ // --- Failure modes ------------------------------------------------------
41
+ it('throws RangeError when the discount exceeds one hundred percent', () => {
42
+ // The throw IS the observable behavior — assert on it directly.
43
+ expect(() => applyDiscount(10, 150)).toThrow(RangeError);
44
+ });
45
+
46
+ it('throws RangeError when the discount is negative', () => {
47
+ expect(() => applyDiscount(10, -5)).toThrow(RangeError);
48
+ });
49
+ });
50
+
51
+ // Notes on why this is a good test file:
52
+ // - Each test has one Act and asserts a real, observable outcome (no "didn't throw" tests).
53
+ // - Boundaries (0, 100) and error modes (negative, > 100) are covered, not just happy path.
54
+ // - No time, randomness, network, or shared state — fully deterministic and order-independent.
55
+ // - Names state the behavior, so the suite reads as a specification of applyDiscount.
@@ -0,0 +1,75 @@
1
+ # Arrange-Act-Assert and Test Naming
2
+
3
+ A good test reads like a specification. Two habits get you most of the way: a consistent
4
+ three-phase body (AAA) and a name that states the behavior, not the method.
5
+
6
+ ## Arrange-Act-Assert (AAA)
7
+
8
+ Structure every test in three visually separated phases:
9
+
10
+ ```ts
11
+ it('caps the cart total at the configured maximum', () => {
12
+ // Arrange — set up inputs, fixtures, doubles
13
+ const cart = makeCart({ items: 5, unitPrice: 100 });
14
+ const maxTotal = 400;
15
+
16
+ // Act — invoke exactly ONE behavior
17
+ const total = cart.totalWithCap(maxTotal);
18
+
19
+ // Assert — verify the observable outcome
20
+ expect(total).toBe(400);
21
+ });
22
+ ```
23
+
24
+ Why it works:
25
+
26
+ - **One Act per test.** If you need two actions, you probably have two tests. A single Act
27
+ keeps failures unambiguous — you know exactly what broke.
28
+ - **Assertions only in Assert.** An assertion buried in Arrange hides setup failures as
29
+ behavior failures.
30
+ - **No logic in tests.** Loops, conditionals, and try/catch in a test are a smell; they can
31
+ hide bugs in the test itself. Prefer table-driven cases or parameterized tests instead.
32
+
33
+ ## Given-When-Then
34
+
35
+ The same shape, phrased for behavior-driven tests: *Given* a context, *When* an action
36
+ occurs, *Then* an outcome is observed. Map Given→Arrange, When→Act, Then→Assert.
37
+
38
+ ## Naming tests
39
+
40
+ A test name should let a reader understand the requirement **without reading the body**.
41
+ State the scenario and the expected outcome.
42
+
43
+ **Weak (restates the method):**
44
+ ```
45
+ test('applyDiscount')
46
+ test('test refund')
47
+ test('works')
48
+ ```
49
+
50
+ **Strong (states the behavior):**
51
+ ```
52
+ test('subtracts a percentage discount and rounds to two decimals')
53
+ test('throws RangeError when the discount exceeds 100 percent')
54
+ test('returns empty array when no markets match the query')
55
+ test('falls back to substring search when Redis is unavailable')
56
+ ```
57
+
58
+ A useful template: **`<does X> when <condition Y>`** or **`<verb> <expected> given <state>`**.
59
+
60
+ ## One behavior per test
61
+
62
+ When a test fails, its name plus its single Act should tell you what's broken without
63
+ debugging. Resist the "mega test" that arranges a huge world and asserts twenty things — the
64
+ first failure masks the rest, and the name can't describe what it covers. Split by behavior.
65
+
66
+ ## Multiple assertions are fine — if they describe one behavior
67
+
68
+ ```ts
69
+ // OK: these three assertions all describe "creates an open order"
70
+ expect(order.id).toBeDefined();
71
+ expect(order.status).toBe('open');
72
+ expect(order.createdAt).toBeInstanceOf(Date);
73
+ ```
74
+
75
+ The guideline is *one logical concept per test*, not literally one `expect`.
@@ -0,0 +1,94 @@
1
+ # Mocking, Fixtures, and Determinism
2
+
3
+ Tests must be **isolated** (independent of each other) and **deterministic** (same result
4
+ every run). That means controlling everything that crosses a boundary or varies between runs:
5
+ time, randomness, network, filesystem, and shared collaborators.
6
+
7
+ ## Test doubles — know which one you need
8
+
9
+ | Double | Purpose |
10
+ |---|---|
11
+ | **Dummy** | Filler passed to satisfy a signature; never used. |
12
+ | **Stub** | Returns canned answers to calls made during the test. |
13
+ | **Spy** | A stub that also records how it was called. |
14
+ | **Mock** | A double with pre-set expectations that verifies interactions. |
15
+ | **Fake** | A working but lightweight implementation (in-memory DB, fake clock). |
16
+
17
+ Reach for the **simplest** double that does the job. Prefer **fakes and stubs** over strict
18
+ mocks: asserting on exact call sequences couples the test to implementation and makes
19
+ refactoring break green tests.
20
+
21
+ ## Mock at the boundary, only the boundary
22
+
23
+ ```ts
24
+ // GOOD: mock the HTTP client (a true external boundary)
25
+ const httpGet = vi.fn().mockResolvedValue({ status: 200, body: { rate: 1.1 } });
26
+ const rate = await fetchExchangeRate(httpGet, 'USD', 'EUR');
27
+ expect(rate).toBe(1.1);
28
+ ```
29
+
30
+ ```ts
31
+ // BAD: mocking the function under test leaves nothing real to verify
32
+ const fetchExchangeRate = vi.fn().mockResolvedValue(1.1); // tests the mock, not the code
33
+ ```
34
+
35
+ Over-mocking is a top cause of tests that pass while the system is broken. If a test is
36
+ almost entirely mock setup, you are testing the mock framework. Either widen the unit or move
37
+ the check to an integration test with a real collaborator.
38
+
39
+ ## Controlling time
40
+
41
+ Wall-clock time is the most common flakiness source. Inject a clock or freeze it:
42
+
43
+ ```ts
44
+ import { vi } from 'vitest';
45
+
46
+ vi.useFakeTimers();
47
+ vi.setSystemTime(new Date('2026-06-29T12:00:00Z'));
48
+
49
+ expect(isExpired(token)).toBe(false);
50
+
51
+ vi.advanceTimersByTime(60_000); // jump 60s deterministically
52
+ expect(isExpired(token)).toBe(true);
53
+
54
+ vi.useRealTimers(); // always restore
55
+ ```
56
+
57
+ Never assert on `Date.now()` or sleep with real timeouts — that makes tests slow and racy.
58
+
59
+ ## Controlling randomness
60
+
61
+ Seed the RNG or inject it. A test that depends on `Math.random()` passes-then-fails at random:
62
+
63
+ ```ts
64
+ const rng = () => 0.42; // injected, deterministic
65
+ expect(pickWinner(players, rng)).toBe('alice');
66
+ ```
67
+
68
+ ## Fixtures and factories
69
+
70
+ - **Factories over literals.** A `makeOrder(overrides)` helper keeps tests readable and
71
+ resilient — when a required field is added, you change the factory once, not 50 tests.
72
+ - **Build only what the test needs.** Override just the fields relevant to the behavior;
73
+ let the factory default the rest. This makes the *intent* of each test obvious.
74
+ - **Isolate state.** Reset shared resources (DB rows, in-memory stores, module mocks) in
75
+ `beforeEach`/`afterEach` so tests can run in any order. Order-dependent tests are a bug.
76
+
77
+ ```ts
78
+ function makeOrder(overrides = {}) {
79
+ return { id: 'ord_1', status: 'open', total: 100, createdAt: new Date(0), ...overrides };
80
+ }
81
+
82
+ it('marks a paid order as fulfilled', () => {
83
+ const order = makeOrder({ status: 'paid' }); // only the relevant field is set
84
+ expect(fulfill(order).status).toBe('fulfilled');
85
+ });
86
+ ```
87
+
88
+ ## Determinism checklist
89
+
90
+ - [ ] No real network, filesystem, or DB in unit tests (use fakes/stubs).
91
+ - [ ] Time and randomness are frozen or injected.
92
+ - [ ] No shared mutable state leaks between tests; setup/teardown resets it.
93
+ - [ ] All async work is awaited — no dangling promises or unhandled timers.
94
+ - [ ] The suite passes when run in a randomized order.
@@ -0,0 +1,60 @@
1
+ # Test Types and the Test Pyramid
2
+
3
+ Not all tests are equal. Choosing the right *level* for a given behavior is the difference
4
+ between a suite that gives fast, reliable signal and one that is slow, flaky, and ignored.
5
+
6
+ ## The pyramid
7
+
8
+ ```
9
+ /\ E2E / UI few, slow, high-confidence, brittle
10
+ / \
11
+ /----\ Integration some, medium speed, real collaborators
12
+ / \
13
+ /--------\ Unit many, fast, isolated, cheap
14
+ ```
15
+
16
+ Push the bulk of your assertions **down** to the unit level where they are fast and stable.
17
+ Use the higher levels sparingly to prove the pieces wire together, not to re-test logic that
18
+ unit tests already cover. An inverted pyramid (mostly E2E) is slow and flaky.
19
+
20
+ ## Unit tests
21
+
22
+ - **Scope:** one function, method, or class in isolation. Collaborators that cross a process
23
+ or I/O boundary are replaced with doubles.
24
+ - **Cost:** milliseconds. Run thousands on every save.
25
+ - **Owns:** business logic, pure functions, edge cases, error branches, algorithms.
26
+ - **Rule of thumb:** if a behavior can be tested at the unit level, test it there.
27
+
28
+ ## Integration tests
29
+
30
+ - **Scope:** several real units working together, or one unit against a real adjacent
31
+ dependency (a database, an in-memory queue, a local HTTP server).
32
+ - **Cost:** tens to hundreds of milliseconds. Often need setup/teardown.
33
+ - **Owns:** the seams — ORM queries against a real schema, repository wiring, serialization,
34
+ framework request/response handling, transaction behavior.
35
+ - **Avoid:** re-testing branch logic here that a unit test already covers; keep these about
36
+ the *wiring*, not the *rules*.
37
+
38
+ ## End-to-end (E2E) tests
39
+
40
+ - **Scope:** the whole system through its real entry point — a browser, a CLI, a public API.
41
+ - **Cost:** seconds. Slow, infrastructure-heavy, the most prone to flakiness.
42
+ - **Owns:** a handful of critical user journeys (sign up, checkout, the money path).
43
+ - **Hand off:** browser-driven E2E belongs to the **e2e-playwright** skill, not this agent.
44
+
45
+ ## What goes where — a decision guide
46
+
47
+ | Question | Level |
48
+ |---|---|
49
+ | Does `applyDiscount(49.99, 10)` round correctly? | Unit |
50
+ | Does an invalid percentage throw `RangeError`? | Unit |
51
+ | Does the repository persist and re-read an order from Postgres? | Integration |
52
+ | Does `POST /orders` return `201` with a `Location` header? | Integration |
53
+ | Can a user add to cart and complete checkout in the browser? | E2E |
54
+
55
+ ## Coverage as a signal, not a goal
56
+
57
+ The 80% target is a floor that flags untested regions, not a trophy. 100% line coverage with
58
+ assertion-free tests proves nothing. Chase **branch and behavior** coverage — every error
59
+ path and boundary — over raw line percentage. Use `scripts/check-coverage.mjs` to gate the
60
+ floor in CI, and read the uncovered lines to find the cases you forgot.