@brunosps00/dev-workflow 0.8.1 → 0.10.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 (96) hide show
  1. package/README.md +30 -27
  2. package/bin/dev-workflow.js +1 -1
  3. package/lib/constants.js +6 -8
  4. package/lib/init.js +6 -0
  5. package/lib/install-deps.js +0 -5
  6. package/lib/migrate-gsd.js +164 -0
  7. package/lib/uninstall.js +2 -2
  8. package/package.json +1 -1
  9. package/scaffold/en/commands/dw-analyze-project.md +6 -11
  10. package/scaffold/en/commands/dw-autopilot.md +10 -17
  11. package/scaffold/en/commands/dw-brainstorm.md +2 -2
  12. package/scaffold/en/commands/dw-bugfix.md +1 -0
  13. package/scaffold/en/commands/dw-code-review.md +7 -5
  14. package/scaffold/en/commands/dw-commit.md +6 -0
  15. package/scaffold/en/commands/dw-create-prd.md +5 -4
  16. package/scaffold/en/commands/dw-create-techspec.md +7 -4
  17. package/scaffold/en/commands/dw-deep-research.md +6 -0
  18. package/scaffold/en/commands/dw-deps-audit.md +1 -0
  19. package/scaffold/en/commands/dw-find-skills.md +4 -4
  20. package/scaffold/en/commands/dw-fix-qa.md +1 -0
  21. package/scaffold/en/commands/dw-generate-pr.md +1 -0
  22. package/scaffold/en/commands/dw-help.md +10 -27
  23. package/scaffold/en/commands/dw-intel.md +99 -30
  24. package/scaffold/en/commands/dw-map-codebase.md +125 -0
  25. package/scaffold/en/commands/dw-new-project.md +1 -1
  26. package/scaffold/en/commands/dw-redesign-ui.md +5 -9
  27. package/scaffold/en/commands/dw-refactoring-analysis.md +8 -6
  28. package/scaffold/en/commands/dw-review-implementation.md +28 -2
  29. package/scaffold/en/commands/dw-run-plan.md +14 -20
  30. package/scaffold/en/commands/dw-run-task.md +5 -4
  31. package/scaffold/en/commands/dw-update.md +3 -1
  32. package/scaffold/en/templates/idea-onepager.md +2 -2
  33. package/scaffold/pt-br/commands/dw-analyze-project.md +6 -11
  34. package/scaffold/pt-br/commands/dw-autopilot.md +10 -17
  35. package/scaffold/pt-br/commands/dw-brainstorm.md +2 -2
  36. package/scaffold/pt-br/commands/dw-bugfix.md +1 -0
  37. package/scaffold/pt-br/commands/dw-code-review.md +7 -5
  38. package/scaffold/pt-br/commands/dw-commit.md +6 -0
  39. package/scaffold/pt-br/commands/dw-create-prd.md +5 -4
  40. package/scaffold/pt-br/commands/dw-create-techspec.md +7 -4
  41. package/scaffold/pt-br/commands/dw-deep-research.md +6 -0
  42. package/scaffold/pt-br/commands/dw-deps-audit.md +1 -0
  43. package/scaffold/pt-br/commands/dw-find-skills.md +4 -4
  44. package/scaffold/pt-br/commands/dw-fix-qa.md +1 -0
  45. package/scaffold/pt-br/commands/dw-generate-pr.md +1 -0
  46. package/scaffold/pt-br/commands/dw-help.md +10 -27
  47. package/scaffold/pt-br/commands/dw-intel.md +99 -30
  48. package/scaffold/pt-br/commands/dw-map-codebase.md +125 -0
  49. package/scaffold/pt-br/commands/dw-new-project.md +1 -1
  50. package/scaffold/pt-br/commands/dw-redesign-ui.md +5 -9
  51. package/scaffold/pt-br/commands/dw-refactoring-analysis.md +8 -6
  52. package/scaffold/pt-br/commands/dw-review-implementation.md +21 -2
  53. package/scaffold/pt-br/commands/dw-run-plan.md +16 -22
  54. package/scaffold/pt-br/commands/dw-run-task.md +5 -4
  55. package/scaffold/pt-br/commands/dw-update.md +3 -1
  56. package/scaffold/pt-br/templates/idea-onepager.md +2 -2
  57. package/scaffold/skills/dw-codebase-intel/SKILL.md +102 -0
  58. package/scaffold/skills/dw-codebase-intel/agents/intel-updater.md +318 -0
  59. package/scaffold/skills/dw-codebase-intel/references/api-design-discipline.md +138 -0
  60. package/scaffold/skills/dw-codebase-intel/references/incremental-update.md +79 -0
  61. package/scaffold/skills/dw-codebase-intel/references/intel-format.md +208 -0
  62. package/scaffold/skills/dw-codebase-intel/references/query-patterns.md +148 -0
  63. package/scaffold/skills/dw-debug-protocol/SKILL.md +106 -0
  64. package/scaffold/skills/dw-debug-protocol/references/error-categorization.md +127 -0
  65. package/scaffold/skills/dw-debug-protocol/references/non-reproducible-strategy.md +108 -0
  66. package/scaffold/skills/dw-debug-protocol/references/six-step-triage.md +139 -0
  67. package/scaffold/skills/dw-debug-protocol/references/stop-the-line.md +52 -0
  68. package/scaffold/skills/dw-execute-phase/SKILL.md +133 -0
  69. package/scaffold/skills/dw-execute-phase/agents/executor.md +264 -0
  70. package/scaffold/skills/dw-execute-phase/agents/plan-checker.md +215 -0
  71. package/scaffold/skills/dw-execute-phase/references/atomic-commits.md +143 -0
  72. package/scaffold/skills/dw-execute-phase/references/plan-verification.md +156 -0
  73. package/scaffold/skills/dw-execute-phase/references/wave-coordination.md +102 -0
  74. package/scaffold/skills/dw-git-discipline/SKILL.md +120 -0
  75. package/scaffold/skills/dw-git-discipline/references/atomic-commits-discipline.md +158 -0
  76. package/scaffold/skills/dw-git-discipline/references/branch-hygiene.md +150 -0
  77. package/scaffold/skills/dw-git-discipline/references/trunk-based-pattern.md +82 -0
  78. package/scaffold/skills/dw-memory/SKILL.md +1 -2
  79. package/scaffold/skills/dw-simplification/SKILL.md +142 -0
  80. package/scaffold/skills/dw-simplification/references/behavior-preserving.md +148 -0
  81. package/scaffold/skills/dw-simplification/references/chestertons-fence.md +152 -0
  82. package/scaffold/skills/dw-simplification/references/complexity-metrics.md +147 -0
  83. package/scaffold/skills/dw-source-grounding/SKILL.md +128 -0
  84. package/scaffold/skills/dw-source-grounding/references/citation-protocol.md +108 -0
  85. package/scaffold/skills/dw-source-grounding/references/freshness-check.md +108 -0
  86. package/scaffold/skills/dw-source-grounding/references/source-priority.md +146 -0
  87. package/scaffold/skills/dw-verify/SKILL.md +0 -1
  88. package/scaffold/skills/vercel-react-best-practices/SKILL.md +4 -0
  89. package/scaffold/skills/vercel-react-best-practices/references/perf-discipline.md +122 -0
  90. package/scaffold/skills/webapp-testing/SKILL.md +5 -0
  91. package/scaffold/skills/webapp-testing/references/security-boundary.md +115 -0
  92. package/scaffold/skills/webapp-testing/references/three-workflow-patterns.md +144 -0
  93. package/scaffold/en/commands/dw-quick.md +0 -85
  94. package/scaffold/en/commands/dw-resume.md +0 -82
  95. package/scaffold/pt-br/commands/dw-quick.md +0 -85
  96. package/scaffold/pt-br/commands/dw-resume.md +0 -82
@@ -0,0 +1,82 @@
1
+ # Trunk-based development — short branches, fast integration
2
+
3
+ Trunk-based means: `main` is always shippable, and most work lands on `main` within hours-to-days, not weeks. The opposite (long-lived feature branches) creates merge debt that compounds non-linearly.
4
+
5
+ ## The rules
6
+
7
+ 1. **Branches live 1-3 days, max a week.** Past a week, you're carrying risk.
8
+ 2. **Rebase from `main` daily.** A 1-day rebase is trivial; a 7-day one is a project.
9
+ 3. **Incomplete work hides behind a feature flag.** It can sit in `main` half-built without users seeing it.
10
+ 4. **Small PRs (≤400 lines diff).** Larger PRs get worse review quality and longer review cycles.
11
+ 5. **CI green before merge, always.** Yellow/red CI on `main` blocks everyone.
12
+
13
+ ## Why short branches
14
+
15
+ A 7-day branch is not 7× the work of a 1-day branch. It's much worse:
16
+
17
+ - **Compound merge cost.** Every day, the gap between your branch and `main` grows. Conflicts multiply non-linearly because the same files keep moving on both sides.
18
+ - **Stale assumptions.** You wrote code against APIs/types from a week ago. The current version differs in ways you don't know.
19
+ - **Lost context.** By day 7, you've forgotten why you wrote line 200 of file X. Reviews suffer.
20
+ - **Coordination drag.** Other developers can't ship work that depends on your branch finishing.
21
+
22
+ ## Feature flags vs feature branches
23
+
24
+ The classic question: "I'm building feature X but it's not ready to ship — won't merging it break things?"
25
+
26
+ Two options:
27
+
28
+ | Long branch | Feature flag |
29
+ |-------------|--------------|
30
+ | Code lives on a branch until ready | Code lives on `main` behind a `if (flag)` check |
31
+ | Merge debt accumulates daily | No merge debt; integrates as you go |
32
+ | Other devs can't see your changes | Other devs can read/review/depend on contracts |
33
+ | Single big risky merge at the end | Many small low-risk merges |
34
+ | Hard to demo / preview | Can demo to stakeholders by toggling flag |
35
+
36
+ Feature flags win for almost every non-trivial feature. Costs:
37
+
38
+ - Flag system needs to exist (LaunchDarkly, GrowthBook, env-based, or just a constant).
39
+ - Flags have a lifecycle: introduce → ship code behind flag → enable → remove flag. The remove step is critical and often skipped.
40
+
41
+ ## When trunk-based bends
42
+
43
+ - **Open-source / external contributors:** they can't push directly to `main`; long branches are inherent to fork-and-PR. Mitigation: small PRs, fast review, frequent merges.
44
+ - **Compliance / audited environments:** PR + multi-stage review may take days regardless of branch life. Compress what you can.
45
+ - **Big migrations / refactors crossing many modules:** can't always be flag-gated. Mitigation: break into many small PRs, each independently shippable, merged sequentially.
46
+
47
+ In all bend cases, the SPIRIT of trunk-based still applies: minimize the gap between branch and `main`; integrate often; don't let drift compound.
48
+
49
+ ## The "let it sit on a branch" anti-pattern
50
+
51
+ Common scenario: developer finishes feature X, opens PR, gets review delays, starts feature Y on a new branch from X (because Y depends on X), then X gets review feedback, X is rebased, Y now diverges from rebased X, X merges, Y has to rebase against the merged-X (different from branched-X), conflicts everywhere, Y stalls, X starts to bit-rot in production usage…
52
+
53
+ Three fixes:
54
+
55
+ 1. **Don't stack branches.** If Y depends on X, wait for X to land. (Or merge X behind a flag and start Y from `main`.)
56
+ 2. **Cut review delays.** A PR sitting in review for 3 days is the bug; fix the review process, don't work around it.
57
+ 3. **Tighter scope.** If X is so big it takes 3 days to review, X should have been three PRs.
58
+
59
+ ## CI and trunk-based
60
+
61
+ Trunk-based assumes CI is fast and reliable. If CI takes 45 minutes and is flaky:
62
+
63
+ - Developers won't rebase frequently (too painful).
64
+ - Branches drift, then conflicts on merge are big.
65
+ - Pressure mounts to bypass CI ("just merge it, CI's broken anyway").
66
+
67
+ Fixes happen in two directions:
68
+
69
+ - Make CI fast (under 10 minutes ideally; under 20 acceptable).
70
+ - Make CI reliable (flaky tests are bugs; fix or remove them).
71
+
72
+ A team that can't trust CI cannot run trunk-based effectively.
73
+
74
+ ## Trunk-based + feature flags + atomic commits
75
+
76
+ These three disciplines compound:
77
+
78
+ - **Atomic commits** make rebases and reverts surgical.
79
+ - **Trunk-based** keeps branches short, so rebases are tiny.
80
+ - **Feature flags** decouple "code in main" from "feature live for users."
81
+
82
+ Together: you can ship work daily, revert any single change without unwinding others, and toggle features without redeploys. This is the working norm for high-velocity teams; it's not optional discipline, it's load-bearing.
@@ -155,8 +155,7 @@ When flagged for compaction, apply inline:
155
155
 
156
156
  - `/dw-run-task` — reads memory before coding; updates `<N>_memory.md` during; runs promotion test + updates `MEMORY.md` at the end.
157
157
  - `/dw-run-plan` — runs promotion + compaction between tasks, so each task starts with clean shared state.
158
- - `/dw-autopilot` — threads memory through every phase (brainstorm → PRD → techspec → tasks → execution).
159
- - `/dw-resume` — reads `MEMORY.md` first to reconstitute cross-session context, then the active task's memory.
158
+ - `/dw-autopilot` — threads memory through every phase (brainstorm → PRD → techspec → tasks → execution); on re-invocation reads `MEMORY.md` first to reconstitute cross-session context.
160
159
 
161
160
  Callers should mention this skill in their "Skills Complementares" section.
162
161
 
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: dw-simplification
3
+ description: Disciplined code simplification — understand WHY code exists before changing it (Chesterton's Fence), preserve behavior exactly, prefer clarity over cleverness, scope to recent changes. Used by /dw-code-review and /dw-refactoring-analysis. Adapted from addyosmani/agent-skills (MIT).
4
+ allowed-tools:
5
+ - Read
6
+ - Edit
7
+ - Bash
8
+ - Grep
9
+ - Glob
10
+ ---
11
+
12
+ # dw-simplification
13
+
14
+ Behavioral discipline for simplifying code without breaking it. The trap of refactoring is removing something that "looked unused" but was load-bearing for an edge case nobody documented. This skill enforces a protocol that prevents that class of regression.
15
+
16
+ ## When to Use
17
+
18
+ Read this skill when:
19
+
20
+ - `/dw-code-review` flagged a complexity issue (deep nesting, long function, duplication).
21
+ - `/dw-refactoring-analysis` proposed a simplification target.
22
+ - The user explicitly asks to "clean this up" / "simplify X".
23
+ - During `/dw-run-task` if the implementation accidentally produced complex code that wants pre-commit cleanup.
24
+
25
+ Do NOT use when:
26
+
27
+ - The complexity is intentional (e.g., performance hot path with optimization comments — leave alone).
28
+ - You're simplifying code you didn't just write or read recently — scope creep.
29
+ - Tests are missing for the area — without tests, "preserve behavior exactly" is unverifiable. Add tests first, then simplify.
30
+
31
+ ## The Five Rules
32
+
33
+ ### 1. Chesterton's Fence — understand BEFORE changing
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.
79
+
80
+ ## Pattern recognition
81
+
82
+ Frequent simplification targets — but apply Rules 1-5 before acting:
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 |
91
+
92
+ ## Rule of 500 — automate large refactors
93
+
94
+ If a simplification touches >500 lines, **don't do it manually**. Use:
95
+
96
+ - AST-based codemods (`jscodeshift` for JS/TS, `libcst` for Python, Roslyn for C#, `rust-analyzer` for Rust).
97
+ - IDE-driven refactoring tools (Rename Symbol, Extract Method, Inline Variable).
98
+ - Search-and-replace ONLY when the pattern is unambiguous and verifiable.
99
+
100
+ Manual edits across hundreds of lines are how subtle bugs creep in.
101
+
102
+ ## Verification protocol
103
+
104
+ Before committing the simplification:
105
+
106
+ ```
107
+ 1. Lint passes: pnpm lint / ruff check / dotnet format --verify-no-changes / cargo clippy
108
+ 2. Tests pass: pnpm test / pytest / dotnet test / cargo test
109
+ 3. Build passes: pnpm tsc --noEmit / mypy / dotnet build / cargo check
110
+ ```
111
+
112
+ All three GREEN. If any is RED, the simplification broke something — revert or fix.
113
+
114
+ For changes that altered cyclomatic complexity, optionally run a complexity analyzer to confirm the metric actually improved (some "simplifications" make code more complex by spreading it).
115
+
116
+ ## How `dw-code-review` uses this
117
+
118
+ In the formal Level 3 review (post-Level 2 chain from `dw-review-implementation`), code-review flags complexity issues using the patterns above. Each flagged issue references this skill: "consider simplifying via guard clauses; apply Chesterton's Fence — verify why the nested check exists before flattening."
119
+
120
+ ## How `dw-refactoring-analysis` uses this
121
+
122
+ The analysis catalogs code smells (Fowler vocabulary). For each smell, the proposed refactor cites:
123
+ 1. Which simplification rule applies (early return / extract method / lookup table / etc.).
124
+ 2. Whether Chesterton's Fence concerns block the refactor (existing tests inadequate? no recent commits explaining the structure? → flag as YELLOW, don't act).
125
+
126
+ ## Anti-patterns
127
+
128
+ 1. Simplifying code you didn't read carefully. The "obvious dead code" is often someone's hard-won bug fix.
129
+ 2. Skipping the test gate "because the change is small". Small changes break things too.
130
+ 3. Changing whitespace + naming + structure in one commit. Atomic: each commit one kind of change.
131
+ 4. Refactoring while touching unrelated code (mix-in scope creep). Open a separate task.
132
+ 5. Personal-style refactors disguised as simplification. Project conventions > your preferences.
133
+
134
+ ## References
135
+
136
+ - `references/chestertons-fence.md` — the protocol in detail; case studies of "obvious-but-wrong" removals.
137
+ - `references/complexity-metrics.md` — when each metric (cyclomatic, cognitive, depth, fanout) actually matters; how to measure cheaply.
138
+ - `references/behavior-preserving.md` — characterization tests, refactor with test gate, rollback patterns, codemod tooling per language.
139
+
140
+ ## Inspired by
141
+
142
+ Adapted from [`addyosmani/agent-skills/code-simplification`](https://github.com/addyosmani/agent-skills) by Addy Osmani (MIT license). Core principles (Chesterton's Fence, behavior preservation, scope discipline, Rule of 500) preserved. dev-workflow integration: invoked by `dw-code-review` and `dw-refactoring-analysis` via Complementary Skills.
@@ -0,0 +1,148 @@
1
+ # Behavior-preserving refactor — the test gate that protects you
2
+
3
+ The promise of simplification: the code is cleaner; nothing observable changed. The risk: you broke a subtle behavior nobody had a test for.
4
+
5
+ ## The test gate
6
+
7
+ Before simplifying ANY non-trivial code, ensure tests cover what you're about to change. Three cases:
8
+
9
+ ### Case A — tests exist and pass
10
+
11
+ Run them. They pass before, they pass after. Done.
12
+
13
+ ### Case B — tests exist but are incomplete
14
+
15
+ Run them. They pass before. Apply your simplification. They still pass — but you don't trust them to catch your specific concern.
16
+
17
+ Action: write a characterization test FIRST that pins the behavior you're worried about. Then simplify.
18
+
19
+ ### Case C — no tests at all
20
+
21
+ Don't simplify. The "preserve behavior exactly" claim is unverifiable. Either:
22
+ - Write characterization tests first (see below), then simplify.
23
+ - Or open a separate task ("add tests for X") and don't touch the code now.
24
+
25
+ ## Characterization tests
26
+
27
+ A characterization test documents what the code currently does, without claiming whether that's correct. It's a freeze of behavior at a point in time.
28
+
29
+ Pattern:
30
+
31
+ ```javascript
32
+ // Characterization test — not asserting CORRECTNESS,
33
+ // asserting BEHAVIOR-AT-TIME-OF-WRITING.
34
+ describe('legacyFormatter (characterization)', () => {
35
+ it('handles empty input by returning "(none)"', () => {
36
+ expect(legacyFormatter('')).toBe('(none)');
37
+ });
38
+
39
+ it('preserves trailing whitespace in output', () => {
40
+ expect(legacyFormatter('hello ')).toBe('hello ');
41
+ });
42
+
43
+ it('returns null when input is undefined', () => {
44
+ expect(legacyFormatter(undefined)).toBe(null);
45
+ });
46
+ });
47
+ ```
48
+
49
+ Goal: capture the WEIRD behaviors. Boring behaviors don't need tests; nobody breaks them. The weird parts are the load-bearing ones.
50
+
51
+ ## How to find weird behaviors to characterize
52
+
53
+ 1. Run the function with a range of inputs (empty, null, undefined, very long, special chars, edge values).
54
+ 2. Note any output that surprised you. That's a weird behavior.
55
+ 3. Write a test that pins it.
56
+
57
+ Example session:
58
+
59
+ ```bash
60
+ node -e "console.log(JSON.stringify(require('./src/legacy').format('')))"
61
+ # Output: "(none)" ← weird, document it.
62
+
63
+ node -e "console.log(JSON.stringify(require('./src/legacy').format('hello ')))"
64
+ # Output: "hello " ← preserves trailing whitespace? unusual, document.
65
+
66
+ node -e "console.log(JSON.stringify(require('./src/legacy').format(undefined)))"
67
+ # Output: null ← returns null not "(none)" for undefined? document.
68
+ ```
69
+
70
+ Tests written; now you can simplify. If after your refactor any of these tests fail, you broke behavior — even if your version "looks better".
71
+
72
+ ## Refactor with the test gate
73
+
74
+ ```
75
+ 1. Run tests → GREEN (baseline)
76
+ 2. Apply simplification (one logical change)
77
+ 3. Run tests → expect GREEN
78
+ - If RED → revert, understand why, retry differently
79
+ 4. Commit (atomic — one simplification per commit)
80
+ 5. Repeat for next simplification
81
+ ```
82
+
83
+ DO NOT batch multiple simplifications into one commit. If something breaks, you can't bisect which change caused it.
84
+
85
+ ## Rollback patterns
86
+
87
+ If the simplification went wrong AND the issue surfaces only post-merge:
88
+
89
+ ```bash
90
+ git revert <simplification-sha>
91
+ ```
92
+
93
+ Atomic commits make this clean. The revert is one commit; tests pass again; you can investigate at leisure.
94
+
95
+ If multiple simplifications were stacked and one broke:
96
+
97
+ ```bash
98
+ # Find which one
99
+ git bisect start
100
+ git bisect bad HEAD
101
+ git bisect good <known-good-sha>
102
+ # Run tests at each step
103
+ ```
104
+
105
+ Bisect requires that EACH commit be testable in isolation — another reason for atomic commits.
106
+
107
+ ## Codemod tooling per language
108
+
109
+ For refactors >500 lines, manual edits are dangerous. Use AST-based codemods:
110
+
111
+ ### TypeScript / JavaScript
112
+ - **jscodeshift** — Facebook's AST-based codemod runner. Many community codemods at `npmjs.com/package/<framework>-codemods`.
113
+ - **ts-morph** — programmatic TypeScript AST manipulation. Good for one-off refactors.
114
+ - **ESLint --fix** — for any rule that has a fixer (most of them); narrow scope.
115
+
116
+ ```bash
117
+ # Run a community codemod
118
+ npx jscodeshift -t ./codemods/extract-method.js src/
119
+ ```
120
+
121
+ ### Python
122
+ - **libcst** — Instagram's concrete syntax tree library. Preserves comments + formatting.
123
+ - **ast** module + custom walker for simpler cases.
124
+ - **Ruff --fix** for known auto-fixable rules.
125
+
126
+ ### C# / .NET
127
+ - **Roslyn analyzers + code fixes** — write a custom analyzer with a fix provider; runs across the solution.
128
+ - **Visual Studio Refactor menu** for IDE-driven multi-file renames/extractions.
129
+
130
+ ### Rust
131
+ - **rust-analyzer** has refactor support: rename, extract function, inline variable.
132
+ - **rustfmt + clippy --fix** for style/idiom-level fixes.
133
+
134
+ ## Don't
135
+
136
+ - Don't simplify and update tests in the same commit. The test changes hide what you changed in the code.
137
+ - Don't consider lint passing as "behavior preserved". Lint catches syntax; tests catch behavior.
138
+ - Don't trust "no test failed therefore behavior preserved" if test coverage is <60%. Add tests first.
139
+ - Don't pre-emptively simplify for a future hypothetical maintainer. Simplify when you have a present reason (a bug, a read-time confusion).
140
+
141
+ ## When the refactor is too risky
142
+
143
+ If steps 2-3 of the test gate consistently fail (you can't pin the behavior, or your simplification keeps breaking subtle things):
144
+
145
+ - The code is genuinely complex for a reason; leave it.
146
+ - Or: rewrite-with-tests as a separate, larger task — not in-flight simplification.
147
+
148
+ The right call sometimes is "this code is ugly but works; ship around it; revisit when you have hours, not minutes."
@@ -0,0 +1,152 @@
1
+ # Chesterton's Fence — understand WHY before changing
2
+
3
+ > "If you find a fence in the middle of a field, don't tear it down until you know why it was put there." — G.K. Chesterton (paraphrased)
4
+
5
+ In code, the "fence" is anything that looks unnecessary on first read. Most code that looks unnecessary is actually load-bearing for a case the casual reader hasn't encountered.
6
+
7
+ ## The protocol
8
+
9
+ Before removing or rewriting anything:
10
+
11
+ ### 1. What does it actually do?
12
+
13
+ Don't trust the name. Names lie. Names get repurposed. Read the body. Trace the call graph.
14
+
15
+ If you can't say in one sentence what the code does (not what it's named, what it DOES), you don't yet understand it.
16
+
17
+ ### 2. Why was it added?
18
+
19
+ ```bash
20
+ # When did this line/block enter the codebase?
21
+ git log -L <start>,<end>:<file> --oneline | head -5
22
+
23
+ # Or for a whole file
24
+ git log --follow --oneline <file> | head -10
25
+
26
+ # Read the introducing commit
27
+ git show <commit-hash>
28
+ ```
29
+
30
+ The commit message + PR title + linked issue often have the rationale. Common findings:
31
+
32
+ - "Fix race condition when X happens during Y" — the seemingly redundant check is the fix.
33
+ - "Workaround for [framework] bug #1234" — the weird code is a workaround.
34
+ - "Required for [external system] compatibility" — the cast/wrapper exists for a reason.
35
+ - "TODO: simplify after we deprecate Y" — there's an explicit plan, follow it.
36
+
37
+ ### 3. What breaks if it's gone?
38
+
39
+ ```bash
40
+ # Find callers
41
+ rg -F "<symbol>" --type <lang>
42
+
43
+ # Run the test suite
44
+ <test-runner>
45
+ ```
46
+
47
+ If tests pass with the code removed, it might be safe — but tests have gaps. Cross-check with:
48
+ - Search the whole codebase for the symbol/string
49
+ - Check `.dw/intel/files.json` for `imports` referencing it
50
+ - For exported symbols, check downstream consumers (other repos, npm, etc.)
51
+
52
+ If even one of the three answers is "I don't know", DON'T REMOVE.
53
+
54
+ ## Case studies
55
+
56
+ ### Case 1 — "Redundant" early return
57
+
58
+ Code:
59
+
60
+ ```python
61
+ def process(item):
62
+ if item is None:
63
+ return
64
+ if item.id is None:
65
+ return
66
+ # ...rest...
67
+ ```
68
+
69
+ Looks like the second `if` could merge into the first via `if item is None or item.id is None`. But:
70
+
71
+ - `item is None` is a system error (caller passed None).
72
+ - `item.id is None` is a domain state (item not yet persisted).
73
+
74
+ The two cases warrant different downstream behavior in the future (e.g., logging the second but not the first). Merging them collapses the distinction. Chesterton's Fence: leave alone unless you also remove the future flexibility intentionally.
75
+
76
+ ### Case 2 — "Useless" type cast
77
+
78
+ Code:
79
+
80
+ ```typescript
81
+ const value = (someApi.fetch() as unknown) as MyType;
82
+ ```
83
+
84
+ Looks like `as MyType` would suffice. But the double-cast was added because the API's actual return type is incompatible with `MyType` and a direct cast errors. Removing the `as unknown` step breaks compilation.
85
+
86
+ The fix isn't to remove the double-cast; it's to either fix `MyType` (if the API is right) or fix the API typings (if `MyType` is right). Chesterton's Fence: read the commit message — usually says "compiler workaround for incompatible types".
87
+
88
+ ### Case 3 — "Duplicated" validation
89
+
90
+ Code:
91
+
92
+ ```javascript
93
+ function createUser(payload) {
94
+ if (!payload.email) throw new ValidationError('email required');
95
+ // ...
96
+ }
97
+
98
+ function updateUser(id, payload) {
99
+ if (!payload.email) throw new ValidationError('email required');
100
+ // ...
101
+ }
102
+ ```
103
+
104
+ Looks like duplication; "extract" to `validatePayload(payload)`. But:
105
+
106
+ - `createUser` requires email.
107
+ - `updateUser` may eventually allow email-less updates (e.g., just change name).
108
+
109
+ Today they happen to share validation; tomorrow they might not. The "duplication" preserves independence of evolution. Don't extract a shared validator until at least 3 callers exist with truly identical rules.
110
+
111
+ ### Case 4 — "Dead" branch
112
+
113
+ Code:
114
+
115
+ ```typescript
116
+ if (process.env.LEGACY_MIGRATION_MODE === 'true') {
117
+ return legacyTransform(input);
118
+ }
119
+ return modernTransform(input);
120
+ ```
121
+
122
+ Looks dead because nobody sets `LEGACY_MIGRATION_MODE` in test/dev. But:
123
+
124
+ - Production sets it on the data-import service.
125
+ - The migration runs once a quarter when new historical data lands.
126
+
127
+ Removing the branch means the next quarterly migration breaks silently. Chesterton's Fence: check production env vars / feature flags / runtime configs before deleting "dead" branches.
128
+
129
+ ## When you DO act
130
+
131
+ After all three protocol steps, if:
132
+
133
+ 1. You understand what the code does.
134
+ 2. You know why it was added (and the reason no longer applies, or you have evidence it never applied).
135
+ 3. You've verified nothing breaks.
136
+
137
+ THEN you can simplify. Cite the rationale in the commit message:
138
+
139
+ ```
140
+ refactor(auth): remove fallback to legacy session API
141
+
142
+ The fallback was added in 2022-04 for compatibility with the v1
143
+ session service, which was decommissioned in 2024-09 (PR #4567).
144
+ No callers reference the legacy code path; tests confirm the
145
+ modern path covers all scenarios. Removed.
146
+ ```
147
+
148
+ Future maintainers can see why the change was safe. They can also see — if it turns out NOT to be safe — exactly what assumption was wrong.
149
+
150
+ ## Anti-pattern
151
+
152
+ Removing code because "it looks unused" without doing the three steps. Speed is not the goal; correctness is. A 30-minute investigation to avoid a 3-day production rollback is a great trade.
@@ -0,0 +1,147 @@
1
+ # Complexity metrics — when each one actually matters
2
+
3
+ Metrics are signal, not verdict. A function with cyclomatic complexity 12 isn't automatically "bad" — but it's worth a second look. This file describes the four common metrics, when each matters, and how to measure cheaply.
4
+
5
+ ## The four metrics
6
+
7
+ ### 1. Cyclomatic complexity
8
+
9
+ Counts independent paths through a function. Each `if`, `else`, `for`, `while`, `case`, `&&`, `||`, `?:` adds one.
10
+
11
+ | Score | What it means | Action |
12
+ |-------|---------------|--------|
13
+ | 1-5 | Simple, easily testable | Leave alone |
14
+ | 6-10 | Moderate; usually fine | Test thoroughly |
15
+ | 11-20 | Complex; review case-by-case | Strong candidate to simplify |
16
+ | 21+ | Very complex; high bug rate empirically | Definitely simplify (or split) |
17
+
18
+ Caveat: long switch/match statements over enum values can score high but be naturally clear. Cyclomatic complexity over-penalizes them.
19
+
20
+ ### 2. Cognitive complexity
21
+
22
+ Counts how hard the function is to **understand** (not just trace through). Nesting amplifies; flat structures don't. `goto`, `break N levels`, and recursion add weight.
23
+
24
+ This metric correlates better with bug rate than cyclomatic. Most modern linters compute it (SonarQube, ESLint with `sonarjs`, `radon` for Python).
25
+
26
+ | Score | Action |
27
+ |-------|--------|
28
+ | 0-9 | Fine |
29
+ | 10-15 | Review |
30
+ | 16+ | Refactor |
31
+
32
+ ### 3. Nesting depth
33
+
34
+ Counts the maximum depth of `if`/`for`/`try` nesting in a function.
35
+
36
+ | Depth | Action |
37
+ |-------|--------|
38
+ | 1-2 | Fine |
39
+ | 3 | Acceptable; consider early returns |
40
+ | 4 | Smell; usually flatten via guard clauses |
41
+ | 5+ | Almost always refactor |
42
+
43
+ Easiest to fix via early returns:
44
+
45
+ ```javascript
46
+ // Before — depth 4
47
+ function process(req) {
48
+ if (req) {
49
+ if (req.user) {
50
+ if (req.user.active) {
51
+ if (req.body) {
52
+ return doWork(req.body);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ // After — depth 1
61
+ function process(req) {
62
+ if (!req) return null;
63
+ if (!req.user) return null;
64
+ if (!req.user.active) return null;
65
+ if (!req.body) return null;
66
+ return doWork(req.body);
67
+ }
68
+ ```
69
+
70
+ ### 4. Fan-out (outgoing dependencies)
71
+
72
+ Number of distinct other modules a function calls. High fan-out = many touchpoints; refactor risky.
73
+
74
+ | Fan-out | Action |
75
+ |---------|--------|
76
+ | 0-3 | Fine |
77
+ | 4-7 | Acceptable |
78
+ | 8+ | Function probably has too many concerns; consider splitting |
79
+
80
+ ## When each metric matters
81
+
82
+ | Symptom | Metric to check |
83
+ |---------|-----------------|
84
+ | "This function has a lot of `if`s" | Cyclomatic + nesting depth |
85
+ | "I keep getting lost reading this" | Cognitive complexity |
86
+ | "Tests miss edge cases" | Cyclomatic (each path needs a test) |
87
+ | "Refactor keeps breaking adjacent code" | Fan-out + fan-in |
88
+ | "Function is 200 lines" | Length + nesting (length alone is weak signal) |
89
+
90
+ ## How to measure cheaply
91
+
92
+ ### TypeScript / JavaScript
93
+
94
+ ```bash
95
+ # ESLint with sonarjs plugin
96
+ npx eslint --rule 'sonarjs/cognitive-complexity: ["error", 15]' src/
97
+
98
+ # Or per-file analysis
99
+ npx complexity-report src/foo.ts
100
+
101
+ # Bundle analysis adjacent
102
+ npx eslint --rule 'complexity: ["warn", 10]' src/
103
+ ```
104
+
105
+ ### Python
106
+
107
+ ```bash
108
+ # Radon — cyclomatic + cognitive
109
+ pip install radon
110
+ radon cc src/ -a # cyclomatic, average per file
111
+ radon mi src/ # maintainability index
112
+
113
+ # Or via ruff
114
+ ruff check --select C901 src/
115
+ ```
116
+
117
+ ### C# / .NET
118
+
119
+ ```bash
120
+ # Code metrics via Roslyn
121
+ dotnet build /p:CodeAnalysisRuleSet=...
122
+ # Or in Visual Studio: Analyze → Calculate Code Metrics
123
+ ```
124
+
125
+ ### Rust
126
+
127
+ ```bash
128
+ # Clippy + complexity lints
129
+ cargo clippy -- -W clippy::cognitive_complexity
130
+
131
+ # Or rust-code-analysis CLI
132
+ rust-code-analysis-cli -m -p src/
133
+ ```
134
+
135
+ ## Don't
136
+
137
+ - **Don't chase a score.** "Cyclomatic 9 vs 10" is meaningless. The function either reads clearly or doesn't.
138
+ - **Don't refactor purely to lower a number.** If `cognitive_complexity: 16` flags a switch over 16 enum cases, leaving it alone is fine.
139
+ - **Don't measure the whole repo at once and act on the top 100.** Most will be false positives. Focus on functions you're touching.
140
+ - **Don't ignore the trend.** A file going from average cognitive 8 → 14 over 6 months is a smell, even if no individual function crossed the threshold.
141
+
142
+ ## Healthy use of metrics
143
+
144
+ - Run on PRs that touched complex code; flag if a function moved to a worse bucket.
145
+ - Run on long-lived hot files quarterly; spot drift.
146
+ - Set CI gates ONLY at gross thresholds (e.g., cognitive >25), not at edge thresholds (>10).
147
+ - Treat metrics as conversation starters, not pass/fail gates.