@brunosps00/dev-workflow 0.10.0 → 0.13.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 (120) hide show
  1. package/README.md +78 -6
  2. package/lib/constants.js +20 -20
  3. package/lib/init.js +44 -4
  4. package/lib/migrate-skills.js +129 -0
  5. package/lib/removed-bundled-skills.js +16 -0
  6. package/lib/uninstall.js +6 -2
  7. package/lib/utils.js +51 -4
  8. package/package.json +1 -1
  9. package/scaffold/en/agent-instructions.md +68 -0
  10. package/scaffold/en/commands/dw-analyze-project.md +61 -0
  11. package/scaffold/en/commands/dw-autopilot.md +1 -1
  12. package/scaffold/en/commands/dw-brainstorm.md +1 -1
  13. package/scaffold/en/commands/dw-bugfix.md +3 -3
  14. package/scaffold/en/commands/dw-code-review.md +28 -0
  15. package/scaffold/en/commands/dw-create-prd.md +16 -0
  16. package/scaffold/en/commands/dw-create-tasks.md +42 -0
  17. package/scaffold/en/commands/dw-create-techspec.md +18 -1
  18. package/scaffold/en/commands/dw-deps-audit.md +1 -1
  19. package/scaffold/en/commands/dw-fix-qa.md +1 -1
  20. package/scaffold/en/commands/dw-functional-doc.md +2 -2
  21. package/scaffold/en/commands/dw-help.md +1 -1
  22. package/scaffold/en/commands/dw-redesign-ui.md +7 -7
  23. package/scaffold/en/commands/dw-run-qa.md +4 -4
  24. package/scaffold/en/commands/dw-run-task.md +2 -2
  25. package/scaffold/en/templates/constitution-template.md +111 -0
  26. package/scaffold/pt-br/agent-instructions.md +68 -0
  27. package/scaffold/pt-br/commands/dw-analyze-project.md +61 -0
  28. package/scaffold/pt-br/commands/dw-autopilot.md +1 -1
  29. package/scaffold/pt-br/commands/dw-brainstorm.md +1 -1
  30. package/scaffold/pt-br/commands/dw-bugfix.md +3 -3
  31. package/scaffold/pt-br/commands/dw-code-review.md +28 -0
  32. package/scaffold/pt-br/commands/dw-create-prd.md +16 -0
  33. package/scaffold/pt-br/commands/dw-create-tasks.md +42 -0
  34. package/scaffold/pt-br/commands/dw-create-techspec.md +18 -1
  35. package/scaffold/pt-br/commands/dw-deps-audit.md +1 -1
  36. package/scaffold/pt-br/commands/dw-fix-qa.md +1 -1
  37. package/scaffold/pt-br/commands/dw-functional-doc.md +2 -2
  38. package/scaffold/pt-br/commands/dw-help.md +1 -1
  39. package/scaffold/pt-br/commands/dw-redesign-ui.md +7 -7
  40. package/scaffold/pt-br/commands/dw-run-qa.md +4 -4
  41. package/scaffold/pt-br/commands/dw-run-task.md +2 -2
  42. package/scaffold/pt-br/templates/constitution-template.md +111 -0
  43. package/scaffold/skills/dw-council/SKILL.md +1 -1
  44. package/scaffold/skills/dw-testing-discipline/SKILL.md +148 -0
  45. package/scaffold/skills/dw-testing-discipline/references/ai-agent-gates.md +170 -0
  46. package/scaffold/skills/dw-testing-discipline/references/anti-patterns.md +336 -0
  47. package/scaffold/skills/dw-testing-discipline/references/flaky-discipline.md +163 -0
  48. package/scaffold/skills/dw-testing-discipline/references/iron-laws.md +128 -0
  49. package/scaffold/skills/dw-testing-discipline/references/playwright-recipes.md +282 -0
  50. package/scaffold/skills/dw-testing-discipline/references/positive-patterns.md +241 -0
  51. package/scaffold/skills/{webapp-testing → dw-testing-discipline}/references/security-boundary.md +1 -1
  52. package/scaffold/skills/dw-ui-discipline/SKILL.md +128 -0
  53. package/scaffold/skills/dw-ui-discipline/references/accessibility-floor.md +225 -0
  54. package/scaffold/skills/dw-ui-discipline/references/anti-slop.md +162 -0
  55. package/scaffold/skills/dw-ui-discipline/references/curated-defaults.md +195 -0
  56. package/scaffold/skills/dw-ui-discipline/references/hard-gate.md +142 -0
  57. package/scaffold/skills/dw-ui-discipline/references/state-matrix.md +101 -0
  58. package/scaffold/templates-overrides-readme.md +75 -0
  59. package/scaffold/skills/ui-ux-pro-max/LICENSE +0 -21
  60. package/scaffold/skills/ui-ux-pro-max/SKILL.md +0 -659
  61. package/scaffold/skills/ui-ux-pro-max/data/_sync_all.py +0 -414
  62. package/scaffold/skills/ui-ux-pro-max/data/app-interface.csv +0 -31
  63. package/scaffold/skills/ui-ux-pro-max/data/charts.csv +0 -26
  64. package/scaffold/skills/ui-ux-pro-max/data/colors.csv +0 -162
  65. package/scaffold/skills/ui-ux-pro-max/data/design.csv +0 -1776
  66. package/scaffold/skills/ui-ux-pro-max/data/draft.csv +0 -1779
  67. package/scaffold/skills/ui-ux-pro-max/data/google-fonts.csv +0 -1924
  68. package/scaffold/skills/ui-ux-pro-max/data/icons.csv +0 -106
  69. package/scaffold/skills/ui-ux-pro-max/data/landing.csv +0 -35
  70. package/scaffold/skills/ui-ux-pro-max/data/products.csv +0 -162
  71. package/scaffold/skills/ui-ux-pro-max/data/react-performance.csv +0 -45
  72. package/scaffold/skills/ui-ux-pro-max/data/stacks/angular.csv +0 -51
  73. package/scaffold/skills/ui-ux-pro-max/data/stacks/astro.csv +0 -54
  74. package/scaffold/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  75. package/scaffold/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  76. package/scaffold/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  77. package/scaffold/skills/ui-ux-pro-max/data/stacks/laravel.csv +0 -51
  78. package/scaffold/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  79. package/scaffold/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  80. package/scaffold/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  81. package/scaffold/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  82. package/scaffold/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  83. package/scaffold/skills/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  84. package/scaffold/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  85. package/scaffold/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  86. package/scaffold/skills/ui-ux-pro-max/data/stacks/threejs.csv +0 -54
  87. package/scaffold/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  88. package/scaffold/skills/ui-ux-pro-max/data/styles.csv +0 -85
  89. package/scaffold/skills/ui-ux-pro-max/data/typography.csv +0 -74
  90. package/scaffold/skills/ui-ux-pro-max/data/ui-reasoning.csv +0 -162
  91. package/scaffold/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  92. package/scaffold/skills/ui-ux-pro-max/scripts/core.py +0 -262
  93. package/scaffold/skills/ui-ux-pro-max/scripts/design_system.py +0 -1148
  94. package/scaffold/skills/ui-ux-pro-max/scripts/search.py +0 -114
  95. package/scaffold/skills/ui-ux-pro-max/skills/brand/SKILL.md +0 -97
  96. package/scaffold/skills/ui-ux-pro-max/skills/design/SKILL.md +0 -302
  97. package/scaffold/skills/ui-ux-pro-max/skills/design-system/SKILL.md +0 -244
  98. package/scaffold/skills/ui-ux-pro-max/templates/base/quick-reference.md +0 -297
  99. package/scaffold/skills/ui-ux-pro-max/templates/base/skill-content.md +0 -358
  100. package/scaffold/skills/ui-ux-pro-max/templates/platforms/agent.json +0 -21
  101. package/scaffold/skills/ui-ux-pro-max/templates/platforms/augment.json +0 -18
  102. package/scaffold/skills/ui-ux-pro-max/templates/platforms/claude.json +0 -21
  103. package/scaffold/skills/ui-ux-pro-max/templates/platforms/codebuddy.json +0 -21
  104. package/scaffold/skills/ui-ux-pro-max/templates/platforms/codex.json +0 -21
  105. package/scaffold/skills/ui-ux-pro-max/templates/platforms/continue.json +0 -21
  106. package/scaffold/skills/ui-ux-pro-max/templates/platforms/copilot.json +0 -21
  107. package/scaffold/skills/ui-ux-pro-max/templates/platforms/cursor.json +0 -21
  108. package/scaffold/skills/ui-ux-pro-max/templates/platforms/droid.json +0 -21
  109. package/scaffold/skills/ui-ux-pro-max/templates/platforms/gemini.json +0 -21
  110. package/scaffold/skills/ui-ux-pro-max/templates/platforms/kilocode.json +0 -21
  111. package/scaffold/skills/ui-ux-pro-max/templates/platforms/kiro.json +0 -21
  112. package/scaffold/skills/ui-ux-pro-max/templates/platforms/opencode.json +0 -21
  113. package/scaffold/skills/ui-ux-pro-max/templates/platforms/qoder.json +0 -21
  114. package/scaffold/skills/ui-ux-pro-max/templates/platforms/roocode.json +0 -21
  115. package/scaffold/skills/ui-ux-pro-max/templates/platforms/trae.json +0 -21
  116. package/scaffold/skills/ui-ux-pro-max/templates/platforms/warp.json +0 -18
  117. package/scaffold/skills/ui-ux-pro-max/templates/platforms/windsurf.json +0 -21
  118. package/scaffold/skills/webapp-testing/SKILL.md +0 -138
  119. package/scaffold/skills/webapp-testing/assets/test-helper.js +0 -56
  120. /package/scaffold/skills/{webapp-testing → dw-testing-discipline}/references/three-workflow-patterns.md +0 -0
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: dw-testing-discipline
3
+ description: Use when authoring, reviewing, or debugging tests — enforces Six Iron Laws (behavior over mocks, push to lowest layer, fix prod first on red, real systems gate merge), 25 anti-patterns, 7 AI agent gates, and flaky-test discipline so tests reveal bugs instead of decorating CI.
4
+ ---
5
+
6
+ # Testing Discipline
7
+
8
+ > **Inspired by** [`pedronauck/skills/testing-boss`](https://github.com/pedronauck/skills/tree/main/skills/mine/testing-boss) (MIT). Six Iron Laws, positive/anti-pattern catalogs, AI agent gates, and flaky-test taxonomy adapted from Pedro Nauck's work. The browser security-boundary and three-workflow-patterns references additionally cite [`addyosmani/agent-skills/browser-devtools`](https://github.com/addyosmani/agent-skills) (MIT), and Playwright recipes carry over from earlier dev-workflow work.
9
+
10
+ ## Cardinal Premise
11
+
12
+ > Tests exist to expose defects, not to keep CI green.
13
+ > A test that fails has done its job.
14
+ > A test that passes for the wrong reason is worse than no test.
15
+
16
+ ## Six Iron Laws
17
+
18
+ ```
19
+ 1. Test the behavior, never the mock.
20
+ 2. Push every test to the lowest layer that can detect the failure.
21
+ 3. When a test fails, fix production first — change the test only after writing why.
22
+ 4. Real systems gate the merge. Mocks isolate; they do not validate.
23
+ 5. Coverage is a flashlight. Mutation score is a quality probe. Neither is a target.
24
+ 6. No test-only methods, branches, or flags leak into production code.
25
+ ```
26
+
27
+ Each law has nuance — read `references/iron-laws.md` for the full version with examples.
28
+
29
+ ## Required Reading Router
30
+
31
+ | Task | MUST read |
32
+ |------|-----------|
33
+ | Deciding where a test belongs | `references/iron-laws.md` (Law 2 deep-dive) |
34
+ | Writing new tests | `references/positive-patterns.md` |
35
+ | Reviewing / debugging tests | `references/anti-patterns.md` |
36
+ | Test authored by an AI agent | `references/ai-agent-gates.md` + `references/anti-patterns.md` |
37
+ | Flaky tests appeared | `references/flaky-discipline.md` |
38
+ | Browser-based E2E with Playwright | `references/playwright-recipes.md` |
39
+ | Browser security boundary testing | `references/security-boundary.md` |
40
+ | Picking the right test workflow (UI vs network vs perf) | `references/three-workflow-patterns.md` |
41
+
42
+ ## Twelve positive patterns (one-liners, full version in references/positive-patterns.md)
43
+
44
+ 1. Query by behavior and accessible role; never CSS selectors or DOM indices.
45
+ 2. Selector hierarchy: role → label → text → test-id → structural (stop at highest rung that disambiguates).
46
+ 3. Wait on observable conditions; never wall-clock sleeps.
47
+ 4. Each test independent and order-free; setup over teardown.
48
+ 5. One behavior per test; as many assertions as that behavior needs.
49
+ 6. Names read like specifications: `should <outcome> when <condition> given <state>`.
50
+ 7. Table-driven / parameterized when inputs vary.
51
+ 8. Build test data via factories; literal blobs only for fields under test.
52
+ 9. Mock at boundaries you don't control; real wiring for owned systems.
53
+ 10. Real systems gate final merge; contract tests bridge unit and E2E.
54
+ 11. Mutation score, not coverage percentage, measures suite strength.
55
+ 12. Page Object Model is a tool, not a religion — collapse for small suites.
56
+
57
+ ## Five anti-pattern families (25 total, full catalog in references/anti-patterns.md)
58
+
59
+ **Brittleness** — tests bound to internals:
60
+ - Implementation-detail selectors, internal-structure assertions, testing private methods, snapshot-as-test, vague existence assertions, action-without-assertion.
61
+
62
+ **Flakiness** — tests randomizing verdicts:
63
+ - Static sleeps, test order dependency, non-deterministic inputs (clock, RNG, locale).
64
+
65
+ **Mock misuse** — tests testing the test setup:
66
+ - Asserting the mock exists, mock drift, over-mocking children, incomplete mocks, mocking wrong level.
67
+
68
+ **Process** — team and suite pathologies:
69
+ - Coverage-as-vanity, happy-path-only, eternal `beforeAll`, cleanup in `afterEach`, magic strings, testing third-party sites, quarantine-as-cemetery, retry-as-fix, duplicate tests across layers, weakening tests to make them pass, mock-driven confidence.
70
+
71
+ **AI-specific** — agent failure modes:
72
+ - The seven failure modes that gates in `ai-agent-gates.md` block.
73
+
74
+ ## Seven AI agent gates (mandatory when an agent writes tests)
75
+
76
+ These are mandatory pre-conditions whenever an LLM produces test code. Each gate is a forcing function against a specific LLM tendency:
77
+
78
+ 1. **Invariant first** — agent prints `INVARIANT: …`, `OWNING_LAYER: …`, `EXISTING_SUITE: …` before any code.
79
+ 2. **Owning layer** — extend an existing suite; reject new files without a named invariant.
80
+ 3. **Real execution** — every test runs against real DB / real route / real external integration at least once before merging.
81
+ 4. **Failure → fix production** — on a red test, the next move reads production code, NOT the test. Document the analysis before changing either.
82
+ 5. **No snapshot without contract** — classify the artifact as `PRODUCT_CONTRACT` or `IMPLEMENTATION_DETAIL`. The latter forbids snapshots.
83
+ 6. **No assertion on self-set mock** — cannot assert on values the same test body wrote into the mock.
84
+ 7. **Negative companion** — every positive assertion ships with a negative test for invalid input or failure mode.
85
+
86
+ Full prompt blocks and verification recipes in `references/ai-agent-gates.md`.
87
+
88
+ ## Placement doctrine (tripwires)
89
+
90
+ Before writing test code:
91
+
92
+ - Name the invariant in **one sentence**. Fuzzy language signals unclear requirements — stop and clarify.
93
+ - Place the test at the **lowest layer** capable of detecting the failure when the invariant breaks.
94
+ - Reject tests where `(likelihood × blast-radius)` falls below the ten-minute-maintenance threshold (the test is more expensive to maintain than the bug would be to fix).
95
+
96
+ ## Flaky discipline (tripwires)
97
+
98
+ - Quarantine flaky tests within ONE HOUR of detection. Assign a named owner within 24 hours with a fix-by date.
99
+ - Track `flaky_rate` as a first-class metric: SLO under 1–2%; alert at >5%.
100
+ - Real systems at the final gate: mock at unit; contract-test boundaries; real DB/queue/route at integration; near-zero mocks at E2E.
101
+
102
+ Full taxonomy in `references/flaky-discipline.md`.
103
+
104
+ ## Cross-cutting red flags
105
+
106
+ Any of these in a PR triggers REJECTED in `/dw-code-review`:
107
+
108
+ - Mock setup larger than test logic.
109
+ - Test breaks when an internal method is renamed (not the public contract).
110
+ - Removing the assertion body leaves the test green.
111
+ - Test fails when run with `.only` in isolation.
112
+ - `sleep`, `Thread.sleep`, or `cy.wait(<number>)` appears.
113
+ - Selector contains CSS class, index, or `xpath`.
114
+ - Test asserts a third-party site is reachable.
115
+ - Snapshot diffs accepted without reading.
116
+ - Coverage percentage is the only metric quoted.
117
+ - Failing tests auto-retried until green; no investigation.
118
+ - Skipped/quarantined tests without named owner and fix-by date.
119
+ - Test depends on `new Date()`, `Math.random()`, or system locale.
120
+ - `afterEach` resets database (move to `beforeEach`).
121
+ - AI-written test has 6+ assertions and zero edge cases.
122
+ - Phrase "I'll mock this to be safe" appears in the diff.
123
+
124
+ ## When NOT to use this skill
125
+
126
+ - General code review unrelated to tests.
127
+ - Library-specific debugging where the test is just a reproduction.
128
+ - Non-testing CI pipeline design (deploys, artifacts, secrets).
129
+ - Production observability and alerting.
130
+ - Single-line typo fixes in existing tests.
131
+
132
+ ## Integration with dev-workflow commands
133
+
134
+ - `/dw-create-tasks` uses the placement doctrine — each test-adding task must name the invariant.
135
+ - `/dw-run-task` applies the 7 AI gates when generating tests as part of implementation.
136
+ - `/dw-code-review` runs the anti-pattern checks on diff hunks under test paths.
137
+ - `/dw-fix-qa` runs flaky-discipline taxonomy when retesting bugs.
138
+ - `/dw-run-qa` (UI mode) references `playwright-recipes.md` for concrete recipes.
139
+
140
+ ## Why this skill exists
141
+
142
+ The previous bundled skill (`webapp-testing`) mixed Playwright recipes with two discipline references (`security-boundary`, `three-workflow-patterns`) added later. The discipline references were enterred in a tactical skill that the agent didn't reach for as doctrine.
143
+
144
+ This skill consolidates: doctrine at the top, Playwright recipes as one reference, security and workflow patterns as their own references. One skill, coherent voice, doctrine-first.
145
+
146
+ ## Bottom line
147
+
148
+ > A test that cannot fail is decorative. A test that fails for the wrong reason is misleading. Build tests that fail for exactly one reason — the reason the invariant was violated — and trust them when they do. Mocks isolate. Real systems validate. Coverage shines a light. Mutation score grades the suite. Agents will reach for the mock and the snapshot; the gates here make them put both down. Tests reveal bugs, not just pass.
@@ -0,0 +1,170 @@
1
+ # Seven AI agent gates — mandatory when an LLM writes tests
2
+
3
+ LLMs have characteristic failure modes when authoring tests. These gates are forcing functions for the seven most common.
4
+
5
+ Every test produced by an agent (via `/dw-run-task`, `/dw-bugfix`, `/dw-autopilot`, or any other code-generating flow) must pass all seven gates BEFORE the diff is presented for review.
6
+
7
+ ## Gate 1: Invariant first
8
+
9
+ **The failure mode it blocks:** Agent writes 200 lines of test code without articulating what the test is supposed to prove.
10
+
11
+ **The gate:**
12
+
13
+ Before writing any test code, the agent prints:
14
+
15
+ ```
16
+ INVARIANT: <one sentence: what behavior is true that the test verifies>
17
+ OWNING_LAYER: <unit | integration | contract | e2e>
18
+ EXISTING_SUITE: <path to existing test file the new test joins>
19
+ ```
20
+
21
+ **Why it works:**
22
+ - "Invariant" forces specific behavior naming.
23
+ - "Owning layer" forces Law 2 (lowest detectable layer).
24
+ - "Existing suite" forces extending coverage rather than spawning new files.
25
+
26
+ **Verification:** In `/dw-code-review`, look for this 3-line preamble in the PR description or the commit body. Missing = REJECTED.
27
+
28
+ ## Gate 2: Owning layer
29
+
30
+ **The failure mode it blocks:** Agent creates a new test file every time, scattering coverage across orphan files. Or, agent writes E2E tests for things unit could prove.
31
+
32
+ **The gate:**
33
+
34
+ The agent must:
35
+ 1. Identify the existing test suite that owns the module under test.
36
+ 2. Extend that suite, OR document why a new suite is needed (genuinely new module, new test pyramid layer).
37
+ 3. Map the test to the right layer per Law 2.
38
+
39
+ **Verification:**
40
+ - New test file in PR but existing file covers the same module? REJECTED.
41
+ - E2E test for pure-logic invariant? REJECTED unless documented.
42
+ - Unit test for cross-service flow? REJECTED — push to integration/E2E.
43
+
44
+ ## Gate 3: Real execution
45
+
46
+ **The failure mode it blocks:** Agent writes tests that mock everything. They pass green forever and validate nothing.
47
+
48
+ **The gate:**
49
+
50
+ Every test path the agent writes must, at SOME layer, run against real systems before the diff merges:
51
+
52
+ - Pure logic: unit only is fine.
53
+ - Code that touches DB: must have at least one integration test running real DB (testcontainers, ephemeral container, dedicated test DB).
54
+ - Code that calls external services: must have a contract test OR a sandbox-account smoke test.
55
+ - UI interactions: must have at least one E2E run on a real preview environment.
56
+
57
+ **Verification:** PR description must list the real-system runs that exercise the touched code. If no real-system path covers the change, REJECTED.
58
+
59
+ ## Gate 4: Failure → fix production
60
+
61
+ **The failure mode it blocks:** Agent sees test red, modifies the test until green. Bug ships.
62
+
63
+ **The gate:**
64
+
65
+ When the agent encounters a failing test (its own or pre-existing):
66
+
67
+ 1. Print: `INVESTIGATING FAILURE: <test name>`
68
+ 2. Read production code in the path that produces the observed value.
69
+ 3. Print: `ANALYSIS: <2-3 sentences on whether production is wrong, test is wrong, or invariant changed>`
70
+ 4. Decide:
71
+ - Production wrong → fix production.
72
+ - Test wrong → fix test AND document the change in the commit body.
73
+ - Invariant changed → update the test AND open an ADR if the change is a public contract change.
74
+
75
+ **Verification:** Every commit that changes a previously-green test must have an `ANALYSIS:` line in the commit body explaining the decision. Missing = REJECTED.
76
+
77
+ ## Gate 5: No snapshot without contract
78
+
79
+ **The failure mode it blocks:** Agent reaches for `toMatchSnapshot()` whenever it doesn't know what to assert. Snapshot becomes the assertion. Drift goes unnoticed.
80
+
81
+ **The gate:**
82
+
83
+ Before adding a snapshot assertion, the agent classifies the artifact:
84
+
85
+ - **PRODUCT_CONTRACT**: a stable contract worth pinning (e.g., serialized output of a public API, schema of a stored record). Snapshot is appropriate. Document the classification.
86
+ - **IMPLEMENTATION_DETAIL**: HTML structure, internal representation, component tree shape. Snapshot is FORBIDDEN. Write specific assertions instead.
87
+
88
+ **Verification:** Snapshots in the diff without a classification comment = REJECTED. Snapshots classified as IMPLEMENTATION_DETAIL = REJECTED.
89
+
90
+ ## Gate 6: No assertion on self-set mock
91
+
92
+ **The failure mode it blocks:** Agent writes `mockFn.mockReturnValue('X')`, then asserts `expect(mockFn()).toBe('X')`. Proves nothing.
93
+
94
+ **The gate:**
95
+
96
+ The agent cannot assert on values it directly fed into a mock. Assertions must be on:
97
+ - The OUTPUT of production code that consumed the mock.
98
+ - The SIDE EFFECTS (DB state, network calls, event emissions) caused by production code.
99
+ - The VISIBLE behavior (UI change, log line, response) the user/caller observes.
100
+
101
+ **Verification:** Diff analysis flags pairs where a literal value appears in BOTH a mock setup AND an assertion. Flagged = REJECTED unless the agent can show the value passed through production code.
102
+
103
+ ## Gate 7: Negative companion
104
+
105
+ **The failure mode it blocks:** Agent writes happy-path-only tests. Edge cases, error paths, boundaries uncovered.
106
+
107
+ **The gate:**
108
+
109
+ Every positive assertion the agent writes ships WITH at least one negative companion:
110
+
111
+ - Asserting `createUser(validInput)` succeeds → also assert `createUser(invalidInput)` fails with a specific error.
112
+ - Asserting `parseDate(validString)` returns a Date → also assert `parseDate(invalidString)` throws/returns null.
113
+ - Asserting `transferFunds(...)` succeeds with sufficient balance → also assert it fails with insufficient balance.
114
+
115
+ **Verification:** A PR adding N positive assertions must add ≥1 negative assertion per public path. Imbalance >3:1 (positive:negative) on a public path = REJECTED.
116
+
117
+ ## How the gates compose
118
+
119
+ Together, the seven gates produce tests that:
120
+ 1. State what they prove (invariant first).
121
+ 2. Live at the right layer (owning layer).
122
+ 3. Exercise reality somewhere (real execution).
123
+ 4. Reveal bugs when red (failure → fix production).
124
+ 5. Assert specifically, not via snapshots (no snapshot w/o contract).
125
+ 6. Assert outputs, not setup (no self-mock assertion).
126
+ 7. Cover failures, not just success (negative companion).
127
+
128
+ A test passing all seven is a test worth running. A test missing any one is more likely to mislead than help.
129
+
130
+ ## Override procedure
131
+
132
+ If an agent (or user) wants to skip a gate, they must:
133
+ 1. State which gate is being skipped.
134
+ 2. State why (one sentence).
135
+ 3. Add a `// SKIP-GATE-N: <reason>` comment in the test.
136
+ 4. Open a follow-up issue tracking the gap.
137
+
138
+ Without all four, the gate is enforced.
139
+
140
+ ## Prompt block to include when invoking the agent
141
+
142
+ ```
143
+ You are about to write tests. Before producing test code, complete the
144
+ seven-gate preamble:
145
+
146
+ INVARIANT: ___
147
+ OWNING_LAYER: ___
148
+ EXISTING_SUITE: ___
149
+
150
+ If you cannot complete these three lines, STOP and ask the user for
151
+ the requirement (do not invent an invariant).
152
+
153
+ Then, while writing tests:
154
+ - Real execution: name the real-system path covering this code.
155
+ - On red: investigate production before changing tests; print ANALYSIS.
156
+ - Snapshots: classify as PRODUCT_CONTRACT or IMPLEMENTATION_DETAIL.
157
+ - Assertions: never assert on values you fed into a mock.
158
+ - Coverage: every positive assertion needs a negative companion.
159
+
160
+ Tests that violate gates without explicit SKIP-GATE-N comments will be
161
+ REJECTED at review.
162
+ ```
163
+
164
+ `/dw-run-task` and `/dw-bugfix` inject this prompt before generating test code.
165
+
166
+ ## Why these seven and not more
167
+
168
+ These are the seven LLM failure modes empirically observed in test generation across multiple projects (per pedronauck/skills/testing-boss, MIT, plus dev-workflow internal observation). Other tendencies exist; they're either covered by the positive patterns (e.g., wall-clock waits) or have lower hit-rate.
169
+
170
+ If a NEW LLM failure mode appears that none of the seven catches, add a gate AND document the failure mode that motivated it. Don't add gates speculatively.
@@ -0,0 +1,336 @@
1
+ # Anti-patterns — 25 patterns across 5 families
2
+
3
+ Each anti-pattern names the smell, shows the violation in pseudo-code, gives the fix, and notes how `/dw-code-review` detects it.
4
+
5
+ ---
6
+
7
+ ## Family 1: Brittleness (tests bound to internals)
8
+
9
+ ### A1. Implementation-detail selectors
10
+
11
+ **Violation:**
12
+ ```javascript
13
+ await page.click('.btn.btn-primary.checkout-button');
14
+ ```
15
+
16
+ **Fix:** Use `getByRole('button', { name: 'Checkout' })`.
17
+
18
+ **Detection:** Grep for `.click(`, `.querySelector(`, `cy.get('.`, `getByTestId(` with a class-flavored argument.
19
+
20
+ ### A2. Testing internal structure vs observable behavior
21
+
22
+ **Violation:**
23
+ ```javascript
24
+ expect(component.state.cart.items.length).toBe(3);
25
+ ```
26
+
27
+ **Fix:** Assert what the user sees: `expect(await page.getByText('3 items in cart')).toBeVisible()`.
28
+
29
+ **Detection:** Tests that import/inspect internal state, refs, or private fields. Class-based component tests that read `.state` directly.
30
+
31
+ ### A3. Testing private methods directly
32
+
33
+ **Violation:**
34
+ ```javascript
35
+ expect(orderService._calculateTax(...)).toBe(8.5);
36
+ ```
37
+
38
+ **Fix:** Test the public method that uses tax calculation; verify the result. If the private method is independently complex, extract it to a module and test that module's public API.
39
+
40
+ **Detection:** Identifiers starting with `_` accessed from tests.
41
+
42
+ ### A4. Snapshot-as-test (snapshot replacing assertion)
43
+
44
+ **Violation:**
45
+ ```javascript
46
+ expect(rendered).toMatchSnapshot(); // ← only assertion in test
47
+ ```
48
+
49
+ **Fix:** Either:
50
+ 1. Write specific assertions about what the component renders, OR
51
+ 2. Use a snapshot AS A SECONDARY check after specific assertions, with a comment classifying the snapshot as `PRODUCT_CONTRACT` (UI contract worth pinning) — never `IMPLEMENTATION_DETAIL`.
52
+
53
+ **Detection:** Tests where the only assertion is `toMatchSnapshot` or equivalent.
54
+
55
+ ### A5. Vague existence assertions
56
+
57
+ **Violation:**
58
+ ```javascript
59
+ expect(result).toBeTruthy();
60
+ expect(element).toBeDefined();
61
+ expect(button).should('exist');
62
+ ```
63
+
64
+ **Fix:** Assert what you actually want: `expect(result.status).toBe('success')`, `expect(button).toBeEnabled()`, `expect(button).toHaveText('Continue')`.
65
+
66
+ **Detection:** Tests asserting only existence/truthiness without follow-up semantic check.
67
+
68
+ ### A6. Action without assertion
69
+
70
+ **Violation:**
71
+ ```javascript
72
+ test('clicking save works', async () => {
73
+ await page.getByRole('button', { name: 'Save' }).click();
74
+ // ← no assertion. What did "works" mean?
75
+ });
76
+ ```
77
+
78
+ **Fix:** Define what "works" means. Assert the observable outcome: URL changed, modal closed, success message visible, data persisted.
79
+
80
+ **Detection:** Tests with `await x.click()` or `await x.type()` followed by no `expect(...)`.
81
+
82
+ ---
83
+
84
+ ## Family 2: Flakiness (tests randomizing verdicts)
85
+
86
+ ### A7. Static sleeps / fixed-timeout waits
87
+
88
+ **Violation:**
89
+ ```javascript
90
+ await page.waitForTimeout(2000);
91
+ ```
92
+
93
+ **Fix:** `await expect(page.getByText(/order confirmed/i)).toBeVisible({ timeout: 5000 })` — wait on the actual condition.
94
+
95
+ **Detection:** Grep for `waitForTimeout`, `cy.wait(<number>)`, `sleep(`, `Thread.sleep`, `time.sleep` in test files.
96
+
97
+ ### A8. Test order dependency / hidden shared state
98
+
99
+ **Violation:** Test B passes only after Test A has run because A populates a shared cache or DB row.
100
+
101
+ **Fix:** Each test sets up its own state in `beforeEach`. Verify by running tests with `--shuffle` or `--randomize`.
102
+
103
+ **Detection:** Tests fail when run with `.only`. Tests fail with `--shuffle`. Setup in `beforeAll` instead of `beforeEach`.
104
+
105
+ ### A9. Non-deterministic inputs (clock, RNG, locale)
106
+
107
+ **Violation:**
108
+ ```javascript
109
+ test('today is Monday', () => {
110
+ expect(new Date().getDay()).toBe(1); // fails 6 days a week
111
+ });
112
+ ```
113
+
114
+ **Fix:** Mock the clock (`vi.useFakeTimers()`, `jest.useFakeTimers()`, `freezegun` in Python). Seed RNG. Pin locale.
115
+
116
+ **Detection:** Tests using `new Date()`, `Date.now()`, `Math.random()`, `Intl.DateTimeFormat` without fakes.
117
+
118
+ ---
119
+
120
+ ## Family 3: Mock misuse (tests testing the test setup)
121
+
122
+ ### A10. Asserting the mock exists
123
+
124
+ **Violation:**
125
+ ```javascript
126
+ expect(mockFn).toBeDefined();
127
+ ```
128
+
129
+ **Fix:** Don't assert on mock setup. If the mock is wrong, the behavior assertion downstream will fail naturally.
130
+
131
+ **Detection:** Mock functions referenced in assertions without `toHaveBeenCalled` semantics.
132
+
133
+ ### A11. Mock drift
134
+
135
+ **Violation:** Mocked API response set up 6 months ago still returns `{ status: 'OK' }` while the real API now returns `{ ok: true }`.
136
+
137
+ **Fix:** Contract testing (Pact, schemathesis) or periodic recording (msw + real-traffic capture). Re-validate mocks against real APIs quarterly.
138
+
139
+ **Detection:** Tests with mocks that haven't been touched in >90 days against APIs that have changed. Hard to detect in CI; needs explicit contract checks.
140
+
141
+ ### A12. Over-mocking child components
142
+
143
+ **Violation:**
144
+ ```javascript
145
+ vi.mock('./UserAvatar');
146
+ vi.mock('./UserMenu');
147
+ vi.mock('./UserBanner');
148
+ // ... testing nothing real
149
+ ```
150
+
151
+ **Fix:** Mock at boundaries (HTTP, DB, third-party SDKs). Render real children unless they're genuinely expensive or test-irrelevant.
152
+
153
+ **Detection:** Test files with >3 module mocks of internal modules.
154
+
155
+ ### A13. Incomplete mocks (missing fields the code reads)
156
+
157
+ **Violation:**
158
+ ```javascript
159
+ const mockUser = { id: 1 }; // missing email, but code reads user.email
160
+ ```
161
+
162
+ **Fix:** Use a factory that supplies sensible defaults for ALL fields the type/contract declares.
163
+
164
+ **Detection:** Runtime errors like `Cannot read property 'X' of undefined` inside production code under test.
165
+
166
+ ### A14. Mocking wrong level (mocking methods the logic depends on)
167
+
168
+ **Violation:**
169
+ ```javascript
170
+ // Testing OrderService, but mocking its private calculate() method
171
+ const service = new OrderService();
172
+ vi.spyOn(service, 'calculate').mockReturnValue(100);
173
+ expect(service.processOrder(...)).toBe(/* uses mocked 100 */);
174
+ ```
175
+
176
+ You've tested the SCAFFOLD, not the logic.
177
+
178
+ **Fix:** Mock at the EDGE (DB call, HTTP call, time). Let internal logic run.
179
+
180
+ **Detection:** Spies on methods of the System Under Test itself.
181
+
182
+ ---
183
+
184
+ ## Family 4: Process (team and suite pathologies)
185
+
186
+ ### A15. Coverage as vanity metric
187
+
188
+ **Violation:** PR comments demanding "you need to hit 90% coverage" with no discussion of what the coverage means.
189
+
190
+ **Fix:** Coverage is a flashlight. Use it to FIND blind spots. Don't optimize for the number.
191
+
192
+ **Detection:** Cultural; visible in PR templates that gate on coverage percentage.
193
+
194
+ ### A16. Happy-path-only coverage
195
+
196
+ **Violation:** Every test exercises the success case. Edge cases, error paths, boundary values uncovered.
197
+
198
+ **Fix:** For each unit, write at minimum: happy path + 1 boundary + 1 invalid input + 1 failure path.
199
+
200
+ **Detection:** Tests where every assertion is positive (`toBe`, `toEqual`) and none is negative (`toThrow`, `toReject`).
201
+
202
+ ### A17. Eternal `beforeAll` / shared setup hiding dependencies
203
+
204
+ **Violation:**
205
+ ```javascript
206
+ beforeAll(async () => {
207
+ await db.users.create([100 users]);
208
+ await db.orders.create([500 orders]);
209
+ });
210
+ ```
211
+
212
+ Tests now SHARE state. Order matters. Cleanup is fragile.
213
+
214
+ **Fix:** `beforeEach` with minimal setup specific to each test.
215
+
216
+ **Detection:** `beforeAll` blocks creating data (vs `beforeAll` blocks doing one-time framework setup like spinning testcontainers).
217
+
218
+ ### A18. Cleanup in `afterEach` (use `beforeEach` instead)
219
+
220
+ **Violation:**
221
+ ```javascript
222
+ afterEach(async () => {
223
+ await db.users.deleteAll();
224
+ });
225
+ ```
226
+
227
+ If a test fails mid-run, cleanup might not run; next test starts dirty.
228
+
229
+ **Fix:** `beforeEach` with explicit setup-from-clean (truncate + seed). Reliable regardless of previous test outcome.
230
+
231
+ **Detection:** `afterEach` blocks doing state reset.
232
+
233
+ ### A19. Magic strings and logic in tests
234
+
235
+ **Violation:**
236
+ ```javascript
237
+ const TIMESTAMP = '2024-01-15T10:30:00Z'; // why?
238
+ expect(formatted).toBe('a long string with embedded specifics');
239
+ ```
240
+
241
+ When the test fails, what was the test's INTENT?
242
+
243
+ **Fix:** Use factories with named defaults. Extract magic values to constants with documenting names. Use snapshot testing for legitimate snapshot cases (with classification).
244
+
245
+ **Detection:** Test files with ≥10 string literals not bound to a named variable.
246
+
247
+ ### A20. Testing against third-party sites you don't control
248
+
249
+ **Violation:**
250
+ ```javascript
251
+ test('Google homepage loads', async ({ page }) => {
252
+ await page.goto('https://google.com');
253
+ expect(await page.title()).toContain('Google');
254
+ });
255
+ ```
256
+
257
+ You're testing Google's availability, not your code.
258
+
259
+ **Fix:** Mock the third party. Use a wiremock or msw to fake their responses. If you must call them, do it in a separate "external dependencies up" smoke test, not unit/integration.
260
+
261
+ **Detection:** External URLs in test code outside designated smoke tests.
262
+
263
+ ### A21. Quarantine-as-cemetery
264
+
265
+ **Violation:**
266
+ ```javascript
267
+ test.skip('flaky on CI sometimes', () => { /* ... */ });
268
+ // commented 8 months ago, no owner, no fix-by date
269
+ ```
270
+
271
+ **Fix:** Every skip/quarantine has a NAMED OWNER and a FIX-BY DATE. Tracking issue exists. PR that introduces the skip says exactly when the test gets fixed.
272
+
273
+ **Detection:** Skipped tests without comments/labels naming owner and date.
274
+
275
+ ### A22. Retry-as-fix (auto-retry hiding real bugs)
276
+
277
+ **Violation:**
278
+ ```javascript
279
+ // jest.config or playwright.config
280
+ retries: 3,
281
+ ```
282
+
283
+ A flaky test is a SIGNAL. Retrying until green hides it.
284
+
285
+ **Fix:** When a test is flaky, FIX IT (probably a race condition or non-deterministic input). Quarantine if you can't fix immediately. Never just retry.
286
+
287
+ **Detection:** CI config with retry counts. Test runners showing "1 retry succeeded" badges.
288
+
289
+ ### A23. Duplicate tests across pyramid layers
290
+
291
+ **Violation:** Same scenario tested at unit, integration, AND E2E. Triple maintenance, no triple value.
292
+
293
+ **Fix:** Apply Law 2 — lowest layer wins. Drop higher-layer duplicates.
294
+
295
+ **Detection:** Search for the same scenario name across `tests/unit`, `tests/integration`, `tests/e2e`.
296
+
297
+ ### A24. Weakening tests to make them pass
298
+
299
+ **Violation:**
300
+ ```diff
301
+ - expect(orders.length).toBe(5);
302
+ + expect(orders.length).toBeGreaterThan(0);
303
+ ```
304
+
305
+ The "fix" makes the test useless.
306
+
307
+ **Fix:** Read Law 3. Fix production OR document WHY the assertion is weaker.
308
+
309
+ **Detection:** PR diff shows assertion relaxation without commit body explanation.
310
+
311
+ ### A25. Mock-driven confidence (test asserts on its own setup)
312
+
313
+ **Violation:**
314
+ ```javascript
315
+ const mock = vi.fn().mockReturnValue('hello');
316
+ expect(mock()).toBe('hello');
317
+ ```
318
+
319
+ You wrote `hello` in the mock. You asserted `hello`. You proved nothing.
320
+
321
+ **Fix:** Assert on the OUTPUT of the production code that consumed the mock — not on the mock itself.
322
+
323
+ **Detection:** Tests asserting equality between a value the test body created and a value the test body retrieved.
324
+
325
+ ---
326
+
327
+ ## How `/dw-code-review` uses this catalog
328
+
329
+ For each diff hunk under a test path:
330
+ 1. Run regex scans for the patterns flagged "Detection" above.
331
+ 2. Each hit becomes a finding with severity from this skill's `dw-review-rigor` integration.
332
+ 3. Hits classified as Brittleness/Flakiness/Mock-misuse → severity `high`.
333
+ 4. Hits classified as Process → severity `medium`.
334
+ 5. Hits where the SAME test has multiple patterns → severity `critical` (suite-health smell, not just one test).
335
+
336
+ A PR with ≥1 `high` test anti-pattern that lacks ADR justification gets REJECTED.