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,213 @@
1
+ #!/usr/bin/env node
2
+ // suggest-index.mjs
3
+ // Propose a composite B-tree index from a query's WHERE / ORDER BY columns,
4
+ // honoring two rules from this skill:
5
+ // 1. equality-before-range -- equality columns first, then a single range column
6
+ // 2. leftmost-prefix -- the proposed order is what the index can seek on
7
+ // Equality columns are ordered most-selective-first when a "cardinality" hint is
8
+ // given (higher distinct-count => more selective => leads). ORDER BY columns not
9
+ // already in the key are appended so the sort can be served from the index.
10
+ //
11
+ // Zero dependencies. Node built-ins only.
12
+ //
13
+ // Usage:
14
+ // node suggest-index.mjs '<json-spec>' # propose from a literal JSON spec
15
+ // echo '<json-spec>' | node suggest-index.mjs # propose from stdin
16
+ // node suggest-index.mjs --selftest # run built-in assertions (exit 0/1)
17
+ //
18
+ // Spec shape:
19
+ // {
20
+ // "table": "orders",
21
+ // "predicates": [
22
+ // { "column": "tenant_id", "op": "eq", "cardinality": 5000 },
23
+ // { "column": "status", "op": "eq", "cardinality": 5 },
24
+ // { "column": "created_at", "op": "range" }
25
+ // ],
26
+ // "orderBy": [ { "column": "created_at", "direction": "desc" } ]
27
+ // }
28
+ // op is one of: "eq" (equality) | "range" (>, <, BETWEEN, LIKE 'x%').
29
+
30
+ const RANGE_OPS = new Set(['range', 'gt', 'lt', 'gte', 'lte', 'between', 'like_prefix']);
31
+ const EQ_OPS = new Set(['eq', 'equals', '=']);
32
+
33
+ export function suggestIndex(spec) {
34
+ const warnings = [];
35
+ if (!spec || typeof spec !== 'object') throw new Error('spec must be an object');
36
+
37
+ const table = typeof spec.table === 'string' && spec.table ? spec.table : 'my_table';
38
+ const predicates = Array.isArray(spec.predicates) ? spec.predicates : [];
39
+ const orderBy = Array.isArray(spec.orderBy) ? spec.orderBy : [];
40
+
41
+ const equality = predicates.filter((p) => EQ_OPS.has(p.op));
42
+ const ranges = predicates.filter((p) => RANGE_OPS.has(p.op));
43
+ const unknown = predicates.filter((p) => !EQ_OPS.has(p.op) && !RANGE_OPS.has(p.op));
44
+ for (const p of unknown) warnings.push(`predicate on "${p.column}" has unknown op "${p.op}"; ignored`);
45
+
46
+ // 1. Equality columns first, most-selective (highest cardinality) first.
47
+ // Stable: ties and missing cardinality preserve input order.
48
+ const eqSorted = equality
49
+ .map((p, i) => ({ p, i }))
50
+ .sort((a, b) => {
51
+ const ca = num(a.p.cardinality);
52
+ const cb = num(b.p.cardinality);
53
+ if (ca !== cb) return cb - ca; // higher cardinality leads
54
+ return a.i - b.i;
55
+ })
56
+ .map((x) => x.p.column);
57
+
58
+ // 2. At most ONE range column is useful in the seek; the rest become filters.
59
+ let rangeCol = null;
60
+ if (ranges.length > 0) {
61
+ rangeCol = ranges[0].column;
62
+ for (let i = 1; i < ranges.length; i++) {
63
+ warnings.push(
64
+ `multiple range predicates; only "${rangeCol}" can seek -- "${ranges[i].column}" becomes a filter`,
65
+ );
66
+ }
67
+ }
68
+
69
+ // 3. ORDER BY columns not already present, appended so the sort is index-served.
70
+ const columns = [...eqSorted];
71
+ if (rangeCol) columns.push(rangeCol);
72
+ for (const o of orderBy) {
73
+ const col = typeof o === 'string' ? o : o && o.column;
74
+ if (col && !columns.includes(col)) columns.push(col);
75
+ }
76
+
77
+ if (columns.length === 0) {
78
+ warnings.push('no usable equality/range/order-by columns found; nothing to index');
79
+ return { table, columns, ddl: null, warnings };
80
+ }
81
+
82
+ // Carry ORDER BY direction onto matching trailing columns (DESC needs it stated).
83
+ const dir = new Map();
84
+ for (const o of orderBy) {
85
+ if (o && typeof o === 'object' && o.column && o.direction) {
86
+ dir.set(o.column, String(o.direction).toLowerCase() === 'desc' ? 'DESC' : 'ASC');
87
+ }
88
+ }
89
+ const rendered = columns.map((c) => (dir.get(c) === 'DESC' ? `${c} DESC` : c));
90
+
91
+ const indexName = `ix_${table}_${columns.join('_')}`.slice(0, 63);
92
+ const ddl = `CREATE INDEX CONCURRENTLY ${indexName}\n ON ${table} (${rendered.join(', ')});`;
93
+
94
+ if (rangeCol && eqSorted.length === 0) {
95
+ warnings.push('no equality columns -- a leading range column limits selectivity; verify it pays off');
96
+ }
97
+
98
+ return { table, columns, ddl, warnings };
99
+ }
100
+
101
+ function num(v) {
102
+ const n = Number(v);
103
+ return Number.isFinite(n) ? n : -1; // missing cardinality sorts last (-1)
104
+ }
105
+
106
+ function run(json) {
107
+ let spec;
108
+ try {
109
+ spec = typeof json === 'string' ? JSON.parse(json) : json;
110
+ } catch {
111
+ console.error('✗ invalid JSON spec');
112
+ return false;
113
+ }
114
+ let result;
115
+ try {
116
+ result = suggestIndex(spec);
117
+ } catch (e) {
118
+ console.error(`✗ ${e.message}`);
119
+ return false;
120
+ }
121
+ console.log(`Proposed index columns: (${result.columns.join(', ')})`);
122
+ if (result.ddl) console.log(result.ddl);
123
+ for (const w of result.warnings) console.log(`! ${w}`);
124
+ return true;
125
+ }
126
+
127
+ function selftest() {
128
+ const cases = [
129
+ {
130
+ name: 'equality-before-range + selectivity ordering',
131
+ spec: {
132
+ table: 'orders',
133
+ predicates: [
134
+ { column: 'status', op: 'eq', cardinality: 5 },
135
+ { column: 'tenant_id', op: 'eq', cardinality: 5000 },
136
+ { column: 'created_at', op: 'range' },
137
+ ],
138
+ orderBy: [{ column: 'created_at', direction: 'desc' }],
139
+ },
140
+ // tenant_id (higher cardinality) leads, then status, then the range/sort col
141
+ expectColumns: ['tenant_id', 'status', 'created_at'],
142
+ expectDdlIncludes: 'created_at DESC',
143
+ },
144
+ {
145
+ name: 'order-by column appended when not already in key',
146
+ spec: {
147
+ table: 'events',
148
+ predicates: [{ column: 'user_id', op: 'eq' }],
149
+ orderBy: [{ column: 'occurred_at', direction: 'asc' }],
150
+ },
151
+ expectColumns: ['user_id', 'occurred_at'],
152
+ },
153
+ {
154
+ name: 'preserve input order when no cardinality hints',
155
+ spec: {
156
+ table: 'memberships',
157
+ predicates: [
158
+ { column: 'tenant_id', op: 'eq' },
159
+ { column: 'role', op: 'eq' },
160
+ ],
161
+ },
162
+ expectColumns: ['tenant_id', 'role'],
163
+ },
164
+ {
165
+ name: 'second range predicate warns and becomes a filter',
166
+ spec: {
167
+ table: 'orders',
168
+ predicates: [
169
+ { column: 'tenant_id', op: 'eq' },
170
+ { column: 'created_at', op: 'range' },
171
+ { column: 'total_cents', op: 'range' },
172
+ ],
173
+ },
174
+ expectColumns: ['tenant_id', 'created_at'],
175
+ expectWarningIncludes: 'total_cents',
176
+ },
177
+ {
178
+ name: 'empty predicates yields no index',
179
+ spec: { table: 'orders', predicates: [], orderBy: [] },
180
+ expectColumns: [],
181
+ expectNullDdl: true,
182
+ },
183
+ ];
184
+
185
+ let allOk = true;
186
+ for (const c of cases) {
187
+ const r = suggestIndex(c.spec);
188
+ let ok = arrEq(r.columns, c.expectColumns);
189
+ if (ok && c.expectDdlIncludes) ok = !!r.ddl && r.ddl.includes(c.expectDdlIncludes);
190
+ if (ok && c.expectWarningIncludes) ok = r.warnings.some((w) => w.includes(c.expectWarningIncludes));
191
+ if (ok && c.expectNullDdl) ok = r.ddl === null;
192
+ allOk = allOk && ok;
193
+ console.log(
194
+ `${ok ? '✓' : '✗'} ${c.name}: got (${r.columns.join(', ')})` +
195
+ (ok ? '' : ` -- expected (${c.expectColumns.join(', ')})`),
196
+ );
197
+ }
198
+ console.log(allOk ? 'All selftests passed.' : 'Selftest FAILURES present.');
199
+ process.exit(allOk ? 0 : 1);
200
+ }
201
+
202
+ function arrEq(a, b) {
203
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);
204
+ }
205
+
206
+ const arg = process.argv[2];
207
+ if (arg === '--selftest') selftest();
208
+ else if (arg) process.exit(run(arg) ? 0 : 1);
209
+ else {
210
+ let buf = '';
211
+ process.stdin.on('data', (c) => (buf += c));
212
+ process.stdin.on('end', () => process.exit(run(buf) ? 0 : 1));
213
+ }
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: test-plan-design
3
+ description: How to design a test plan — scope and risk-based prioritization, the test pyramid, case-design techniques (equivalence partitioning, boundary values, decision tables), entry/exit criteria, coverage and requirement traceability, test data and environments. Deep reference with a runnable coverage-gap check.
4
+ type: skill
5
+ version: 2.0.0
6
+ updated: 2026-06-29
7
+ ---
8
+ # Test Plan Design
9
+
10
+ A test plan is a **budget**: finite effort spent where it prevents the most expensive failures. The goal
11
+ is never "test everything" — that is impossible and economically illiterate. The goal is to make a
12
+ defensible decision about *what* to test, *at which layer*, *with which cases*, and *when you are done*.
13
+ This skill is the deep reference for that decision. Heavy detail lives in `references/`; copy-paste
14
+ material in `examples/`; a runnable traceability check in `scripts/`.
15
+
16
+ ## Mental model
17
+
18
+ You are allocating scarce attention across a feature. Four questions drive every plan:
19
+
20
+ | Question | Answer comes from |
21
+ |---|---|
22
+ | What matters most? | risk = impact × likelihood (scope & prioritization) |
23
+ | At which layer do I test it? | the test pyramid (unit / integration / E2E) |
24
+ | Which concrete cases? | case-design techniques (partitions, boundaries, decision tables) |
25
+ | When am I done? | exit criteria + coverage traced to requirements |
26
+
27
+ Skip any one of these and the plan degrades: no risk ranking and you over-test trivia; no case design and
28
+ you write five tests that all exercise the same path; no traceability and you ship a requirement nobody
29
+ verified.
30
+
31
+ ## 1. Scope and risk-based prioritization
32
+
33
+ Start by listing what the feature must do, then rank each item by **risk = impact × likelihood**.
34
+
35
+ - **Impact**: what does failure cost? Money movement, data loss, security, and legal/compliance are
36
+ top-tier; cosmetic glitches are bottom-tier.
37
+ - **Likelihood**: how probable is a defect? New code, complex logic, many integrations, and unclear
38
+ requirements raise it; stable, simple, well-understood code lowers it.
39
+
40
+ Spend the most rigor at the top of the ranked list and explicitly decide to *under*-test the bottom.
41
+ Writing down "we are not testing X because it is low-risk" is a feature of a good plan, not a gap. The
42
+ full scoring rubric and a worked heat-map live in [references/risk-prioritization.md](references/risk-prioritization.md).
43
+
44
+ ## 2. The test pyramid — choosing a layer
45
+
46
+ Each behavior should be tested at the **lowest layer that can meaningfully verify it**. Lower layers are
47
+ faster, cheaper, and more precise about *where* a defect is.
48
+
49
+ ```
50
+ /\ E2E few — full user journeys, real-ish stack, slow & flaky
51
+ / \ Integration some — module + its collaborators (DB, gateway, queue)
52
+ /____\ Unit many — one function/class, no I/O, milliseconds
53
+ ```
54
+
55
+ - **Unit**: pure logic — pricing math, validation, state machines. No network, no DB. Run in ms.
56
+ - **Integration**: a module talking to a real (or faithfully faked) collaborator — a repository hitting a
57
+ test DB, a client calling a mocked gateway with success/decline/timeout responses.
58
+ - **E2E**: a thin layer covering only critical journeys end to end (e.g. "add to cart → pay → confirm").
59
+
60
+ The classic anti-pattern is the **inverted pyramid** (an "ice-cream cone"): many slow E2E tests and few
61
+ units. It is slow, flaky, and tells you *that* something broke but not *where*. Layer-selection heuristics
62
+ and the integration/contract-test distinction are in [references/test-pyramid.md](references/test-pyramid.md).
63
+
64
+ ## 3. Case-design techniques
65
+
66
+ For each unit under test, do not invent cases ad hoc — derive them. Three techniques cover the vast
67
+ majority of input-space reasoning:
68
+
69
+ - **Equivalence partitioning**: split the input domain into classes that should behave identically, then
70
+ test *one* representative per class. If ages 18–65 are all "standard", you need one value from that
71
+ band, not forty.
72
+ - **Boundary value analysis**: bugs cluster at edges. For a valid range `[18, 65]`, test `17, 18, 19` and
73
+ `64, 65, 66` — just-below, on, and just-above each boundary. Off-by-one errors live here.
74
+ - **Decision tables**: when output depends on a combination of conditions, enumerate the combinations so
75
+ no rule is missed.
76
+
77
+ ```text
78
+ Discount rules (decision table)
79
+ Member? | Cart ≥ $100 | Coupon valid | → Discount
80
+ no | no | no | 0%
81
+ no | yes | no | 5%
82
+ yes | no | no | 10%
83
+ yes | yes | yes | 25% ← interaction case, easy to miss
84
+ ```
85
+
86
+ A worked example combining all three on one function is in [references/case-design.md](references/case-design.md),
87
+ and a ready-to-fill case template is in `examples/test-case-template.md`.
88
+
89
+ ## 4. Entry and exit criteria
90
+
91
+ Make "ready to test" and "ready to ship" explicit so the plan ends on data, not vibes.
92
+
93
+ - **Entry criteria** (can we start?): code merged to the test branch, build green, test environment and
94
+ seed data available, dependencies/stubs deployed.
95
+ - **Exit criteria** (can we stop?): all planned cases executed; all CRITICAL/HIGH-risk requirements have a
96
+ passing test; coverage target met (e.g. 80% line on changed code); zero open Sev-1/Sev-2 defects; known
97
+ issues triaged and accepted by the owner.
98
+
99
+ Exit criteria are the contract that prevents both premature shipping and endless gold-plating.
100
+
101
+ ## 5. Coverage and traceability to requirements
102
+
103
+ Coverage has two distinct meanings — track both:
104
+
105
+ 1. **Code coverage** (line/branch): a *necessary* signal, not a *sufficient* one. 100% line coverage with
106
+ zero assertions proves nothing.
107
+ 2. **Requirement coverage**: every requirement maps to at least one test that verifies it. This is the one
108
+ that catches "we built it but nobody tested it."
109
+
110
+ Maintain a traceability matrix (requirement ↔ test case). The runnable check
111
+ `scripts/coverage-gaps.mjs` takes your requirements and test cases as JSON and lists any requirement with
112
+ **no** covering test — run it in CI so an untested requirement fails the build. See
113
+ `examples/test-plan.md` for a filled matrix.
114
+
115
+ ## 6. Test data and environments
116
+
117
+ - **Data**: prefer small, purpose-built fixtures over a copy of production. Cover the equivalence classes
118
+ and boundaries from §3. Never use real PII in test data — synthesize it. Make data setup deterministic
119
+ and self-cleaning so tests are independent and order-free.
120
+ - **Environments**: keep a dedicated, reproducible test environment (containers/IaC) so "works on my
121
+ machine" is not a defense. Pin external dependencies via fakes/contract tests; do not let a flaky
122
+ third-party sandbox gate your pipeline.
123
+
124
+ ## Common pitfalls (failure modes)
125
+
126
+ - **Testing everything equally** — no risk ranking, so trivial getters get the same attention as the
127
+ payment path. Rank first.
128
+ - **Inverted pyramid** — leaning on slow, flaky E2E for logic that a unit test would pin precisely.
129
+ - **Coverage theater** — chasing a line-coverage number with assertion-free tests; green bar, no safety.
130
+ - **Redundant cases** — five tests in the same equivalence class, zero at the boundary where the bug is.
131
+ - **No traceability** — a requirement ships with no test because nothing connected the two.
132
+ - **Non-deterministic data/time** — tests depend on "today" or shared mutable rows and fail intermittently.
133
+ - **Vague exit criteria** — "looks good" instead of measurable gates, so testing ends by exhaustion.
134
+
135
+ ## When NOT to use / trade-offs
136
+
137
+ A heavyweight, document-driven test plan is overkill for a one-line bug fix or a throwaway spike — there,
138
+ a couple of targeted tests and a green CI run are the proportionate response. Formal test plans earn their
139
+ cost on high-risk, multi-team, regulated, or long-lived features. Likewise, exhaustive case enumeration is
140
+ wasteful on low-risk surfaces; reserve decision tables and full boundary analysis for logic where a defect
141
+ is expensive. The plan should be *proportional to risk* — a plan that costs more than the failures it
142
+ prevents is itself a failure. For ongoing change to an existing suite (what to re-run after a change, how
143
+ to fight flakiness over time), that is a different discipline — see the pairing below.
144
+
145
+ ## Files in this package
146
+
147
+ - `references/risk-prioritization.md` — risk scoring rubric, impact/likelihood matrix, worked heat-map
148
+ - `references/test-pyramid.md` — layer-selection heuristics, integration vs contract tests, anti-patterns
149
+ - `references/case-design.md` — equivalence partitioning, boundary analysis, decision tables, worked example
150
+ - `examples/test-plan.md` — a complete, filled-in test plan with a traceability matrix
151
+ - `examples/test-case-template.md` — a reusable single-test-case template
152
+ - `scripts/coverage-gaps.mjs` — runnable check: lists requirements with no covering test (`--selftest`)
153
+
154
+ Pairs with the `qa-strategist` agent, the `test-author` agent, and the `regression-strategy` skill.
@@ -0,0 +1,45 @@
1
+ # Test Case Template
2
+
3
+ A reusable shape for a single test case. Keep one per case; the `id` is what the traceability matrix and
4
+ `scripts/coverage-gaps.mjs` reference.
5
+
6
+ ```yaml
7
+ id: coupon_min_5000
8
+ requirement: R3 # links back to a requirement in the test plan
9
+ title: Coupon applies exactly at the $50 minimum
10
+ layer: unit # unit | integration | e2e
11
+ technique: boundary # partition | boundary | decision-table | exploratory
12
+ priority: high # from risk ranking
13
+
14
+ preconditions:
15
+ - a valid, non-expired coupon "SAVE10" exists
16
+
17
+ # Arrange / Act / Assert
18
+ arrange:
19
+ cartTotal: 50.00 # the boundary value (min is inclusive)
20
+ coupon: "SAVE10"
21
+ act:
22
+ call: validateCoupon(coupon, cartTotal)
23
+ assert:
24
+ result: applied
25
+ discountPercent: 10
26
+
27
+ notes: paired with coupon_min_4999 (just below) and coupon_min_5001 (just above)
28
+ ```
29
+
30
+ ## Filled boundary trio
31
+
32
+ The same requirement R3 needs three cases to pin the `$50` edge — copy the block above per row:
33
+
34
+ | id | cartTotal | expected |
35
+ |---|---|---|
36
+ | `coupon_min_4999` | `49.99` | `min_not_met` |
37
+ | `coupon_min_5000` | `50.00` | `applied` |
38
+ | `coupon_min_5001` | `50.01` | `applied` |
39
+
40
+ ## Why this shape
41
+
42
+ - `id` + `requirement` make traceability machine-checkable.
43
+ - `technique` documents *why* this case exists, so reviewers can spot a partition or boundary that is
44
+ missing.
45
+ - Arrange/Act/Assert keeps each case focused on one behavior with one reason to fail.
@@ -0,0 +1,57 @@
1
+ # Test Plan — Checkout v2
2
+
3
+ A filled-in example you can copy and adapt. Keep it short; a plan nobody reads is wasted effort.
4
+
5
+ ## 1. Scope
6
+
7
+ In scope: cart pricing, tax, coupon validation, payment capture/refund, order confirmation.
8
+ Out of scope: marketing emails (covered by the comms team), the static footer.
9
+
10
+ ## 2. Risk ranking
11
+
12
+ | Area | Impact | Likelihood | Risk | Primary layer |
13
+ |---|---|---|---|---|
14
+ | Payment capture/refund | 5 | 4 | 20 | integration + E2E |
15
+ | Price & tax math | 5 | 3 | 15 | unit |
16
+ | Coupon validation | 3 | 4 | 12 | unit (decision table) |
17
+ | Inventory decrement | 4 | 3 | 12 | integration |
18
+ | Confirmation email | 2 | 2 | 4 | integration (fake mailer) |
19
+ | Footer year | 1 | 1 | 1 | not tested (accepted by PO) |
20
+
21
+ ## 3. Entry criteria
22
+
23
+ - Feature branch merged to `test`, CI build green.
24
+ - Test environment provisioned; seed catalog + test cards loaded.
25
+ - Payment gateway sandbox reachable OR contract fakes deployed.
26
+
27
+ ## 4. Exit criteria
28
+
29
+ - All planned cases executed.
30
+ - Every CRITICAL/HIGH requirement has ≥1 passing test (see matrix).
31
+ - Line coverage ≥ 80% on changed files.
32
+ - Zero open Sev-1/Sev-2 defects; remaining issues triaged and accepted.
33
+
34
+ ## 5. Requirement → test traceability matrix
35
+
36
+ This is the data `scripts/coverage-gaps.mjs` consumes (as JSON) to fail the build on any uncovered
37
+ requirement.
38
+
39
+ | Req ID | Requirement | Covering test(s) | Layer |
40
+ |---|---|---|---|
41
+ | R1 | Totals include tax for the buyer's region | `tax_us`, `tax_eu_boundary` | unit |
42
+ | R2 | Expired coupons are rejected | `coupon_expired` | unit |
43
+ | R3 | Coupon needs cart ≥ $50 | `coupon_min_4999`, `coupon_min_5000` | unit |
44
+ | R4 | Declined card shows a retry message | `pay_declined` | integration |
45
+ | R5 | Gateway timeout does not double-charge | `pay_timeout_idempotent` | integration |
46
+ | R6 | Successful pay → confirmation screen | `e2e_happy_checkout` | E2E |
47
+
48
+ ## 6. Test data & environment
49
+
50
+ - Cards: `4242…` (success), `4000…0002` (decline), a sandbox timeout trigger.
51
+ - Cart fixtures hit each coupon partition (below-min, at-min, above-min) and tax boundary.
52
+ - Data is seeded per-run and torn down after; no shared mutable rows.
53
+
54
+ ## 7. Notes
55
+
56
+ - Confirmation email asserted via a fake mailer, not the real provider (avoids flaky external dep).
57
+ - Footer year explicitly out of scope — recorded so it reads as a decision, not a miss.
@@ -0,0 +1,79 @@
1
+ # Case-Design Techniques
2
+
3
+ Do not invent test cases ad hoc — derive them. These three techniques cover most input-space reasoning
4
+ and stop you from writing many tests that all hit the same path while missing the one that breaks.
5
+
6
+ ## 1. Equivalence partitioning
7
+
8
+ Split the input domain into classes whose members *should* behave identically, then test **one
9
+ representative per class**. Testing forty values from the same class adds cost, not confidence.
10
+
11
+ Example — a shipping-cost function by weight (kg):
12
+
13
+ | Partition | Range | Representative |
14
+ |---|---|---|
15
+ | invalid (negative) | `w < 0` | `-1` |
16
+ | light | `0 ≤ w ≤ 5` | `2` |
17
+ | medium | `5 < w ≤ 20` | `12` |
18
+ | heavy | `20 < w ≤ 50` | `35` |
19
+ | over-limit (rejected) | `w > 50` | `60` |
20
+
21
+ Five tests cover the whole domain's behavior classes.
22
+
23
+ ## 2. Boundary value analysis
24
+
25
+ Defects cluster at edges (off-by-one, `<` vs `<=`). For each boundary test **just-below, on, and
26
+ just-above**. For the light/medium boundary at `5`:
27
+
28
+ ```text
29
+ w = 4.99 → light
30
+ w = 5.00 → light (boundary is inclusive)
31
+ w = 5.01 → medium
32
+ ```
33
+
34
+ Repeat at every partition edge (`0`, `5`, `20`, `50`). Boundaries are where equivalence partitioning and
35
+ real bugs intersect, so this is the highest-yield technique per test.
36
+
37
+ ## 3. Decision tables
38
+
39
+ When output depends on a **combination** of conditions, enumerate the combinations so no rule — and no
40
+ interaction between rules — is missed.
41
+
42
+ ```text
43
+ Loan approval
44
+ Credit ≥ 700 | Income ≥ 50k | Existing debt | → Decision
45
+ no | no | - | reject
46
+ no | yes | yes | reject
47
+ yes | no | no | review
48
+ yes | yes | no | approve
49
+ yes | yes | yes | review ← interaction, easy to miss
50
+ ```
51
+
52
+ For N independent booleans there are 2^N rows; collapse with "don't care" (`-`) entries where a condition
53
+ cannot change the outcome, and keep the interaction rows that ad-hoc testing skips.
54
+
55
+ ## Worked example — combining all three
56
+
57
+ Function: `validateCoupon(code, cartTotal)` returns `applied | min_not_met | expired | invalid`.
58
+
59
+ 1. **Partition** `cartTotal`: invalid (`< 0`), below-min (`0..49.99`), at-or-above-min (`>= 50`).
60
+ 2. **Boundaries** on the `50` minimum: `49.99`, `50.00`, `50.01`.
61
+ 3. **Decision table** over `code` state × threshold:
62
+
63
+ ```text
64
+ code state | cartTotal ≥ 50 | → result
65
+ valid | yes | applied
66
+ valid | no | min_not_met
67
+ expired | yes | expired (expiry checked before threshold)
68
+ unknown | - | invalid
69
+ ```
70
+
71
+ Result: ~8 deliberate cases that cover the domain, the edges, and the rule interactions — versus a dozen
72
+ random inputs that would likely all land in the same partition.
73
+
74
+ ## Pitfalls
75
+
76
+ - **All cases in one partition** — many "valid" inputs, none invalid, none at a boundary.
77
+ - **Skipping interaction rows** — testing each condition alone but never the combination that flips the
78
+ result.
79
+ - **Boundary blindness** — testing `2` and `35` but never `5` or `50`, exactly where off-by-one hides.
@@ -0,0 +1,58 @@
1
+ # Risk-Based Prioritization
2
+
3
+ You cannot test everything, so test what matters most first. Rank each feature area by
4
+ **risk = impact × likelihood** and let that ranking drive where rigor goes.
5
+
6
+ ## Scoring rubric
7
+
8
+ Score each axis 1–5, multiply, and sort descending.
9
+
10
+ ### Impact (cost of failure)
11
+
12
+ | Score | Meaning | Examples |
13
+ |---|---|---|
14
+ | 5 | Catastrophic | money moved wrongly, data loss, security breach, legal/compliance breach |
15
+ | 4 | Severe | core flow blocked for all users, corrupted records |
16
+ | 3 | Moderate | feature broken for a segment, recoverable with effort |
17
+ | 2 | Minor | inconvenient, easy workaround |
18
+ | 1 | Cosmetic | visual glitch, no functional effect |
19
+
20
+ ### Likelihood (probability of a defect)
21
+
22
+ | Score | Meaning | Signals that raise it |
23
+ |---|---|---|
24
+ | 5 | Very likely | brand-new code, complex logic, vague requirements, many integrations |
25
+ | 4 | Likely | recently changed, moderate complexity |
26
+ | 3 | Possible | some history, average complexity |
27
+ | 2 | Unlikely | stable code, simple logic, well understood |
28
+ | 1 | Rare | trivial, unchanged for a long time, heavily exercised in production |
29
+
30
+ ## Worked heat-map (checkout feature)
31
+
32
+ | Area | Impact | Likelihood | Risk | Rigor |
33
+ |---|---|---|---|---|
34
+ | Payment capture / refund | 5 | 4 | 20 | exhaustive: unit + integration (success/decline/timeout) + E2E |
35
+ | Price & tax calculation | 5 | 3 | 15 | unit-heavy: partitions + full boundary analysis + decision table |
36
+ | Inventory decrement | 4 | 3 | 12 | integration with concurrency case |
37
+ | Coupon validation | 3 | 4 | 12 | decision table over rule combinations |
38
+ | Order confirmation email | 2 | 2 | 4 | one integration test with a fake mailer |
39
+ | Footer copyright year | 1 | 1 | 1 | not tested — documented as accepted |
40
+
41
+ The bottom rows matter as much as the top: writing "footer year — not tested, low risk" is an explicit,
42
+ defensible decision, not an oversight.
43
+
44
+ ## Turning risk into a budget
45
+
46
+ 1. Sort areas by risk score.
47
+ 2. Allocate effort top-down until the budget (time, people) runs out.
48
+ 3. For anything below the cut line, record the decision to under-test and who accepted it.
49
+ 4. Re-rank when the system changes — risk is not static; a quiet module becomes high-risk the moment it
50
+ is rewritten.
51
+
52
+ ## Anti-patterns
53
+
54
+ - **Flat prioritization** — every area gets equal attention, so the payment path and a tooltip compete
55
+ for the same hour.
56
+ - **Gut-feel only** — skipping the score and "just knowing" what is risky; the matrix forces the
57
+ conversation and creates an auditable record.
58
+ - **Set-and-forget** — never re-scoring after a refactor moves the risk.
@@ -0,0 +1,58 @@
1
+ # The Test Pyramid — Choosing a Layer
2
+
3
+ Test each behavior at the **lowest layer that can meaningfully verify it**. Lower is faster, cheaper, and
4
+ more precise about *where* a defect lives.
5
+
6
+ ```
7
+ /\ E2E few — full journeys, real-ish stack, slow, flakiest
8
+ / \ Integration some — module + collaborators (DB, gateway, queue)
9
+ /____\ Unit many — one function/class, no I/O, milliseconds
10
+ ```
11
+
12
+ ## The three layers
13
+
14
+ ### Unit
15
+ - Scope: a single function/class with no I/O.
16
+ - Verifies: pure logic — pricing, validation, parsing, state machines.
17
+ - Speed: milliseconds; run thousands on every commit.
18
+ - Rule of thumb: if it needs a network, a DB, or the clock, it is not a unit test (inject those).
19
+
20
+ ### Integration
21
+ - Scope: a module talking to a real or faithfully faked collaborator.
22
+ - Verifies: wiring — does the repository persist correctly against a test DB? Does the payment client
23
+ handle success, decline, and timeout from a mocked gateway?
24
+ - Speed: tens to hundreds of ms; run on every push.
25
+
26
+ ### End-to-end (E2E)
27
+ - Scope: the whole system through its real entry point (HTTP, UI).
28
+ - Verifies: critical user journeys only — login, checkout, the one flow whose failure is unacceptable.
29
+ - Speed: seconds; the slowest and flakiest. Keep this layer thin.
30
+
31
+ ## Layer-selection heuristics
32
+
33
+ | If you are verifying... | Prefer |
34
+ |---|---|
35
+ | a calculation or branch in logic | unit |
36
+ | that two components talk correctly | integration |
37
+ | that a third-party contract holds | contract test (a focused integration test) |
38
+ | that a user can complete a journey | E2E (one happy path + key failure) |
39
+
40
+ Push tests down whenever possible. A bug findable by a unit test should not be left for E2E to catch
41
+ slowly and vaguely.
42
+
43
+ ## Integration vs contract tests
44
+
45
+ A **contract test** pins the shape of the boundary with an external service (request/response schema) so
46
+ you catch drift without depending on the live service in CI. Use it instead of hitting a flaky vendor
47
+ sandbox on every build; run the real integration on a slower cadence.
48
+
49
+ ## Anti-patterns
50
+
51
+ - **Ice-cream cone (inverted pyramid)** — many E2E, few units. Slow, flaky, and tells you *that*
52
+ something broke but not *where*.
53
+ - **Testing the framework** — asserting that the ORM saves a row or the HTTP library sends a header; test
54
+ *your* logic, not the dependency's.
55
+ - **Integration tests that are secretly E2E** — spinning up the entire stack to check one function;
56
+ collapse it to a unit.
57
+ - **No E2E at all** — every layer green, yet the assembled product is broken because nothing exercised
58
+ the seams.