slash-do 1.7.0 → 1.8.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/commands/do/review.md
CHANGED
|
@@ -212,6 +212,10 @@ Check every file against this checklist. The checklist is organized into tiers
|
|
|
212
212
|
**Formatting & structural consistency**
|
|
213
213
|
- If the PR adds content to an existing file (list items, sections, config entries), verify the new content matches the file's existing indentation, bullet style, heading levels, and structure — rendering inconsistencies are the most common Copilot review finding
|
|
214
214
|
|
|
215
|
+
**Dependent operation ordering**
|
|
216
|
+
- If a handler orchestrates multiple operations (primary write + side effects like rewards, uploads, notifications), trace the dependency graph — verify side effects only execute after the primary operation confirms success. Watch for `Promise.all` grouping operations that should be sequential because one depends on the other's outcome, and for resource allocation (file uploads, external API calls) happening before a gate operation (lock acquisition, uniqueness check, validation) that may reject the request
|
|
217
|
+
- If the PR handles file uploads or binary data, verify the server validates content independently of client-supplied metadata — check MIME type via magic bytes, size via buffer length, and content validity via actual parsing rather than trusting request headers or middleware-provided fields
|
|
218
|
+
|
|
215
219
|
**Bulk vs single-item operation parity**
|
|
216
220
|
- If the PR modifies a single-item CRUD operation (create, update, delete) to handle new fields or apply new logic, trace the corresponding bulk/batch operation for the same entity — it often has its own independent implementation that won't pick up the change. Verify both paths handle the same fields, apply the same validation, and preserve the same secondary data
|
|
217
221
|
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
**Trust boundaries & data exposure**
|
|
33
33
|
- API responses returning full objects with sensitive fields — destructure and omit across ALL response paths (GET, PUT, POST, error, socket); comments/docs claiming data isn't exposed while the code path does expose it
|
|
34
|
-
- Server trusting client-provided computed/derived values (scores, totals, correctness flags) when the server can recompute them — strip and recompute server-side;
|
|
34
|
+
- Server trusting client-provided computed/derived values (scores, totals, correctness flags, file metadata like MIME type and size) when the server can recompute or verify them — strip and recompute server-side; for file uploads, validate content type via magic bytes and size via actual buffer length rather than trusting client-supplied headers
|
|
35
35
|
- New endpoints mounted under restricted paths (admin, internal) missing authorization verification — compare with sibling endpoints in the same route group to ensure the same access gate (role check, scope validation) is applied consistently
|
|
36
36
|
- User-controlled objects merged via `Object.assign`/spread without sanitizing keys — `__proto__`, `constructor`, and `prototype` keys enable prototype pollution. Use `Object.create(null)` for the target, whitelist allowed keys, and use `hasOwnProperty` (not `in`) to check membership. Also verify the merge can't override reserved/internal fields the system depends on
|
|
37
37
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
- State updates guarded by truthiness of the new value (`if (arr?.length)`) — prevents clearing state when the source legitimately returns empty. Distinguish "no response" from "empty response"
|
|
46
46
|
- Periodic/scheduled operations with skip conditions (gates, precondition checks, "nothing to do" early exits) that don't advance timing state (lastRun, nextFireTime) on skip — null or stale lastRun causes immediate re-trigger in a tight loop. Record the skip as an execution or compute the next fire time from now, not from the missing lastRun
|
|
47
47
|
- Mutation/trigger functions that return or propagate stale pre-mutation state — if a function activates, updates, or resets an entity, the returned value and any dependent scheduling/evaluation state (backoff timers, "last run" timestamps, status flags) must reflect the post-mutation state, not a snapshot read before the mutation
|
|
48
|
-
- Fire-and-forget or async writes where the in-memory object is not updated (response returns stale data) or is updated unconditionally regardless of write success (response claims state that was never persisted) — update in-memory state conditionally on write outcome, or document the tradeoff explicitly. Also applies to responses and business-logic decisions (threshold triggers, status transitions) derived from pre-transaction reads — concurrent writers all read the same stale value, so thresholds may be crossed without triggering the transition. Compute from post-write state or use conditional expressions that evaluate the stored value. For monotonic counters (sequence numbers, cursors) that must stay in lockstep with append-only storage, advancing before the write risks the counter running ahead on failure; not advancing after a partial write risks reuse — reserve the range before writing and commit only on success
|
|
48
|
+
- Fire-and-forget or async writes where the in-memory object is not updated (response returns stale data) or is updated unconditionally regardless of write success (response claims state that was never persisted) — update in-memory state conditionally on write outcome, or document the tradeoff explicitly. Also applies to responses and business-logic decisions (threshold triggers, status transitions) derived from pre-transaction reads — concurrent writers all read the same stale value, so thresholds may be crossed without triggering the transition. Compute from post-write state or use conditional expressions that evaluate the stored value. For monotonic counters (sequence numbers, cursors) that must stay in lockstep with append-only storage, advancing before the write risks the counter running ahead on failure; not advancing after a partial write risks reuse — reserve the range before writing and commit only on success. Also check for dependent side effects (rewards, notifications, secondary uploads, resource allocation) executing in parallel with or before the primary write they depend on — if the primary write fails or is rejected (lock contention, dedup, validation), the side effects are irrecoverable (orphaned uploads, unearned rewards, phantom notifications). Gate side effects on confirmed primary write success
|
|
49
49
|
- Error/early-exit paths that return status metadata (pagination flags, truncation indicators, hasMore, completion markers) or emit events (WebSocket, SSE, pub/sub) with default/initial values instead of reflecting actual accumulated state — downstream consumers make incorrect decisions (e.g., treating a failed sync as successful because the completion event was emitted unconditionally). Set metadata flags and event payloads based on actual outcome, not just the final request's exit path. Also check paired lifecycle events (started/completed/failed): if a function emits a "started" event, every exit path — including early returns and no-op branches — must emit the corresponding "completed" or "failed" event, or clients waiting for completion will hang or show stale state
|
|
50
50
|
- Missing `await` on async operations in error/cleanup paths — fire-and-forget cleanup (e.g., aborting a failed operation, rolling back partial state) that must complete before the function returns or the caller proceeds
|
|
51
51
|
- `Promise.all` without error handling — partial load with unhandled rejection. Wrap with fallback/error state. For `Promise.allSettled`, verify rejected entries are logged with rejection reasons before mapping to fallback values — silently substituting defaults makes partial failures invisible in production
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
**Error handling** _[applies when: code has try/catch, .catch, error responses, or external calls]_
|
|
56
56
|
- Service functions throwing generic `Error` for client-caused conditions — bubbles as 500 instead of 400/404. Use typed error classes with explicit status codes; ensure consistent error responses across similar endpoints — when multiple endpoints make the same access-control decision (e.g., "resource exists but caller lacks access"), they must return the same HTTP status (typically 404 to avoid leaking existence). Include expected concurrency/conditional failures (transaction cancellations, optimistic lock conflicts) — catch and translate to 409/retry rather than letting them surface as 500
|
|
57
57
|
- Swallowed errors (empty `.catch(() => {})`), handlers that replace detailed failure info with generic messages, and error/catch handlers that exit cleanly (`exit 0`, `return`) without any user-visible output — surface a notification, propagate original context, and make failures look like failures. Includes external service wrappers that return `null`/empty for all non-success responses — collapsing configuration errors (missing API key), auth failures (403), rate limits (429), and server errors (5xx) into a single "not found" return masks outages and misconfiguration as normal "no match" results. Distinguish retriable from non-retriable failures and surface infrastructure errors loudly
|
|
58
|
-
- Caller/callee disagreement on success/decision semantics — a function that resolves with `{ success: false }` while callers use `.catch()` for error handling means failures are treated as successes. Verify that the contract between producer and consumer is consistent: if callers branch on rejection, the function must reject on failure; if callers branch on a status field, the function must never reject. This extends beyond success/failure to any evaluation result — if a gate/check function returns `{ shouldRun: false }` on errors while the runtime treats errors as fail-open (run anyway), the API surface and runtime disagree on skip semantics. Also check EventEmitter async handlers — `async` callbacks on `'close'`/`'error'` events create unhandled rejections because EventEmitter doesn't await handler promises; wrap in try/catch
|
|
58
|
+
- Caller/callee disagreement on success/decision semantics — a function that resolves with `{ success: false }` while callers use `.catch()` for error handling means failures are treated as successes. Verify that the contract between producer and consumer is consistent: if callers branch on rejection, the function must reject on failure; if callers branch on a status field, the function must never reject. This extends beyond success/failure to any evaluation result — if a gate/check function returns `{ shouldRun: false }` on errors while the runtime treats errors as fail-open (run anyway), the API surface and runtime disagree on skip semantics. Also covers argument shape contracts — passing a wrapped object (`{ items: [...] }`) when the callee expects a bare array (or vice versa), or supplying arguments at the wrong parameter position, causes silent no-ops or partial processing; verify argument shapes match by reading the callee's signature, not assuming from naming conventions. Also check EventEmitter async handlers — `async` callbacks on `'close'`/`'error'` events create unhandled rejections because EventEmitter doesn't await handler promises; wrap in try/catch
|
|
59
59
|
- Destructive operations in retry/cleanup paths assumed to succeed without their own error handling — if cleanup fails, retry logic crashes instead of reporting the intended failure
|
|
60
60
|
- External service calls without configurable timeouts — a hung downstream service blocks the caller indefinitely
|
|
61
61
|
- Missing fallback behavior when downstream services are unavailable (see also: retry without backoff in "Sync & replication")
|
package/package.json
CHANGED