@brunosps00/dev-workflow 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +121 -0
- package/README.md +25 -15
- package/lib/constants.js +39 -36
- package/lib/doctor.js +1 -1
- package/lib/init.js +8 -0
- package/lib/instruction-health.js +38 -0
- package/lib/post-update.js +2 -1
- package/lib/validate.js +3 -1
- package/package.json +1 -1
- package/scaffold/agent-registry.json +17 -1
- package/scaffold/agents/core/dw-finding-refuter.md +55 -0
- package/scaffold/config/routing.json +61 -23
- package/scaffold/en/agent-instructions.md +32 -137
- package/scaffold/en/commands/dw-analyze-project.md +2 -0
- package/scaffold/en/commands/dw-autopilot.md +36 -241
- package/scaffold/en/commands/dw-brainstorm.md +1 -1
- package/scaffold/en/commands/dw-bugfix.md +10 -35
- package/scaffold/en/commands/dw-claude-run.md +47 -45
- package/scaffold/en/commands/dw-codex-run.md +48 -45
- package/scaffold/en/commands/dw-context-budget.md +6 -14
- package/scaffold/en/commands/dw-copilot-run.md +47 -41
- package/scaffold/en/commands/dw-find-skills.md +3 -1
- package/scaffold/en/commands/dw-generate-pr.md +2 -2
- package/scaffold/en/commands/dw-goal.md +25 -5
- package/scaffold/en/commands/dw-help.md +3 -3
- package/scaffold/en/commands/dw-install-aws-skills.md +2 -0
- package/scaffold/en/commands/dw-install-azure-skills.md +2 -0
- package/scaffold/en/commands/dw-plan.md +15 -8
- package/scaffold/en/commands/dw-qa.md +3 -0
- package/scaffold/en/commands/dw-resume.md +5 -11
- package/scaffold/en/commands/dw-review.md +154 -10
- package/scaffold/en/commands/dw-run.md +47 -177
- package/scaffold/en/commands/dw-secure-audit.md +4 -0
- package/scaffold/en/commands/dw-skill-health.md +39 -0
- package/scaffold/en/commands/dw-triage.md +8 -0
- package/scaffold/en/commands/dw-update.md +18 -4
- package/scaffold/en/references/automode.md +71 -0
- package/scaffold/en/references/command-routing.md +54 -0
- package/scaffold/en/references/execution-contract.md +55 -0
- package/scaffold/en/references/invariants.md +88 -0
- package/scaffold/en/references/untrusted-input.md +69 -0
- package/scaffold/en/templates/constitution-template.md +1 -0
- package/scaffold/en/templates/frontend-quality-template.md +43 -0
- package/scaffold/en/templates/task-template.md +20 -40
- package/scaffold/en/templates/tasks-template.md +9 -6
- package/scaffold/en/templates/techspec-template.md +4 -0
- package/scaffold/en/templates/triage-record-template.md +5 -0
- package/scaffold/pt-br/agent-instructions.md +32 -137
- package/scaffold/pt-br/commands/dw-analyze-project.md +3 -1
- package/scaffold/pt-br/commands/dw-autopilot.md +36 -241
- package/scaffold/pt-br/commands/dw-brainstorm.md +1 -1
- package/scaffold/pt-br/commands/dw-bugfix.md +10 -35
- package/scaffold/pt-br/commands/dw-claude-run.md +47 -46
- package/scaffold/pt-br/commands/dw-codex-run.md +48 -45
- package/scaffold/pt-br/commands/dw-context-budget.md +6 -14
- package/scaffold/pt-br/commands/dw-copilot-run.md +47 -43
- package/scaffold/pt-br/commands/dw-find-skills.md +3 -1
- package/scaffold/pt-br/commands/dw-generate-pr.md +2 -2
- package/scaffold/pt-br/commands/dw-goal.md +25 -5
- package/scaffold/pt-br/commands/dw-help.md +3 -3
- package/scaffold/pt-br/commands/dw-install-aws-skills.md +2 -0
- package/scaffold/pt-br/commands/dw-install-azure-skills.md +2 -0
- package/scaffold/pt-br/commands/dw-plan.md +15 -8
- package/scaffold/pt-br/commands/dw-qa.md +3 -0
- package/scaffold/pt-br/commands/dw-resume.md +7 -11
- package/scaffold/pt-br/commands/dw-review.md +154 -10
- package/scaffold/pt-br/commands/dw-run.md +47 -177
- package/scaffold/pt-br/commands/dw-secure-audit.md +5 -1
- package/scaffold/pt-br/commands/dw-skill-health.md +39 -0
- package/scaffold/pt-br/commands/dw-triage.md +8 -0
- package/scaffold/pt-br/commands/dw-update.md +18 -4
- package/scaffold/pt-br/references/automode.md +72 -0
- package/scaffold/pt-br/references/command-routing.md +54 -0
- package/scaffold/pt-br/references/execution-contract.md +55 -0
- package/scaffold/pt-br/references/invariants.md +88 -0
- package/scaffold/pt-br/references/untrusted-input.md +70 -0
- package/scaffold/pt-br/templates/constitution-template.md +1 -0
- package/scaffold/pt-br/templates/frontend-quality-template.md +43 -0
- package/scaffold/pt-br/templates/task-template.md +20 -40
- package/scaffold/pt-br/templates/tasks-template.md +9 -6
- package/scaffold/pt-br/templates/techspec-template.md +4 -0
- package/scaffold/pt-br/templates/triage-record-template.md +5 -0
- package/scaffold/scripts/hooks/git-guardrails.mjs +12 -0
- package/scaffold/scripts/hooks/session-cost.mjs +122 -7
- package/scaffold/scripts/lib/workflow-contract.mjs +101 -0
- package/scaffold/skill-registry.json +35 -23
- package/scaffold/skills/dw-chaos-engineering/SKILL.md +126 -0
- package/scaffold/skills/dw-chaos-engineering/references/vector-catalog.md +43 -0
- package/scaffold/skills/dw-cli-run/SKILL.md +39 -215
- package/scaffold/skills/dw-cli-run/references/dispatch-tuning.md +14 -72
- package/scaffold/skills/dw-codebase-intel/SKILL.md +5 -39
- package/scaffold/skills/dw-codebase-intel/agents/intel-updater.md +1 -1
- package/scaffold/skills/dw-codebase-intel/references/agents-detail.md +7 -0
- package/scaffold/skills/dw-codebase-intel/references/files-in-dw-intel-detail.md +16 -0
- package/scaffold/skills/dw-codebase-intel/references/how-dw-intel-reads-this-detail.md +12 -0
- package/scaffold/skills/dw-codebase-intel/references/how-to-compose-the-typical-flow-detail.md +11 -0
- package/scaffold/skills/dw-council/SKILL.md +2 -17
- package/scaffold/skills/dw-council/references/integration-with-other-dw-commands-detail.md +7 -0
- package/scaffold/skills/dw-council/references/phase-2-tensions-and-rebuttals-detail.md +15 -0
- package/scaffold/skills/dw-debug-protocol/references/six-step-triage.md +1 -0
- package/scaffold/skills/dw-domain-modeling/SKILL.md +1 -1
- package/scaffold/skills/dw-execute-phase/SKILL.md +12 -32
- package/scaffold/skills/dw-execute-phase/agents/executor.md +15 -252
- package/scaffold/skills/dw-execute-phase/agents/plan-checker.md +4 -207
- package/scaffold/skills/dw-execute-phase/references/atomic-commits.md +8 -18
- package/scaffold/skills/dw-execute-phase/references/de-sloppify-pass-optional-cleanup-task-detail.md +12 -0
- package/scaffold/skills/dw-execute-phase/references/deviation-rules-during-execution-detail.md +8 -0
- package/scaffold/skills/dw-execute-phase/references/how-the-two-agents-compose-detail.md +7 -0
- package/scaffold/skills/dw-execute-phase/references/plan-verification.md +9 -155
- package/scaffold/skills/dw-execute-phase/references/wave-coordination.md +6 -97
- package/scaffold/skills/dw-git-discipline/SKILL.md +2 -2
- package/scaffold/skills/dw-grilling/SKILL.md +1 -1
- package/scaffold/skills/dw-incident-response/SKILL.md +1 -64
- package/scaffold/skills/dw-incident-response/references/the-five-phases-detail.md +66 -0
- package/scaffold/skills/dw-llm-eval/SKILL.md +6 -53
- package/scaffold/skills/dw-llm-eval/references/agent-tool-use-evaluation-detail.md +23 -0
- package/scaffold/skills/dw-llm-eval/references/anti-patterns-will-block-in-dw-review-code-only-detail.md +9 -0
- package/scaffold/skills/dw-llm-eval/references/bounded-decisions.md +76 -0
- package/scaffold/skills/dw-llm-eval/references/consistency-run-to-run-stability-detail.md +10 -0
- package/scaffold/skills/dw-llm-eval/references/integration-with-dev-workflow-commands-detail.md +6 -0
- package/scaffold/skills/dw-llm-eval/references/judge-calibration.md +5 -0
- package/scaffold/skills/dw-llm-eval/references/the-oracle-ladder-detail.md +15 -0
- package/scaffold/skills/dw-memory/SKILL.md +4 -50
- package/scaffold/skills/dw-memory/references/context-budget.md +8 -56
- package/scaffold/skills/dw-memory/references/promotion-decision-test-detail.md +30 -0
- package/scaffold/skills/dw-minimalism/SKILL.md +4 -5
- package/scaffold/skills/dw-review-rigor/SKILL.md +31 -88
- package/scaffold/skills/dw-review-rigor/references/composition-audit.md +160 -0
- package/scaffold/skills/dw-review-rigor/references/refutation-pass.md +124 -0
- package/scaffold/skills/dw-review-rigor/references/the-five-rules-detail.md +56 -0
- package/scaffold/skills/dw-simplification/SKILL.md +2 -55
- package/scaffold/skills/dw-simplification/references/pattern-recognition-detail.md +11 -0
- package/scaffold/skills/dw-simplification/references/the-five-rules-detail.md +48 -0
- package/scaffold/skills/dw-source-grounding/SKILL.md +21 -56
- package/scaffold/skills/dw-source-grounding/references/the-protocol-detect-fetch-implement-cite-detail.md +58 -0
- package/scaffold/skills/dw-testing-discipline/SKILL.md +25 -177
- package/scaffold/skills/dw-testing-discipline/references/agent-guardrails.md +9 -167
- package/scaffold/skills/dw-testing-discipline/references/core-rules.md +3 -3
- package/scaffold/skills/dw-testing-discipline/references/mutation-testing.md +19 -0
- package/scaffold/skills/dw-testing-discipline/references/patterns.md +3 -12
- package/scaffold/skills/dw-ui-discipline/SKILL.md +2 -59
- package/scaffold/skills/dw-ui-discipline/references/frontend-engineering.md +46 -0
- package/scaffold/skills/dw-ui-discipline/references/the-four-grounding-questions-detail.md +61 -0
- package/scaffold/skills/dw-verify/SKILL.md +26 -148
- package/scaffold/skills/dw-verify/references/verification-discovery-and-attribution.md +2 -2
- package/scaffold/skills/humanizer/SKILL.md +2 -1
- package/scaffold/skills/security-review/SKILL.md +6 -184
- package/scaffold/skills/security-review/references/do-not-flag-detail.md +49 -0
- package/scaffold/skills/security-review/references/modern-threats.md +5 -0
- package/scaffold/skills/security-review/references/quick-patterns-reference-detail.md +56 -0
- package/scaffold/skills/security-review/references/review-process-detail.md +112 -0
- package/scaffold/skills/vercel-react-best-practices/SKILL.md +1 -90
- package/scaffold/skills/vercel-react-best-practices/references/quick-reference-detail.md +92 -0
|
@@ -14,88 +14,46 @@ A set of rules the caller applies while producing a review report. This skill do
|
|
|
14
14
|
|
|
15
15
|
## When Invoked
|
|
16
16
|
|
|
17
|
-
By `/dw-review --code-only
|
|
17
|
+
By `/dw-review --code-only` (all five rules plus the full candidate pipeline), `/dw-review --coverage-only` (de-dup and severity-ordering on PRD-vs-code gaps), `/dw-refactor` and `/dw-brainstorm --mode=refactor-audit` (rules 1, 2, 4, 5; rule 3 adapts, so a smell with a justifying ADR becomes a `low` finding at most). The caller has already identified a scope — files, a PR, a codebase area — and this skill governs how findings are selected, deduplicated, ordered, and phrased.
|
|
18
18
|
|
|
19
19
|
## Required Inputs
|
|
20
20
|
|
|
21
21
|
- The scope the caller is reviewing (file paths, directories, or PR diff).
|
|
22
|
-
- Optional: prior review reports
|
|
22
|
+
- Optional: prior review reports for the same target, so this round surfaces only NEW findings. See Prior-Round Awareness.
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Candidate Pipeline
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Nothing you notice is a finding yet. Everything enters as a **candidate** and leaves through exactly one of three exits: `finding`, `needs-validation`, or `rejected`. There is no fourth — a candidate that quietly disappears is a defect of the review.
|
|
27
|
+
|
|
28
|
+
**Stage 1 — Gate.** Clear four checks; any "no" or "unsure" means this is not ready to be a finding:
|
|
27
29
|
|
|
28
30
|
1. **Location** — exact file and line?
|
|
29
31
|
2. **Failure mode** — a concrete input/state and the bad outcome it produces?
|
|
30
32
|
3. **Context** — read the surrounding code (callers, imports, tests), not just the flagged line?
|
|
31
33
|
4. **Severity** — defensible against Rule 2's definitions? (a missing JSDoc is never HIGH)
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## The Five Rules
|
|
36
|
-
|
|
37
|
-
### 1. De-duplicate before writing
|
|
38
|
-
|
|
39
|
-
If the same pattern (e.g., missing null check, unhandled error, magic constant) appears in multiple files, **create one finding** for the most representative instance and list the other affected files inside its body. Do not create N identical findings for N files sharing one root cause.
|
|
40
|
-
|
|
41
|
-
Example — wrong:
|
|
42
|
-
```
|
|
43
|
-
- [HIGH] src/a.ts:14 — missing null check on user
|
|
44
|
-
- [HIGH] src/b.ts:22 — missing null check on user
|
|
45
|
-
- [HIGH] src/c.ts:31 — missing null check on user
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Example — right:
|
|
49
|
-
```
|
|
50
|
-
- [HIGH] Missing null check on `user` in 3 places.
|
|
51
|
-
Representative: src/a.ts:14
|
|
52
|
-
Also affects: src/b.ts:22, src/c.ts:31
|
|
53
|
-
Fix: add `if (!user) return` at function entry.
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 2. Severity-order the output
|
|
57
|
-
|
|
58
|
-
Always present findings in this order: **critical → high → medium → low**. Inside each severity, order by impact or blast radius, not by file path.
|
|
35
|
+
See `references/false-positives.md` for patterns LLM reviewers habitually mis-flag.
|
|
59
36
|
|
|
60
|
-
|
|
61
|
-
- **critical** — correctness bug, security hole, data loss, unavailability.
|
|
62
|
-
- **high** — material deviation from PRD/TechSpec, concurrency hazard, significant perf regression, missing error handling on a user path.
|
|
63
|
-
- **medium** — maintainability cost that will hurt the next change, missing edge-case handling, inconsistent with project rules.
|
|
64
|
-
- **low** — stylistic, naming, minor readability. Often omit unless pattern-level.
|
|
37
|
+
**Stage 2 — Refutation.** The reasoning that produced a candidate cannot also be its check; it anchors every reading that follows. Each candidate clearing Stage 1 faces a deliberate attempt to disprove it. With subagents, dispatch `dw-finding-refuter` with the claim and the raw code, never the reasoning behind it, one candidate per dispatch. Otherwise re-derive the path from source, working to show the outcome cannot happen. Packet contract, upstream guards and both output formats: `references/refutation-pass.md`.
|
|
65
38
|
|
|
66
|
-
|
|
39
|
+
**Stage 3 — Disposition.** Holds on evidence the refutation found for itself → `finding`, with a severity. Blocked upstream, input unreachable, or behavior intended → `rejected`, one line at the end of the report naming what disproved it. Cannot be established either way → `needs-validation`, which never receives a severity and records what is missing and what would settle it. When the environment needed to settle a claim is unavailable, it **stays** `needs-validation` — not promoted, not dropped. Each exit carries different fields: contract in `references/refutation-pass.md`.
|
|
67
40
|
|
|
68
|
-
|
|
69
|
-
- adjacent comment explaining the choice?
|
|
70
|
-
- ADR in `.dw/spec/*/adrs/` that justifies it?
|
|
71
|
-
- test coverage that asserts the behavior?
|
|
72
|
-
- rule in `.dw/rules/` that permits it?
|
|
41
|
+
Report only findings you are **>80% confident** are real *after* Stage 2. Three needs-validation entries and one finding is a more honest round than four.
|
|
73
42
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
### 4. Skip what linters already catch
|
|
77
|
-
|
|
78
|
-
Before writing findings, ensure the project's linter/formatter has run. Anything the configured linter would flag is NOT a finding in this review — it is a linter task. Save human attention for issues linters cannot find (logic, architecture, security, requirements).
|
|
79
|
-
|
|
80
|
-
If the linter cannot run (missing tooling, build errors), note that in the summary and proceed with the review.
|
|
81
|
-
|
|
82
|
-
### 5. Signal over volume
|
|
83
|
-
|
|
84
|
-
Aim for fewer, higher-quality findings.
|
|
43
|
+
## The Five Rules
|
|
85
44
|
|
|
86
|
-
|
|
87
|
-
- If total findings exceed 20, prune `medium` and `low` to the most impactful ones.
|
|
88
|
-
- A review with 8 precise findings is more useful than one with 30 that includes marginal concerns.
|
|
45
|
+
For the five rules, read `references/the-five-rules-detail.md`. Load only when this part of the task applies.
|
|
89
46
|
|
|
90
|
-
|
|
47
|
+
When the scope is an already-merged range (`/dw-review --post-merge`), also read `references/composition-audit.md`: the seven cross-interaction classes, the composition-only defect checklist, the documentation ledger and the semver recommendation.
|
|
91
48
|
|
|
92
49
|
## Prior-Round Awareness
|
|
93
50
|
|
|
94
|
-
|
|
51
|
+
Prior reports live in `<target>/QA/` (PRD target) or `<target>/review/` (bugfix target). Read them and extract three lists — known findings (titles + file/line signatures), open `needs-validation` entries, rejected candidates — then:
|
|
95
52
|
|
|
96
|
-
1.
|
|
97
|
-
2.
|
|
98
|
-
3.
|
|
53
|
+
1. Surface **only NEW findings**. Do not re-flag items already tracked as pending, resolved, or accepted.
|
|
54
|
+
2. Re-check every open `needs-validation` entry. Resolve it into a finding or a rejection when this round has the missing evidence; otherwise carry it forward unchanged. Carrying it forward is a valid outcome — dropping it is not.
|
|
55
|
+
3. Do not re-raise a rejected candidate unless the code changed in a way that defeats the recorded refutation. Name what changed.
|
|
56
|
+
4. If a prior finding was resolved incorrectly, open it as a NEW finding with "Regression of <prior ref>" in the body.
|
|
99
57
|
|
|
100
58
|
## Finding Format
|
|
101
59
|
|
|
@@ -115,14 +73,13 @@ Evidence: <relevant code snippet, test output, or reference>
|
|
|
115
73
|
|
|
116
74
|
The caller emits:
|
|
117
75
|
|
|
118
|
-
1. **Merge/ship recommendation** —
|
|
119
|
-
- `Needs fixes before merge` (if any critical or high exist), with blocking findings named.
|
|
120
|
-
- `Safe to merge with follow-ups` (only medium/low).
|
|
121
|
-
- `Clean — ready to merge` (no findings).
|
|
76
|
+
1. **Merge/ship recommendation** — `Needs fixes before merge` when any critical or high exists, naming the blockers; `Safe to merge with follow-ups` for medium/low only; `Clean — ready to merge` for none.
|
|
122
77
|
2. **Counts** — critical / high / medium / low.
|
|
123
78
|
3. **Findings** — ordered by severity, each in the format above.
|
|
124
|
-
4. **
|
|
125
|
-
5. **
|
|
79
|
+
4. **Needs Validation** — open questions carried by this round, without severities.
|
|
80
|
+
5. **Rejected Candidates** — one line per refuted candidate, with what disproved it.
|
|
81
|
+
6. **Well-implemented aspects** — short bulleted list, calibrates tone.
|
|
82
|
+
7. **Self-score** — rate the report on the five axes in `references/self-eval-rubric.md`; any axis below top cites the gap, then fix (<30s) or flag.
|
|
126
83
|
|
|
127
84
|
## Critical Rules
|
|
128
85
|
|
|
@@ -131,33 +88,19 @@ The caller emits:
|
|
|
131
88
|
- Do not flag patterns that have a clear adjacent justification or ADR.
|
|
132
89
|
- Do not write N identical findings for one root cause — de-duplicate.
|
|
133
90
|
- Do not mix severities — order is critical → high → medium → low.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
- `/dw-review --code-only` — applies all five rules to its Level-3 review output; uses prior reports in `.dw/spec/*/reviews/` to dedupe across rounds.
|
|
138
|
-
- `/dw-review --coverage-only` — applies de-dup + severity-ordering when listing gaps between PRD requirements and code.
|
|
139
|
-
- `/dw-refactor` — applies rules 1, 2, 4, 5 when cataloging code smells (rule 3 adapts: a "smell" with a justifying ADR becomes a `low` finding at most).
|
|
140
|
-
|
|
141
|
-
Callers should mention this skill in their "Skills Complementares" section.
|
|
91
|
+
- Do not assign a severity to anything that did not survive Stage 2.
|
|
92
|
+
- Do not delete a candidate — route it to findings, needs-validation, or rejected.
|
|
142
93
|
|
|
143
94
|
## Inspired by
|
|
144
95
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- No `reviews-NNN/` directory convention — dev-workflow reviews already persist in `.dw/spec/*/reviews/` per command's existing contract.
|
|
148
|
-
- The five rules are extracted here so three different dev-workflow review commands can share the discipline without duplicating it.
|
|
149
|
-
- No issue-file frontmatter (Compozy uses it to interoperate with its remediation engine; dev-workflow's remediation is manual or via `/dw-qa --fix`).
|
|
150
|
-
|
|
151
|
-
Credit: Compozy project (https://github.com/compozy/compozy).
|
|
96
|
+
The five rules are ported from Compozy's `cy-review-round` (credit: [Compozy](https://github.com/compozy/compozy), MIT), extracted here so three review commands share one discipline. The candidate pipeline, `needs-validation` and the rejected-candidate log originate in [`cloudflare/security-audit-skill`](https://github.com/cloudflare/security-audit-skill) (MIT), reaching us via `akitaonrails/my-skills` — chain: cloudflare → akitaonrails → dev-workflow. Reimplemented independently; no upstream text reused.
|
|
152
97
|
|
|
153
98
|
## Structured Return
|
|
154
99
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- **Status:** `PASS` when no defensible findings remain, `FINDINGS` when review findings exist, `BLOCKED` when diff/context is insufficient, `NOT_APPLICABLE` when no review is in scope.
|
|
100
|
+
- **Status:** `PASS` when no defensible findings remain (open `needs-validation` entries do not block it, but each must be listed), `FINDINGS` when review findings exist, `BLOCKED` when diff/context is insufficient, `NOT_APPLICABLE` when no review is in scope.
|
|
158
101
|
- **Scope:** diff range, files reviewed, prior rounds, and review mode.
|
|
159
|
-
- **Evidence:** file/line references, behavior impact,
|
|
102
|
+
- **Evidence:** file/line references, behavior impact, the refutation each finding survived, prior-round disposition.
|
|
160
103
|
- **Artifacts:** review report, inline findings, or consolidation notes.
|
|
161
|
-
- **Decisions:**
|
|
162
|
-
- **Risks:** missing tests, unreviewed generated files, stale base branch,
|
|
163
|
-
- **Next Step:** exact fix, verification, or approval/block marker.
|
|
104
|
+
- **Decisions:** severity and order, duplicate suppression, and every refuted candidate with what disproved it.
|
|
105
|
+
- **Risks:** missing tests, unreviewed generated files, stale base branch, non-defensible claims, open `needs-validation` entries.
|
|
106
|
+
- **Next Step:** exact fix, verification, what would resolve each `needs-validation` entry, or the approval/block marker.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Composition Audit — reviewing N merged PRs as one state
|
|
2
|
+
|
|
3
|
+
Loaded by `/dw-review --post-merge`. It does not replace the five rules or the candidate pipeline; it
|
|
4
|
+
changes what you go looking for when the unit of review is a merged range instead of one branch.
|
|
5
|
+
|
|
6
|
+
## What changes when the scope is a range
|
|
7
|
+
|
|
8
|
+
Every PR in the range was reviewed alone and passed alone. That is exactly why this pass exists: the
|
|
9
|
+
defects below need two changes to exist, so no per-PR review could have caught them, and the collection of
|
|
10
|
+
per-PR summaries is optimistic by construction. Audit the final tree, not the summaries.
|
|
11
|
+
|
|
12
|
+
Two consequences for severity. A defect here is already shipped, which changes **urgency**, not severity —
|
|
13
|
+
do not inflate a medium because it is live, and do not discount a critical because "it has been fine so
|
|
14
|
+
far". And the range is the only honest unit: a finding that says "PR #3 broke it" is usually wrong, because
|
|
15
|
+
what broke it is #3 meeting #7.
|
|
16
|
+
|
|
17
|
+
## The seven interaction classes
|
|
18
|
+
|
|
19
|
+
### 1. Invariant bridges
|
|
20
|
+
|
|
21
|
+
One PR adds a field, column, flag, or route; another populates or authorizes it somewhere that is not the
|
|
22
|
+
canonical boundary. The writer looks fine (it writes a legitimate value) and the reader looks fine (it
|
|
23
|
+
reads a field that exists), but the invariant the boundary was supposed to enforce is now bypassed.
|
|
24
|
+
|
|
25
|
+
- *Detection:* for each new field or path added in the range, `git log -S'<name>' --oneline <range>` to
|
|
26
|
+
find every commit that touched it, then check whether the writers all pass through the same guard.
|
|
27
|
+
- *False positive:* a deliberate second writer with its own validation. Read it before flagging.
|
|
28
|
+
- *Minimum severity:* high when the bypassed boundary is authorization or tenancy, otherwise medium.
|
|
29
|
+
|
|
30
|
+
### 2. Helper and policy drift
|
|
31
|
+
|
|
32
|
+
Two PRs each add or change normalization, identity comparison, validation, retry, error mapping, or a
|
|
33
|
+
permission check — and the two copies now disagree. The system behaves differently depending on which path
|
|
34
|
+
the request took.
|
|
35
|
+
|
|
36
|
+
- *Detection:* `git diff <range> --name-only` grouped by subsystem, then grep for near-duplicate helper
|
|
37
|
+
names and for the same literal in two places.
|
|
38
|
+
- *False positive:* intentional divergence with different requirements. Look for a comment or an ADR.
|
|
39
|
+
- *Minimum severity:* medium; high when the two copies disagree about a security decision.
|
|
40
|
+
|
|
41
|
+
### 3. Default and config composition
|
|
42
|
+
|
|
43
|
+
Each default was compatible on its own. Together they change behavior, create ambiguity, or enable
|
|
44
|
+
something that was meant to stay off. The classic shape: one PR flips a default to "on for new installs"
|
|
45
|
+
and another adds a code path that assumes it is off.
|
|
46
|
+
|
|
47
|
+
- *Detection:* list every default, env var, feature flag, and config key the diff touches, then evaluate
|
|
48
|
+
them as a set, not one at a time. Check the upgrade case — existing installs — separately from a fresh one.
|
|
49
|
+
- *Minimum severity:* high when the composed state is insecure or data-losing.
|
|
50
|
+
|
|
51
|
+
### 4. Order and lifecycle
|
|
52
|
+
|
|
53
|
+
Startup and shutdown ordering, retries, cleanup, transactions, rollback, background jobs, leases. One PR
|
|
54
|
+
adds work at a lifecycle point; another changes when that point runs.
|
|
55
|
+
|
|
56
|
+
- *Detection:* find the lifecycle entry points in the range and read them whole, not as hunks. Ask what
|
|
57
|
+
happens on the second run, on a crash between the two changes, and on rollback.
|
|
58
|
+
- *Minimum severity:* high when a partial state can persist.
|
|
59
|
+
|
|
60
|
+
### 5. Shared resource behavior
|
|
61
|
+
|
|
62
|
+
Queues, connection pools, files, ports, rate limits, caches, locks, disk. Two PRs that each take a
|
|
63
|
+
reasonable share of a bounded resource can together exhaust it.
|
|
64
|
+
|
|
65
|
+
- *Detection:* for each bounded resource the range touches, sum the new consumers. Look for a new consumer
|
|
66
|
+
added to a pool whose size did not change.
|
|
67
|
+
- *Minimum severity:* medium, high when the bound is reached under normal load rather than attack.
|
|
68
|
+
|
|
69
|
+
### 6. Schema, API, and data composition
|
|
70
|
+
|
|
71
|
+
Migrations, wire schemas, public functions, CLI flags, persisted data, older callers. Two migrations that
|
|
72
|
+
each apply cleanly to the pre-range state may not compose; an additive API change plus a rename is a break.
|
|
73
|
+
|
|
74
|
+
- *Detection:* apply the migrations in range order against a copy of the pre-range schema, not against a
|
|
75
|
+
fresh one. For public surfaces, diff the signature set at `BASE_SHA` against `HEAD_SHA` rather than
|
|
76
|
+
reading the PRs' claims.
|
|
77
|
+
- *Minimum severity:* high for anything a released caller can hit.
|
|
78
|
+
|
|
79
|
+
### 7. Test masking
|
|
80
|
+
|
|
81
|
+
The one that most reliably hides in a green build: one PR's mock, fixture, helper, or test config makes
|
|
82
|
+
another PR's test pass without exercising production behavior. Both PRs were green. The composed suite is
|
|
83
|
+
green. The feature does not work.
|
|
84
|
+
|
|
85
|
+
- *Detection:* for each test added in the range, check whether the production path it claims to cover is
|
|
86
|
+
actually reached — a global mock registered in shared setup, an autouse fixture, a stubbed client, a
|
|
87
|
+
widened test config. Where feasible, revert the production change and confirm the test goes red.
|
|
88
|
+
- *False positive:* a legitimately isolated unit test whose integration coverage lives elsewhere. Find that
|
|
89
|
+
elsewhere before flagging.
|
|
90
|
+
- *Minimum severity:* high — a test that cannot fail is worse than no test, because it is counted as
|
|
91
|
+
coverage.
|
|
92
|
+
|
|
93
|
+
## Composition-only defect checklist
|
|
94
|
+
|
|
95
|
+
Run this after the seven classes. These are shapes that per-PR review systematically misses:
|
|
96
|
+
|
|
97
|
+
- an **additive feature that broke a public API by accident** — additive intent does not excuse an
|
|
98
|
+
unrelated signature change;
|
|
99
|
+
- a **fallback that now swallows an unrelated error** — the fallback was added for one failure and now
|
|
100
|
+
catches a second one introduced later;
|
|
101
|
+
- a **test bound to a real home directory, platform, clock, network, or live service** — it passes on the
|
|
102
|
+
machine that wrote it;
|
|
103
|
+
- **one entry point fixed while its parallel implementation went stale** — CLI fixed, API not; one platform
|
|
104
|
+
fixed, the other not;
|
|
105
|
+
- **code only the development platform's gate exercises.** This is the highest-yield item in the list,
|
|
106
|
+
because a single-platform fast gate cannot see any of it:
|
|
107
|
+
- path identity that assumes one canonical spelling — symlinked temp and home roots, drive letter versus
|
|
108
|
+
POSIX separators, a trailing separator, case sensitivity;
|
|
109
|
+
- file-locking semantics that differ per OS;
|
|
110
|
+
- stderr and exit-code differences between platforms for the same command;
|
|
111
|
+
- line endings in anything compared as text.
|
|
112
|
+
|
|
113
|
+
Flag these for the full matrix rather than trusting the merge gate. A red matrix is a real finding — do
|
|
114
|
+
not route around it.
|
|
115
|
+
|
|
116
|
+
## Documentation ledger
|
|
117
|
+
|
|
118
|
+
Build the surface list from the **diff**, never from the PRs' prose. A PR body describes what someone meant
|
|
119
|
+
to ship; the diff describes what shipped.
|
|
120
|
+
|
|
121
|
+
Surfaces to enumerate: features, fixes, changed defaults, flags, env and config fields, supported
|
|
122
|
+
platforms, endpoints, public APIs, schemas, migrations, install and deploy steps, security behavior.
|
|
123
|
+
|
|
124
|
+
For each surface, find every authoritative location and look for a **description that is now wrong**, not
|
|
125
|
+
only a name that is missing. A stale sentence is worse than an absent one: it is confidently incorrect.
|
|
126
|
+
The usual homes are the README's support and compatibility tables, examples, the architecture and config
|
|
127
|
+
reference, migration notes, and platform docs.
|
|
128
|
+
|
|
129
|
+
## Semver recommendation
|
|
130
|
+
|
|
131
|
+
From the diff:
|
|
132
|
+
|
|
133
|
+
- only fixes → **patch**;
|
|
134
|
+
- any additive surface → **minor**;
|
|
135
|
+
- any break — on-disk format, public API, CLI or wire contract, a removed surface → **major**.
|
|
136
|
+
|
|
137
|
+
State the recommendation and the **single highest-impact entry** that forces it, so the reader can check
|
|
138
|
+
the one thing the conclusion rests on.
|
|
139
|
+
|
|
140
|
+
Then check two changelog hazards that a heading-uniqueness script cannot catch:
|
|
141
|
+
|
|
142
|
+
- **An entry stranded in an already-released section.** A PR authored before a release was cut anchors its
|
|
143
|
+
entry at the old position of "Unreleased". Merging the mainline back can resolve *cleanly, with no
|
|
144
|
+
conflict*, and leave the entry inside the now-frozen released section — claiming the change shipped in a
|
|
145
|
+
version that never contained it. Compare the changelog at `BASE_SHA` with the lines the range added:
|
|
146
|
+
`git diff <BASE_SHA>...<HEAD_SHA> -- <changelog>`, and confirm each new entry sits under the pending
|
|
147
|
+
heading.
|
|
148
|
+
- **Merge-resolution residue.** `git diff --check` flags leftover conflict markers, but scripted resolution
|
|
149
|
+
also leaves diff3 base markers (`|||||||`) and duplicated bullets, which no uniqueness check sees. Grep
|
|
150
|
+
for the marker and scan for repeated entries.
|
|
151
|
+
|
|
152
|
+
This section produces a recommendation and nothing else. `/dw-review --post-merge` does not tag, bump, or
|
|
153
|
+
publish.
|
|
154
|
+
|
|
155
|
+
## Attribution
|
|
156
|
+
|
|
157
|
+
The boundary freeze, provenance inventory, cross-interaction sweep, documentation ledger and the two
|
|
158
|
+
changelog hazards were reimplemented independently from the technique described in
|
|
159
|
+
`akitaonrails/my-skills`. That repository declares no license; no upstream text, structure or file was
|
|
160
|
+
reused, and no reuse license is assumed. The release step described there was deliberately not ported.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Refutation Pass — disprove the candidate before it becomes a finding
|
|
2
|
+
|
|
3
|
+
A candidate is a hypothesis produced by one line of reasoning. That same reasoning cannot test it: once a
|
|
4
|
+
conclusion is reached, every later reading is pulled toward it. Re-reading your own trace feels like
|
|
5
|
+
verification and is not. The pass below replaces the reader.
|
|
6
|
+
|
|
7
|
+
## With a subagent: the refutation packet
|
|
8
|
+
|
|
9
|
+
Dispatch `dw-finding-refuter`, **one candidate per run**. It receives exactly this and nothing else:
|
|
10
|
+
|
|
11
|
+
| Include | Exclude |
|
|
12
|
+
|---|---|
|
|
13
|
+
| The claim in one sentence: location, input or state, bad outcome | How the candidate was found |
|
|
14
|
+
| The raw code — the flagged lines, the file, the entry points that reach it | Your confidence and your severity |
|
|
15
|
+
| Which commands it may run | Every other candidate from this round |
|
|
16
|
+
| — | Prior-round text, the PRD, the diff summary |
|
|
17
|
+
|
|
18
|
+
Severity is withheld deliberately. A candidate handed over labelled `critical` comes back confirmed more
|
|
19
|
+
often than the identical candidate labelled `medium`, and that difference is the anchor, not the evidence.
|
|
20
|
+
|
|
21
|
+
The refuter returns `REFUTED`, `HOLDS`, or `UNRESOLVED`. One case needs care: if it returns `HOLDS` citing
|
|
22
|
+
only the `file:line` you handed it, that is an echo, not confirmation. Treat it as `UNRESOLVED`.
|
|
23
|
+
|
|
24
|
+
## Without a subagent: re-derive from zero
|
|
25
|
+
|
|
26
|
+
Read the path as if you had never seen the candidate — start at the entry point, move forward, and reach
|
|
27
|
+
the flagged line **last**. The objective is to show the bad outcome cannot occur. Work the list in order
|
|
28
|
+
and stop at the first hit:
|
|
29
|
+
|
|
30
|
+
1. **Validation upstream** — a schema, parser, or guard clause the value already passed.
|
|
31
|
+
2. **Permission upstream** — an authorization check, middleware, or policy on the route.
|
|
32
|
+
3. **A type or schema that cannot hold the bad value** — a typed boundary, an enum, a branded type, a
|
|
33
|
+
`NOT NULL` constraint.
|
|
34
|
+
4. **A framework or runtime default** — auto-escaping, parameterized queries, CSRF middleware, a default
|
|
35
|
+
timeout.
|
|
36
|
+
5. **Configuration that is not what the claim assumed** — read the config file; do not infer it from
|
|
37
|
+
naming.
|
|
38
|
+
6. **Intent** — a test asserting the behavior, an adjacent comment, an ADR, a rule under `.dw/rules/`.
|
|
39
|
+
|
|
40
|
+
If everything you can say about the candidate at the end is what you already said at the start, the result
|
|
41
|
+
is `UNRESOLVED`, not confirmed.
|
|
42
|
+
|
|
43
|
+
## What each exit may and may not carry
|
|
44
|
+
|
|
45
|
+
The three exits are not three severities of the same thing — they are three different claims, and each one
|
|
46
|
+
is entitled to different fields. Most of the damage a review does comes from mixing them: a severity on an
|
|
47
|
+
unresolved lead reads as an assessed risk, and a remediation on a refuted claim sends someone to fix
|
|
48
|
+
nothing.
|
|
49
|
+
|
|
50
|
+
| Exit | Carries | **Must not carry** |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `finding` | The confirmed cause, the reachable input or state, the observable outcome, the fix, a severity | A *claimed* cause — by this point it is established or it is not a finding |
|
|
53
|
+
| `needs-validation` | The **claimed** cause, the trace, the evidence so far, what blocks resolution, and how to resolve it | **A severity.** Also no fix and no exploit path — you do not have the facts those require |
|
|
54
|
+
| `rejected` | The claim as it was made, and what disproved it | A severity, a fix, an exploit path, a blocker — the claim is closed, not pending |
|
|
55
|
+
|
|
56
|
+
The prohibition on severity for `needs-validation` is the load-bearing one. A severity is an assessment of
|
|
57
|
+
impact, and an unresolved lead has not established the impact. "Possible critical" is not a cautious
|
|
58
|
+
finding; it is a number with nothing behind it, and it will be read as a ranking.
|
|
59
|
+
|
|
60
|
+
## Degradation: no environment means unresolved, not resolved
|
|
61
|
+
|
|
62
|
+
When the environment needed to settle a claim is unavailable — no sandbox, no runtime, no access to the
|
|
63
|
+
configuration that decides it — the candidate stays `needs-validation`. It is not promoted because the
|
|
64
|
+
reasoning looked strong, and it is not dropped because it could not be checked.
|
|
65
|
+
|
|
66
|
+
This is the rule that keeps the class honest. Without it, a missing environment quietly turns into whatever
|
|
67
|
+
the reviewer already believed.
|
|
68
|
+
|
|
69
|
+
## needs-validation format
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
[needs-validation] <file.ext>:<line> — <what is unresolved, ≤72 chars>
|
|
73
|
+
|
|
74
|
+
Unresolved: <the specific fact that could not be established>
|
|
75
|
+
Blocked by: <why — missing runtime config, unreadable caller, external system, no test data>
|
|
76
|
+
Resolved by: <the concrete thing that would settle it — a file, a command, an owner's answer>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
No severity. No "possible critical", no "likely exploitable". The whole point of the class is that you do
|
|
80
|
+
not know yet, and saying so plainly is the honest output. A severity attached here is a severity invented.
|
|
81
|
+
|
|
82
|
+
## Rejected candidate format
|
|
83
|
+
|
|
84
|
+
One line each, under `## Rejected Candidates` at the end of the report:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
- <claim in one line> → refuted: <what disproves it> (<file:line or command>)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Keep it even when it feels like clutter. The line is what stops the next round from rediscovering the same
|
|
91
|
+
false positive and paying for the same refutation twice.
|
|
92
|
+
|
|
93
|
+
## Anti-patterns
|
|
94
|
+
|
|
95
|
+
- **Sending the refuter your reasoning "for context."** That is the exact failure this pass exists to
|
|
96
|
+
prevent; the packet is short on purpose.
|
|
97
|
+
- **Refuting a batch.** One candidate per run. Batching restores the shared frame you were trying to
|
|
98
|
+
escape.
|
|
99
|
+
- **Letting the refuter open findings of its own.** Its output is a verdict on one claim. New observations
|
|
100
|
+
it makes along the way are for the next round, not this report.
|
|
101
|
+
- **Promoting `UNRESOLVED` to a finding** because the round would otherwise look thin. Three
|
|
102
|
+
`needs-validation` entries and one finding is a more honest round than four findings.
|
|
103
|
+
- **Deleting a refuted candidate** instead of logging it.
|
|
104
|
+
- **Treating "the refuter found no blocker" as proof.** Absence of a refutation is `UNRESOLVED` unless the
|
|
105
|
+
refuter cited independent evidence for `HOLDS`.
|
|
106
|
+
|
|
107
|
+
## Proportionality
|
|
108
|
+
|
|
109
|
+
The pass is for candidates that will carry a severity. A duplicate of an already-reported root cause, a
|
|
110
|
+
finding the configured linter owns, or a cosmetic note does not need a refuter — it needs to be dropped or
|
|
111
|
+
merged under Rule 1. Spend the pass where a wrong answer would cost something.
|
|
112
|
+
|
|
113
|
+
## Attribution
|
|
114
|
+
|
|
115
|
+
The three-outcome discipline — adversarial refutation by a reader who did not produce the claim, an
|
|
116
|
+
unresolved-lead class that carries no severity, and a durable record of rejected candidates — originates in
|
|
117
|
+
[`cloudflare/security-audit-skill`](https://github.com/cloudflare/security-audit-skill) (MIT), which calls
|
|
118
|
+
it adversarial verification, needs-validation discipline and coverage honesty. It reached this project
|
|
119
|
+
through `akitaonrails/my-skills`, which credits Cloudflare for the same model and declares no license of
|
|
120
|
+
its own. **Attribution chain: cloudflare → akitaonrails → dev-workflow.**
|
|
121
|
+
|
|
122
|
+
The per-exit field contract and the degradation rule above come from reading the Cloudflare skill directly:
|
|
123
|
+
there they are enforced by a JSON schema and a validator script, and here they are prose, because our
|
|
124
|
+
reviews are markdown reports rather than validated records. No upstream text or file was reused.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## The Five Rules
|
|
2
|
+
|
|
3
|
+
### 1. De-duplicate before writing
|
|
4
|
+
|
|
5
|
+
If the same pattern (e.g., missing null check, unhandled error, magic constant) appears in multiple files, **create one finding** for the most representative instance and list the other affected files inside its body. Do not create N identical findings for N files sharing one root cause.
|
|
6
|
+
|
|
7
|
+
Example — wrong:
|
|
8
|
+
```
|
|
9
|
+
- [HIGH] src/a.ts:14 — missing null check on user
|
|
10
|
+
- [HIGH] src/b.ts:22 — missing null check on user
|
|
11
|
+
- [HIGH] src/c.ts:31 — missing null check on user
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Example — right:
|
|
15
|
+
```
|
|
16
|
+
- [HIGH] Missing null check on `user` in 3 places.
|
|
17
|
+
Representative: src/a.ts:14
|
|
18
|
+
Also affects: src/b.ts:22, src/c.ts:31
|
|
19
|
+
Fix: add `if (!user) return` at function entry.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Severity-order the output
|
|
23
|
+
|
|
24
|
+
Always present findings in this order: **critical → high → medium → low**. Inside each severity, order by impact or blast radius, not by file path.
|
|
25
|
+
|
|
26
|
+
Severity definitions:
|
|
27
|
+
- **critical** — correctness bug, security hole, data loss, unavailability.
|
|
28
|
+
- **high** — material deviation from PRD/TechSpec, concurrency hazard, significant perf regression, missing error handling on a user path.
|
|
29
|
+
- **medium** — maintainability cost that will hurt the next change, missing edge-case handling, inconsistent with project rules.
|
|
30
|
+
- **low** — stylistic, naming, minor readability. Often omit unless pattern-level.
|
|
31
|
+
|
|
32
|
+
### 3. Verify intent before flagging
|
|
33
|
+
|
|
34
|
+
Before creating a finding, check whether the pattern is intentional:
|
|
35
|
+
- adjacent comment explaining the choice?
|
|
36
|
+
- ADR in `.dw/spec/*/adrs/` that justifies it?
|
|
37
|
+
- test coverage that asserts the behavior?
|
|
38
|
+
- rule in `.dw/rules/` that permits it?
|
|
39
|
+
|
|
40
|
+
If the code looks suspicious but has a clear justification (e.g., `// intentionally ignoring close error on read-only handle`), do NOT create a finding. Only flag patterns that are genuinely problematic, not merely unconventional.
|
|
41
|
+
|
|
42
|
+
### 4. Skip what linters already catch
|
|
43
|
+
|
|
44
|
+
Before writing findings, ensure the project's linter/formatter has run. Anything the configured linter would flag is NOT a finding in this review — it is a linter task. Save human attention for issues linters cannot find (logic, architecture, security, requirements).
|
|
45
|
+
|
|
46
|
+
If the linter cannot run (missing tooling, build errors), note that in the summary and proceed with the review.
|
|
47
|
+
|
|
48
|
+
### 5. Signal over volume
|
|
49
|
+
|
|
50
|
+
Aim for fewer, higher-quality findings.
|
|
51
|
+
|
|
52
|
+
- Keep ALL `critical` and `high`.
|
|
53
|
+
- If total findings exceed 20, prune `medium` and `low` to the most impactful ones.
|
|
54
|
+
- A review with 8 precise findings is more useful than one with 30 that includes marginal concerns.
|
|
55
|
+
|
|
56
|
+
**Also note well-implemented aspects.** They inform the summary and calibrate tone — but they do not produce findings.
|
|
@@ -30,64 +30,11 @@ Do NOT use when:
|
|
|
30
30
|
|
|
31
31
|
## The Five Rules
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Before removing or rewriting any code, answer three questions:
|
|
36
|
-
|
|
37
|
-
1. **What does it actually do?** Run/trace it; don't guess from the name.
|
|
38
|
-
2. **Why was it added?** Check `git log --follow <file> --oneline` and look at the introducing commit's message + PR. Often there's a reason.
|
|
39
|
-
3. **What breaks if it's gone?** Search for callers. Run the test suite to find what changes.
|
|
40
|
-
|
|
41
|
-
If you can't answer all three, you're not ready to simplify. Get answers first.
|
|
42
|
-
|
|
43
|
-
Concrete cases where Chesterton's Fence saves you:
|
|
44
|
-
- A "redundant" early return that handles a race condition.
|
|
45
|
-
- A "useless" type cast that fixes a compiler bug on a specific platform.
|
|
46
|
-
- A "duplicated" check that exists for clarity at the API boundary.
|
|
47
|
-
- A "dead" branch that runs only with a feature flag enabled in production.
|
|
48
|
-
|
|
49
|
-
### 2. Preserve behavior exactly
|
|
50
|
-
|
|
51
|
-
The test for "did I simplify or did I change?" is: do the existing tests still pass without modification? If yes, behavior is preserved. If you needed to update tests, you changed behavior — that's a refactor, not a simplification.
|
|
52
|
-
|
|
53
|
-
When tests are inadequate to confirm behavior preservation, write tests FIRST that document current behavior, then simplify against them. This is the test characterization technique.
|
|
54
|
-
|
|
55
|
-
### 3. Follow project conventions
|
|
56
|
-
|
|
57
|
-
Match what the project already does. If files use 2-space indent and arrow functions, your simplified version uses 2-space indent and arrow functions — even if you'd prefer 4-space and `function`. Conventions matter more than personal preference; consistency is itself a form of clarity.
|
|
58
|
-
|
|
59
|
-
Project conventions live in `.dw/rules/<module>.md` (from `/dw-analyze-project`). Read first.
|
|
60
|
-
|
|
61
|
-
### 4. Prefer clarity over cleverness
|
|
62
|
-
|
|
63
|
-
Rules of thumb:
|
|
64
|
-
|
|
65
|
-
- A line of code that takes 30 seconds to understand is worse than three lines that take 5 seconds each.
|
|
66
|
-
- A "neat trick" with comments is uglier than the boring obvious code without comments.
|
|
67
|
-
- Optimization that costs readability needs measurement (`dw-performance` skill territory) — without numbers, prefer the readable version.
|
|
68
|
-
- "Magic" that requires knowing a language feature most teammates haven't used is anti-clarity. Use it only when the alternative is genuinely worse.
|
|
69
|
-
|
|
70
|
-
### 5. Scope to recent changes
|
|
71
|
-
|
|
72
|
-
Don't go simplifying old code while you're "in there". The risk-to-benefit ratio of changing 5-year-old battle-tested code is bad. Limit simplification to:
|
|
73
|
-
|
|
74
|
-
- Code you just wrote in this session.
|
|
75
|
-
- Code your current task touches.
|
|
76
|
-
- Code with explicit recent breakage (e.g., a bug just fixed that revealed bad structure).
|
|
77
|
-
|
|
78
|
-
Out of scope: any other code, no matter how ugly. Open a task / ADR for those instead of fixing in-flight.
|
|
33
|
+
For the five rules, read `references/the-five-rules-detail.md`. Load only when this part of the task applies.
|
|
79
34
|
|
|
80
35
|
## Pattern recognition
|
|
81
36
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Smell | Healthy refactor | When NOT to refactor |
|
|
85
|
-
|-------|------------------|----------------------|
|
|
86
|
-
| Deep nesting (4+ levels) | Early returns / guard clauses | When the nesting maps to a real domain hierarchy (e.g., visitor pattern) |
|
|
87
|
-
| Long function (>50 lines) | Extract method | When the "natural" extraction would produce 3-4 single-call helpers — you're trading complexity for indirection |
|
|
88
|
-
| Generic name (`data`, `info`, `helper`) | Rename to specific | When the function genuinely processes generic input (e.g., a serializer middleware) |
|
|
89
|
-
| Duplication (same 5 lines twice) | Extract to function/constant | When the duplication is by design (e.g., independent business rules that may diverge) |
|
|
90
|
-
| `if/elif/else` chain (5+ branches) | Strategy pattern / lookup table | When the branches are naturally exclusive states with branch-specific logic |
|
|
37
|
+
For pattern recognition, read `references/pattern-recognition-detail.md`. Load only when this part of the task applies.
|
|
91
38
|
|
|
92
39
|
## Rule of 500 — automate large refactors
|
|
93
40
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Pattern recognition
|
|
2
|
+
|
|
3
|
+
Frequent simplification targets — but apply Rules 1-5 before acting:
|
|
4
|
+
|
|
5
|
+
| Smell | Healthy refactor | When NOT to refactor |
|
|
6
|
+
|-------|------------------|----------------------|
|
|
7
|
+
| Deep nesting (4+ levels) | Early returns / guard clauses | When the nesting maps to a real domain hierarchy (e.g., visitor pattern) |
|
|
8
|
+
| Long function (>50 lines) | Extract method | When the "natural" extraction would produce 3-4 single-call helpers — you're trading complexity for indirection |
|
|
9
|
+
| Generic name (`data`, `info`, `helper`) | Rename to specific | When the function genuinely processes generic input (e.g., a serializer middleware) |
|
|
10
|
+
| Duplication (same 5 lines twice) | Extract to function/constant | When the duplication is by design (e.g., independent business rules that may diverge) |
|
|
11
|
+
| `if/elif/else` chain (5+ branches) | Strategy pattern / lookup table | When the branches are naturally exclusive states with branch-specific logic |
|