slash-do 2.10.0 → 2.12.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/README.md +3 -1
- package/commands/do/depfree.md +104 -4
- package/commands/do/help.md +2 -0
- package/commands/do/pr-better.md +94 -0
- package/commands/do/review.md +7 -1
- package/commands/do/rpr.md +3 -3
- package/commands/do/scan.md +775 -0
- package/install.sh +2 -2
- package/lib/code-review-checklist.md +68 -14
- package/lib/copilot-review-loop.md +18 -11
- package/lib/review-cross-file-tracing.md +26 -2
- package/lib/review-security-audit.md +6 -1
- package/lib/review-surface-scan.md +63 -6
- package/package.json +1 -1
- package/uninstall.sh +2 -2
|
@@ -30,22 +30,59 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
30
30
|
**Runtime correctness**
|
|
31
31
|
- Null/undefined access without guards; off-by-one errors; spread of null (is `{}`), spread of non-objects (string → indexed chars, array → numeric keys) — guard with plain-object check before spreading
|
|
32
32
|
- External/user data (parsed JSON, API responses, file reads) used without structural validation — guard parse failures, missing properties, wrong types, null elements. Optional enrichment failures should not abort the main operation
|
|
33
|
-
- Type coercion: `Number('')` is `0` not empty; `0` is falsy in truthy checks; `NaN` comparisons always false; `"10" < "2"` (lexicographic). Deserialized booleans: `"false"` is truthy — use `=== 'true'`. `isinstance(x, int)` accepts `bool` in Python; `typeof NaN === 'number'` in JS
|
|
33
|
+
- Type coercion: `Number('')` is `0` not empty; `0` is falsy in truthy checks; `NaN` comparisons always false; `"10" < "2"` (lexicographic). Deserialized booleans: `"false"` is truthy — use `=== 'true'`. `isinstance(x, int)` accepts `bool` in Python; `typeof NaN === 'number'` in JS. For env-var numeric parsing in particular, use `Number.parseInt(String(value).trim(), 10)` and gate with `Number.isFinite(parsed)` — `NaN` flowing into subprocess args (`-p NaN`) or formatted strings produces opaque downstream failures (whitespace/inline-comment values are common culprits)
|
|
34
34
|
- Config/option defaults applied with `||` or falsy guards — intentional `0`, `false`, or `''` values are treated as "unset" and replaced by the default. Use `?? default` or explicit `=== undefined` checks when zero, false, or empty string are valid configuration values
|
|
35
35
|
- Functions returning different types depending on input conditions (string in one branch, object in another) — callers must branch on return type; prefer a consistent return shape
|
|
36
36
|
- Indexing empty arrays; `every`/`some`/`reduce` on empty collections returning vacuously true; declared-but-never-updated state/variables
|
|
37
37
|
- Parallel arrays coupled by index position — use objects/maps keyed by stable identifier
|
|
38
38
|
- Shared mutable references: module-level defaults mutated across calls (use `structuredClone()`); `useCallback`/`useMemo` referencing later `const` (temporal dead zone); spread followed by unconditional assignment clobbering spread values
|
|
39
|
+
- React state invariants (uniqueness, cap/floor, monotonicity) checked against render-time value before `setX(...)` — rapid events or concurrent updates race the check. Move into the functional updater: `setX(prev => prev.includes(id) ? prev : [...prev, id])`
|
|
40
|
+
- `useEffect` depending on state it writes — the write retriggers the effect (infinite loop / request storm). Split into two effects, drop the self-written value from deps, or use a functional setter that doesn't require the current value in deps
|
|
39
41
|
- Functions with >10 branches or >15 cyclomatic complexity — refactor
|
|
42
|
+
- String accumulation via `+=` inside high-frequency loops (streaming frames, chunked I/O, per-event handlers) is O(n²) for long outputs and triggers React re-renders on growing payloads. Collect chunks into an array and `join('')` at the end while still emitting per-chunk events
|
|
43
|
+
- Server-side string formatting (`toLocaleString`, `toLocaleDateString`, currency/number formatters) that depends on locale/timezone defaults produces non-deterministic outputs across deployments. For data flowing into prompts, logs, or persisted records, format with explicit `Intl.DateTimeFormat({ timeZone: ... })` or ISO strings; reserve locale-aware formatting for user-visible UI layers
|
|
44
|
+
- Required-at-use-time config values (model name, API key, endpoint URL, default selection) that may be null/undefined in source data must be validated at the boundary before invoking the downstream API. Otherwise the API responds with an opaque error far from the user's intent. Emit a clear, actionable error identifying the missing field
|
|
40
45
|
|
|
41
46
|
**API route basics**
|
|
42
|
-
- Route params passed to services without format validation; path containment using string prefix without separator boundary (use `path.relative()`)
|
|
47
|
+
- Route params passed to services without format validation; path containment using string prefix without separator boundary (use `path.relative()`). When sibling endpoints validate body/query fields, the path param must be validated with the same schema — skipping param validation on one endpoint turns schema violations into 404/500 instead of 400
|
|
43
48
|
- Parameterized/wildcard routes registered before specific named routes (`/:id` before `/drafts` matches `/drafts` as `id="drafts"`)
|
|
44
49
|
- Stored or external URLs rendered as clickable links without protocol validation — allowlist `http:`/`https:`
|
|
45
50
|
- Request schema fields for large string/binary payloads (base64, file content, free text) without per-field size limits — a total body-size limit alone doesn't prevent individual oversized fields from consuming excessive memory or exceeding downstream service limits; add per-field `max(N)` constraints with clear error messages
|
|
51
|
+
- Character-class regex validators (`^[a-z0-9-]+$`) claiming to enforce a structured format (slug, kebab-case, reverse-DNS) — they accept leading/trailing separators (`-foo`, `foo-`) and repeated separators (`a--b`). Require alnum boundaries or use a parser
|
|
52
|
+
- `z.string().min(1)` without `.trim()` accepts whitespace-only values for user-visible names — use `z.string().trim().min(1)` when the field represents a human-readable identifier
|
|
53
|
+
- Object spread of a potentially-null/undefined boundary value (`{ ...req.body, id }`) — throws a TypeError and surfaces as 500 instead of 4xx. Use `{ ...(req.body ?? {}), id }` at request/boundary entry points
|
|
54
|
+
- Schemas accepting paired range fields (`startDate`/`endDate`, `min`/`max`, `from`/`to`) without a cross-field refinement (`zod .refine()`) — accepts inconsistent ranges (start > end). Define deterministic rules when only one bound is supplied
|
|
55
|
+
- Outbound payloads (snippets, previews, cached excerpts) stored in persisted records or sent over streaming protocols without size caps — applies the same per-field max constraint as inbound payloads, enforced at capture time so display-layer truncation isn't the only defense
|
|
56
|
+
|
|
57
|
+
**Async & state (single-file patterns)**
|
|
58
|
+
- Optimistic UI state (selection, active flag, list membership) updated before an async call and never reverted on failure — the user sees success, the server remains on the old state. Capture the previous value, await in try/catch, and reset on rejection. Optimistic placeholder IDs ('pending', 'temp_*', client-generated UUIDs) must NOT be echoed back to the server in subsequent requests — the server validates against its real ID format and rejects them as 400s. Disable controls bound to optimistic IDs until the server returns a real one, OR omit the field from outgoing payloads when the local value still matches the optimistic shape
|
|
59
|
+
- Settings whose persistence model is per-record (per-conversation, per-document, per-project) must be persisted on every mutation, not just held in local component state — otherwise refresh resets to the persisted value. Decide explicitly whether the field is per-record, per-session, or per-action — and persist accordingly
|
|
60
|
+
- Async functions invoked from sync event handlers (`onClick`, `onKeyDown`), effects, or dispatchers without rejection handling at the call site — even when a shared `request()` toasts the error, the unhandled rejection leaves UI stuck (modal doesn't close, palette doesn't navigate, dirty state doesn't clear). Wrap in try/catch, attach `.catch(...)`, or use `void p.catch(...)`; only run close/navigate/success in the resolve branch. After awaiting, check `signal.aborted` (or a `mountedRef`) before subsequent state writes — otherwise React warns about updates on unmounted trees and stale state leaks through
|
|
61
|
+
- Single shared error-state variable reused by multiple independent async flows — one flow's success path clears the other flow's displayed error. Split errors by domain or only overwrite the specific error you own
|
|
62
|
+
- Loading flag covers only the primary fetch — a slow or failed secondary fetch renders a blank page with no indicator. Include every render-gating load in the loading state or provide explicit empty/error states
|
|
63
|
+
- Streaming UIs that clear the streaming buffer when a terminal `error` event arrives discard deltas the user already saw. Preserve accumulated content as a partial result with an error indicator so users don't lose what was visible
|
|
64
|
+
- Raw `fetch()` failures (TypeError "Failed to fetch", DNS errors, ECONNREFUSED) at API client boundaries must be translated to a consistent user-friendly message matching the project's established transport-error utility — preserve `AbortError` so callers can still distinguish cancellation from failure
|
|
65
|
+
- Child process `spawn()` calls without an `error` event handler — when the binary is missing or unexecutable, Node emits an `'error'` event and never emits `'close'`. Promise wrappers that only listen for `'close'` hang forever; bare spawn calls with no listener crash the parent process via uncaught exception. Always register `proc.on('error', ...)` alongside `'close'`. SIGKILL escalation timers must check liveness via `proc.exitCode == null` (or a `closed` flag set in the close handler) — `proc.killed` becomes `true` immediately after `kill('SIGTERM')` is called, so guards using `if (!proc.killed)` never fire and hung children survive indefinitely. Single-process tracking ("BUSY guard", `activeProcess` global) must hold the reference until the `'close'` event fires, not until `kill()` is sent — clearing the reference at SIGTERM opens a race window where a new job can start while the previous child is still alive
|
|
66
|
+
- `spawn`/`exec` env objects: setting a key to `undefined` may coerce to the literal string `"undefined"` instead of unsetting the variable — build the env, then `delete env.PYTHONPATH` (or set to `''` if you explicitly want it cleared). Same caveat applies to nullish/numeric values being coerced to strings inside the env map
|
|
67
|
+
- Caches that store negative/error results (`null`, "not found", probe failure) without a TTL or invalidation hook — when a user installs the missing dependency mid-runtime (ffmpeg, python venv, model file), the cache reports "still missing" until process restart. Cache only successful lookups, OR use a short TTL for negatives, OR re-probe on demand when the cached value is the negative sentinel
|
|
68
|
+
- Late-connecting clients to long-running async jobs (SSE, WebSocket subscribe-by-id) receive nothing if they connect after the terminal `complete`/`error` broadcast — the server emitted once and moved on. Persist the most-recent (or terminal) payload on the job and emit it immediately on attach, OR document that subscribers must connect before kicking off the job and update any "late connectors will get the final state" comments accordingly
|
|
69
|
+
- Server returning an empty success payload (`200` with `{ images: [] }`, `{ items: null }`, etc.) when an awaited operation succeeded but the artifact fetch failed — clients treat empty as "no work to show" and never surface the underlying error. After awaiting completion, a missing/unreadable artifact is an internal error: return non-2xx with a structured error, never an empty 200
|
|
70
|
+
|
|
71
|
+
**Streaming response handlers (server-side)**
|
|
72
|
+
- Long-lived streaming handlers (SSE, chunked HTTP) must register a client-disconnect listener (`req.on('close')`/`req.on('aborted')`), set an `aborted` flag, and check it before subsequent writes — otherwise the server keeps emitting frames and incurring `write-after-end` errors after the client navigates away
|
|
73
|
+
- After flushing streaming headers, framework error middleware (asyncHandler, exception filters) cannot send a JSON error — wrap post-handshake logic in try/finally that translates errors into a terminal `event: error` SSE frame and ends the response gracefully (`if (!res.writableEnded && !res.destroyed) res.end()`)
|
|
74
|
+
- Per-request timeouts on streaming responses must remain active for the full duration of stream consumption, not cleared on initial fetch resolution — a stalled upstream that keeps the connection open hangs the consumer indefinitely
|
|
75
|
+
- Honor write backpressure: check the boolean return of `write()` and await `'drain'` when it returns false; otherwise a slow client causes unbounded server-side buffering
|
|
76
|
+
- When attaching paired listeners for backpressure or completion (`drain` + `close`), the cleanup handler must remove ALL of them — asymmetric removal accumulates listeners across slow-client cycles
|
|
77
|
+
- Named lifecycle events on streams (`error`, `done`, `complete`) must be MUTUALLY EXCLUSIVE — after emitting `error`, do NOT also emit `done`. Otherwise clients parsing the last event treat failed runs as completed
|
|
46
78
|
|
|
47
79
|
**Error handling (single-file)**
|
|
48
80
|
- Swallowed errors (empty `.catch(() => {})`); error handlers that exit cleanly (`exit 0`, `return`) without user-visible output; handlers replacing detailed failure info with generic messages
|
|
81
|
+
- Error discrimination by string matching (`err.message.includes('not found')`, regex on error text) — localization, refactors, or wrapper rewrites silently change HTTP status / retry behavior. Use explicit error codes or typed classes
|
|
82
|
+
- Route handlers mapping any exception from a service into a single HTTP status (e.g., `catch { throw new NotFoundError() }`) — hides real server errors (file I/O, parse, write failures) as domain 404s. Map only known codes/classes; let unknown errors surface as 500
|
|
83
|
+
- Error wrappers that re-throw with only `{ status }` and drop `code`/`context`/`cause` — downstream consumers see generic `INTERNAL_ERROR` instead of the specific code. Preserve structured detail when wrapping
|
|
84
|
+
- Errors thrown from middleware/parser modules (multipart, body parsers, validators) without `err.status` set are normalized to HTTP 500 by the framework's default error handler — but they typically represent client payload issues (bad multipart, payload too large, file type rejected, missing boundary). Set `err.status = 400` (or 413 for size limits, etc.) and a stable `err.code` (`PAYLOAD_TOO_LARGE`, `INVALID_MULTIPART`, `VALIDATION_ERROR`) at the throw site, OR throw a typed `ServerError`/`ApiError`, so clients distinguish their bad input from real server failures
|
|
85
|
+
- Outbound `fetch()` / HTTP calls in setup, install, or update scripts (`scripts/*.js`, `setup.sh` invoked tools, post-install hooks) without an `AbortController` per-request timeout — a hung server (accepts connection, never responds) blocks the parent shell process indefinitely, breaking "fail-soft" guarantees that the parent script depends on. Use the same timeout helper the rest of the codebase uses for outbound HTTP, and treat timeout as a skip with a clean exit code
|
|
49
86
|
|
|
50
87
|
### Domain-Specific (check only when file type matches)
|
|
51
88
|
|
|
@@ -82,13 +119,17 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
82
119
|
|
|
83
120
|
**Shell & portability** _[subprocesses, shell scripts, CLI tools]_
|
|
84
121
|
- `set -e` aborting on non-critical failures; broken pipes on non-critical writes — use `|| true`
|
|
85
|
-
- Interactive prompts in non-interactive contexts (CI, cron) — guard with TTY detection
|
|
122
|
+
- Interactive prompts in non-interactive contexts (CI, cron) — guard with TTY detection (`[ -t 0 ]`). Also handle EOF (Ctrl-D, closed stdin) explicitly under `set -e` — a `read` returning non-zero on EOF aborts the script. Use `read ... || true` and check the return; default to a safe value. Validate the full set of expected answers (e.g., `y`/`yes`/`n`/`no` case-insensitive) — treating any non-default input as consent surprises users
|
|
86
123
|
- Detached processes with piped stdio — SIGPIPE on parent exit. Use `'ignore'`
|
|
87
124
|
- Subprocess output buffered without size limits — unbounded memory growth
|
|
88
125
|
- Platform-specific: hardcoded shell interpreters; `path.join()` backslashes breaking ESM imports — use `pathToFileURL()`
|
|
89
126
|
- Naive whitespace splitting of command strings breaks quoted arguments — use proper argv parser
|
|
90
127
|
- Subprocess output parsed from single stream (stdout or stderr) to detect conditions — check both streams and exit code
|
|
128
|
+
- Readiness/health probes that rely solely on subprocess exit code without inspecting output — many CLIs (`psql`, `curl`, `kubectl`) exit 0 for empty results, missing schema, or auth-only handshake. Capture stdout and verify it contains the expected marker. For tools that read user-level config (`.psqlrc`, `~/.curlrc`), pass flags that ignore those files (`-X`, `--no-rcfile`) so the probe behaves the same in every environment
|
|
129
|
+
- Setup/provisioning scripts invoked from hot paths (`npm start`, dev script, container entrypoint) that mutate credentials, privileges, or installed-package state (`ALTER USER`, password resets, brew installs) on every invocation — gate the heavy work behind a cheap readiness check, OR refactor each step to be idempotent and detect already-applied state
|
|
91
130
|
- Shell expansions suppressed by quoting — single quotes prevent all expansion
|
|
131
|
+
- Arguments passed via process argv have OS-imposed length limits (notoriously low on Windows, ~32KB). For variable-length payloads (prompts, JSON blobs, file contents), pipe via stdin instead of constructing a long argv. If argv must be used, enforce a strict cap and fail with a clear message before spawning
|
|
132
|
+
- PowerShell `$LASTEXITCODE` propagates from any external call and is read by the script's final exit. A step claiming to be "fail-soft" (e.g., a non-essential post-install hook) that runs an external command without explicitly resetting `$LASTEXITCODE = 0` (or wrapping in try/catch with `$global:LASTEXITCODE = 0`) leaks a non-zero exit code from the soft step into the parent script's overall exit status — breaking the fail-soft contract that callers depend on
|
|
92
133
|
|
|
93
134
|
**Search & navigation** _[search, deep-linking]_
|
|
94
135
|
- Search results linking to generic list pages instead of deep-linking to specific record
|
|
@@ -98,17 +139,27 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
98
139
|
- Destructive actions without confirmation step
|
|
99
140
|
|
|
100
141
|
**Accessibility** _[UI components, interactive elements]_
|
|
101
|
-
- Interactive elements missing accessible names, roles, or ARIA states — including labels removed or replaced with non-descriptive placeholders in conditional/compact rendering modes
|
|
142
|
+
- Interactive elements missing accessible names, roles, or ARIA states — including labels removed or replaced with non-descriptive placeholders in conditional/compact rendering modes. ARIA attributes should match established patterns used elsewhere for the same widget type (disclosure, menu, dialog)
|
|
143
|
+
- ARIA roles applied without the keyboard interactions they imply — `role="menu"`/`menuitem*` expects roving focus, arrow-key navigation, Escape scoped to the menu, and focus management; `role="listbox"` expects Home/End/typeahead; `role="dialog"` expects focus trap + return focus. Either implement the full interaction pattern or drop to a simpler one (native `<button>` + disclosure)
|
|
144
|
+
- Nested inputs handling `Escape`/`Enter`/`ArrowUp`/`ArrowDown` inside a modal/form that also handles the key at the ancestor — the event bubbles and the ancestor fires too (closes modal, submits form). Call `e.stopPropagation()` (and usually `preventDefault()`) in the inner handler
|
|
102
145
|
- Custom toggles from non-semantic elements instead of native inputs
|
|
103
146
|
- Overlay layers with `pointer-events-auto` intercepting clicks beneath; `pointer-events-none` on parent killing child hover handlers
|
|
147
|
+
- HTML `<button>` elements without an explicit `type="button"` attribute default to `type="submit"`. When the component is rendered (or could be rendered) inside a `<form>` ancestor, clicks trigger unintended form submission. Set `type="button"` on every non-submit button (close, cancel, expand, menu trigger) — the cost is one attribute and the bug is silent until the component lands inside a form
|
|
104
148
|
|
|
105
149
|
**UI performance** _[UI components with streaming, scroll, or frequent updates]_
|
|
106
150
|
- Event handlers or `useEffect` callbacks firing on every high-frequency event (streaming deltas, scroll, resize, keydown) without throttle, debounce, or `requestAnimationFrame` batching — causes jank and excessive re-renders. Batch with rAF or a time-based limiter when the handler doesn't need to run on every tick
|
|
107
151
|
|
|
152
|
+
**Wire-protocol parsers** _[SSE/NDJSON/line-delimited frame parsers, multipart, etc.]_
|
|
153
|
+
- Wire-protocol parsers must (a) handle the spec's full set of separators (e.g., both `\n\n` and `\r\n\r\n` for SSE; multiple `data:` lines joined with `\n`); (b) flush remaining buffered content on EOF — otherwise the last frame is dropped when upstream closes mid-frame; (c) wrap per-frame deserialization (`JSON.parse`) so a single malformed frame doesn't terminate the entire stream
|
|
154
|
+
- Stateful parsers (multipart, MIME, framed protocols) must verify they reached the terminal state on `req.on('end')` / EOF — calling `finish()` while still in `STATE_HEADERS`/`STATE_BODY` accepts truncated input as success, silently corrupting partially-written uploads or persisting half-written state. Track the terminal-state transition (e.g., `STATE_DONE` after the closing `--boundary--`) and return a 400 error otherwise (and clean up any partial files written)
|
|
155
|
+
- Per-part state in stateful parsers must be reset at part boundaries — fields like `currentFileMimetype`, accumulated headers, decoder state, and offsets that aren't cleared at the start of each new part will leak the previous part's value (e.g., a file part with no `Content-Type` inherits the previous part's mimetype). Reset per-part state at the top of the part-start handler
|
|
156
|
+
- Refactoring a streaming parser to "buffer-then-process" (calling `readAllBytes()` / `Buffer.concat(chunks)` / `await req.text()` before parsing) defeats the streaming contract and re-introduces an OOM/DoS vector for large uploads — verify the new implementation still respects each caller's `maxSize`/body cap WHILE reading (stop collecting once bytes exceed the cap), or restore true streaming. Watch for header comments still claiming "streams" / "never buffers entire body in memory" after such refactors — they become a documentation lie
|
|
157
|
+
- Library wrappers advertising a multer/express-style contract `(req, file, cb)` must pass the real `req` (not `null`) through to filters/hooks; treating the `cb` as synchronous breaks any caller that supplied an async filter (callback fires later, but the wrapper already read pre-callback state). Either enforce synchronous filters with a clear error and document, or `await` a Promise-wrapped callback before continuing
|
|
158
|
+
|
|
108
159
|
### Always Check — Quality & Conventions
|
|
109
160
|
|
|
110
161
|
**Intent vs implementation (single-file)**
|
|
111
|
-
- Labels, comments, status messages describing behavior the code doesn't implement
|
|
162
|
+
- Labels, comments, status messages describing behavior the code doesn't implement. Also covers factual doc drift: file paths/extensions (`foo.js` referenced when the file is `foo.jsx`), item counts ("13 widgets" when there are 15), default entity names ("Default" vs actual "Everything"), and route/response-shape comments that don't match what the handler returns. Verify every factual claim in a comment or JSDoc against the code it references
|
|
112
163
|
- Inline code examples or command templates that aren't syntactically valid
|
|
113
164
|
- Sequential numbering with gaps or jumps after edits
|
|
114
165
|
- Template/workflow variables referenced but never assigned — trace each placeholder to a definition
|
|
@@ -119,6 +170,10 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
119
170
|
- Lookups checking only one scope when multiple exist (local branches but not remote)
|
|
120
171
|
- Tracking/checkpoint files defaulting to empty on parse failure — fail-open guards
|
|
121
172
|
- Registering references to resources without verifying resource exists
|
|
173
|
+
- Composed instructions, prompts, system messages, or rule sets that vary by mode/role/context — unconditional clauses can contradict mode-specific directives (e.g., "always cite sources inline" combined with a `draft` mode that asks for "no preamble, no commentary"). Build the composition conditionally — include each block only for modes that want it — or define an explicit precedence so contradictions are predictable
|
|
174
|
+
|
|
175
|
+
**UX integrity (single-component)**
|
|
176
|
+
- Unsaved changes / dirty state silently discarded when the user switches context in a multi-record editor or closes a sheet — data loss. Dirty-check on switch (inline confirm), auto-save drafts, or disable the switch control while dirty. `beforeunload` does not cover in-app context switches
|
|
122
177
|
|
|
123
178
|
**AI-generated code quality**
|
|
124
179
|
- New abstractions, wrapper functions, helper files serving only one call site — inline instead
|
|
@@ -126,6 +181,7 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
126
181
|
- Commit messages claiming a fix while the bug remains
|
|
127
182
|
- Placeholder comments (`// TODO`, `// FIXME`) or stubs presented as complete
|
|
128
183
|
- Unnecessary defensive code for scenarios that provably cannot occur
|
|
184
|
+
- Cleanup callbacks (useEffect return, finalizer, dispose, signal handler) containing only comments are misleading — implement the cleanup or remove the callback entirely
|
|
129
185
|
|
|
130
186
|
**Configuration & hardcoding**
|
|
131
187
|
- Hardcoded values when config/env var exists; dead config fields; unused function parameters
|
|
@@ -149,6 +205,7 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
149
205
|
- Missing tests for trust-boundary enforcement
|
|
150
206
|
- Tests exercising code paths the integration layer doesn't expose — pass against mocks but untriggerable in production
|
|
151
207
|
- Test mock state leaking between tests — "clear" resets invocation counts but not configured behavior; use "reset" variants
|
|
208
|
+
- Response/status assertions written as loose ranges (`status >= 400`, `status < 500`, `ok: false`) — a regression that turns a 400 validation failure into a 500 still passes. Assert the specific expected status so tests distinguish validation from server failure
|
|
152
209
|
|
|
153
210
|
**Automated pipeline discipline**
|
|
154
211
|
- Internal code review must run before creating PRs — never go straight from "tests pass" to PR
|
|
@@ -157,7 +214,7 @@ For each changed file, read the **ENTIRE file** (not just diff hunks). New code
|
|
|
157
214
|
|
|
158
215
|
**Style & conventions**
|
|
159
216
|
- Naming and patterns inconsistent with rest of codebase
|
|
160
|
-
- New content not matching existing indentation, bullet style, heading levels
|
|
217
|
+
- New content not matching existing indentation, bullet style, heading levels. Within a single structured file (changelog, README, TOML config), section headers must be unique — duplicate `## Fixed` blocks or repeated table sections are a merge artifact that splits content downstream tools expect to find under one header. Consolidate
|
|
161
218
|
- Shell instructions with destructive operations not verifying preconditions first
|
|
162
219
|
|
|
163
220
|
## Output Format
|
package/package.json
CHANGED
package/uninstall.sh
CHANGED