@@ -60,7 +60,8 @@ If a finding is even partially covered by an existing issue or PR, skip it entir
6060
6161## Step 2: Code Analysis
6262
63- Scan the ** entire repository** looking for these categories (in priority order):
63+ Scan the ** entire repository** looking for these categories (in priority order).
64+ Use the ** Detection Playbook** (Appendix) for concrete patterns and thresholds.
6465
6566### Category A: Bugs (Highest Priority)
6667- Incorrect error handling (swallowed errors, missing try/catch, wrong error types)
@@ -198,3 +199,145 @@ A successful run means:
198199- Zero overlap with existing work
199200- All tests pass
200201- A human reviewer can understand and approve within 5 minutes
202+
203+ ---
204+
205+ # Appendix: Detection Playbook
206+
207+ Consolidated reference for Step 2 code analysis. All patterns are scoped to this
208+ TypeScript/Node.js codebase (ES2022 target, CommonJS output).
209+
210+ ** How to use:** When scanning files in Step 2, check each file against the relevant
211+ sections below. These are detection heuristics — only flag issues that meet the
212+ confidence threshold from Step 3.
213+
214+ ---
215+
216+ ## A. Complexity Thresholds
217+
218+ Flag any function/file exceeding these limits:
219+
220+ | Metric | Warning | Error | Fix |
221+ | ---| ---| ---| ---|
222+ | Function length | >30 lines | >50 lines | Extract method |
223+ | Nesting depth | >2 levels | >3 levels | Guard clauses / extract |
224+ | Parameter count | >3 | >5 | Parameter object or options bag |
225+ | File length | >300 lines | >500 lines | Split by responsibility |
226+ | Cyclomatic complexity | >5 branches | >10 branches | Decompose conditional / lookup table |
227+
228+ ** Cognitive complexity red flags:**
229+ - Nested ternaries: ` a ? b ? c : d : e `
230+ - Boolean soup: ` a && b || c && !d ` (missing parens)
231+ - Multiple return points inside nested blocks
232+
233+ ---
234+
235+ ## B. Bug Patterns (Category A)
236+
237+ ### Error Handling
238+ - ** Empty catch blocks:** ` catch (e) {} ` — silently swallows errors
239+ - ** Missing error cause when wrapping:** When rethrowing or wrapping an existing error, preserve it via ` throw new Error(msg, { cause: err }) ` instead of dropping the original error.
240+ - ** Broad catch:** Giant try/catch wrapping entire functions instead of targeting fallible ops
241+ - ** Error type check by name:** ` e.constructor.name === 'TypeError' ` instead of ` instanceof `
242+
243+ ### Async/Promise Issues
244+ - ** Missing ` await ` :** Calling async function without ` await ` — result is discarded
245+ - ** Unhandled rejection:** Promise chain without ` .catch() ` or surrounding try/catch
246+ - ** Sequential independent awaits:** ` await a(); await b() ` when they could be ` Promise.all([a(), b()]) `
247+ - ** Unnecessary async:** ` async function f() { return val; } ` — the ` async ` adds overhead for no reason
248+
249+ ### Resource Leaks
250+ - ** Unclosed gRPC streams:** Streams opened but not closed in error paths
251+ - ** Dangling timers:** ` setTimeout ` /` setInterval ` without cleanup on teardown
252+ - ** Event listener leaks:** ` .on() ` without corresponding ` .off() ` or ` .removeListener() `
253+
254+ ### Repo-Specific (Durable Task SDK)
255+ - ** Non-determinism in orchestrators:** ` Date.now() ` , ` Math.random() ` , ` crypto.randomUUID() ` , or direct I/O in orchestrator code
256+ - ** Replay event mismatch:** The executor's switch statement silently drops unmatched events — verify all cases are handled
257+ - ** Timer-to-retry linkage:** Retry tasks create timers with different IDs; verify maps connecting them are maintained
258+ - ** Generator lifecycle:** Check for unguarded ` generator.next() ` when ` done ` might be true
259+ - ** Composite task constructor:** Already-complete children trigger ` onChildCompleted() ` during construction — callers must handle immediate completion
260+ - ** Entity state mutation:** Verify lazy JSON deserialization handles null/undefined state correctly
261+
262+ ---
263+
264+ ## C. Dead Code Patterns (Category C)
265+
266+ ### What to Look For
267+ - ** Unused imports:** Import bindings never referenced in the file
268+ - ** Unused variables:** ` const ` /` let ` declared but never read
269+ - ** Unreachable code:** Statements after ` return ` , ` throw ` , ` break ` , ` continue ` , or ` process.exit() `
270+ - ** Commented-out code:** 3+ consecutive lines of commented code-like patterns — should be removed (use version control)
271+ - ** Unused private functions:** Functions not exported and not called within the module
272+ - ** Dead parameters:** Function parameters never referenced in the body (check interface contracts first)
273+ - ** Always-true/false conditions:** ` if (true) ` , literal tautologies
274+ - ** Stale TODOs:** ` TODO ` /` FIXME ` /` HACK ` comments in code unchanged for months
275+
276+ ### False Positive Guards
277+ - Variables used in string interpolation or destructuring
278+ - Parameters required by interface contracts (gRPC callbacks, event handlers)
279+ - Re-exports through barrel ` index.ts ` files
280+
281+ ### Prioritization
282+ | Category | Impact | Action |
283+ | ---| ---| ---|
284+ | Empty catch blocks | ** High** | Review — likely hiding errors |
285+ | Unreachable code | Medium | Remove — may indicate bugs |
286+ | Unused variables | Medium | Remove — may indicate logic errors |
287+ | Unused functions | Medium | Confirm no dynamic/reflection usage first |
288+ | Unused imports | Low | Remove — zero risk |
289+ | Commented-out code | Low | Remove |
290+ | Dead parameters | Low | Check interface contracts first |
291+
292+ ---
293+
294+ ## D. TypeScript Modernization Patterns (Category C)
295+
296+ Only flag these when the improvement is clear and low-risk. These are ** not** the
297+ primary mission — prioritize bugs and missing tests.
298+
299+ ### High Value (flag these)
300+ | Verbose Pattern | Modern Alternative |
301+ | ---| ---|
302+ | ` x && x.y && x.y.z ` | ` x?.y?.z ` (optional chaining) |
303+ | ` x !== null && x !== undefined ? x : def ` | ` x ?? def ` (nullish coalescing) |
304+ | Manual ` for ` loop building/filtering array | ` .map() ` / ` .filter() ` / ` .find() ` / ` .reduce() ` |
305+ | ` .then().then().catch() ` chains | ` async ` /` await ` with try/catch |
306+ | ` Object.assign({}, a, b) ` | ` { ...a, ...b } ` (spread) |
307+ | ` JSON.parse(JSON.stringify(obj)) ` on JSON-serializable data | ` structuredClone(obj) ` — note: ` structuredClone ` throws on non-serializable values (e.g. functions, symbols); only substitute when data is known to be JSON-serializable |
308+ | ` obj.hasOwnProperty(k) ` | ` Object.hasOwn(obj, k) ` |
309+ | ` arr[arr.length - 1] ` | ` arr.at(-1) ` |
310+ | ` for (k in obj) ` (includes prototype keys) | ` for (const k of Object.keys(obj)) ` |
311+ | ` throw new Error(msg) ` losing cause | ` throw new Error(msg, { cause: origErr }) ` |
312+
313+ ### Medium Value (flag only if clearly better)
314+ | Verbose Pattern | Modern Alternative |
315+ | ---| ---|
316+ | ` const x = obj.x; const y = obj.y; ` | ` const { x, y } = obj ` (destructuring) |
317+ | String concatenation ` 'Hello ' + name ` | Template literal `` `Hello ${name}` `` |
318+ | ` function(x) { return x * 2; } ` | ` x => x * 2 ` (arrow) |
319+ | ` { x: x, y: y } ` | ` { x, y } ` (shorthand property) |
320+ | Implicit ` any ` on public API | Add explicit type annotations |
321+
322+ ### Do NOT Flag (out of scope for this repo)
323+ - CommonJS ` require() ` → ESM ` import ` (repo intentionally uses CommonJS)
324+ - React/Next.js patterns (not in this codebase)
325+ - ES2024+ features (` Object.groupBy ` , ` Set.intersection ` , ` Promise.withResolvers ` ) — repo targets ES2022
326+ - Private field naming (` this._secret ` vs ` #secret ` ); repo uses ` _underscorePrefixed ` per ` .github/copilot-instructions.md `
327+
328+ ---
329+
330+ ## E. Refactoring Strategies
331+
332+ When a fix requires restructuring, use the simplest applicable strategy:
333+
334+ 1 . ** Guard clauses** — Flatten nested if/else by returning early for edge cases
335+ 2 . ** Extract method** — Pull 30+ line blocks with a clear purpose into named functions
336+ 3 . ** Replace loop with pipeline** — ` for ` + ` if ` + ` push ` → ` .filter().map() `
337+ 4 . ** Decompose conditional** — Extract complex boolean expressions into well-named variables
338+ 5 . ** Consolidate duplicates** — If every branch of a conditional has the same code, move it outside
339+ 6 . ** Extract constant** — Replace magic numbers/strings with named constants
340+ 7 . ** Introduce parameter object** — When 4+ related params travel together
341+
342+ ** Key principle:** Only refactor code that you're already fixing for a bug or test gap.
343+ Don't open PRs for refactoring alone — it must accompany a concrete fix.
0 commit comments