@clipboard-health/ai-rules 2.32.0 → 2.33.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/package.json +1 -1
- package/skills/cb-babysit/SKILL.md +4 -4
- package/skills/cb-babysit/scripts/_sentinel.sh +1 -1
- package/skills/cb-review/SKILL.md +238 -0
- package/skills/{simple-review → cb-review}/references/cross-repo-evidence.md +14 -1
- package/skills/cb-review/references/multi-agent.md +76 -0
- package/skills/{simple-review → cb-review}/references/posting-pr-review.md +5 -5
- package/skills/cb-review/references/review-rubric.md +186 -0
- package/skills/cb-ship/references/pr-template.md +1 -1
- package/skills/in-depth-review/SKILL.md +0 -491
- package/skills/in-depth-review/references/cross-repo-evidence.md +0 -37
- package/skills/in-depth-review/references/posting-pr-review.md +0 -105
- package/skills/simple-review/SKILL.md +0 -474
package/package.json
CHANGED
|
@@ -19,9 +19,9 @@ Resolve bundled "./scripts" paths relative to SKILL.md.
|
|
|
19
19
|
|
|
20
20
|
The skill uses two sentinels with visible footer lines.
|
|
21
21
|
|
|
22
|
-
**Addressed sentinel**: `<sub>🤖 <code>cb-babysit:addressed v1 core@3.
|
|
22
|
+
**Addressed sentinel**: `<sub>🤖 <code>cb-babysit:addressed v1 core@3.14.0</code></sub>`. Appended on its own line at the end of every reply the skill posts so re-runs know which threads and review-body comments are already handled. Dedupe also recognizes the legacy `babysit-pr:addressed v1` prefix from before this skill was renamed.
|
|
23
23
|
|
|
24
|
-
**Follow-up sentinel**: `<sub>🤖 <code>cb-babysit:followup v1 core@3.
|
|
24
|
+
**Follow-up sentinel**: `<sub>🤖 <code>cb-babysit:followup v1 core@3.14.0</code></sub>`. Attached to replies that defer an out-of-scope comment as a tracked follow-up. The sentinel is additive: the post-reply scripts still append the `addressed` sentinel at the end.
|
|
25
25
|
|
|
26
26
|
**Sentinel recency rules.** The script emits a per-thread `activityState` with three values. Step 6a owns the handling rules for each state.
|
|
27
27
|
|
|
@@ -247,7 +247,7 @@ Body templates (the script appends the `addressed` sentinel if missing):
|
|
|
247
247
|
- **Agree**: `Addressed in <commit-url>. <one-line what-changed>.`
|
|
248
248
|
- **Disagree**: `Leaving current behavior. <reasoning>.`
|
|
249
249
|
- **Already fixed**: `Already handled by <commit-url-or-file:line>. <brief pointer>.`
|
|
250
|
-
- **Defer**: `Out of scope for this PR; this looks like follow-up work rather than something introduced or required by this change. <one-line rationale or pointer if useful>.\n\n<sub>🤖 <code>cb-babysit:followup v1 core@3.
|
|
250
|
+
- **Defer**: `Out of scope for this PR; this looks like follow-up work rather than something introduced or required by this change. <one-line rationale or pointer if useful>.\n\n<sub>🤖 <code>cb-babysit:followup v1 core@3.14.0</code></sub>`
|
|
251
251
|
|
|
252
252
|
For Defer replies, include the follow-up sentinel on its own line as shown. The script will append the `addressed` sentinel after it on its own line, so the final body ends with the follow-up sentinel followed by a blank line followed by the `addressed` sentinel. `grep cb-babysit:followup` finds the deferral and `grep cb-babysit:addressed` still marks the thread handled for dedupe.
|
|
253
253
|
|
|
@@ -263,7 +263,7 @@ The PR-level summary should:
|
|
|
263
263
|
|
|
264
264
|
- Group by source. Use `## Review-body findings` for step-7 work and `## Conversation-tab comments` for step-6b work. Omit a section if its list is empty.
|
|
265
265
|
- Inside each section, group verdicts under **Agree / Disagree / Already fixed / Deferred (out of scope)** subheadings. Omit a subheading if its list is empty.
|
|
266
|
-
- Under **Deferred (out of scope)**, list each deferred item as a bullet, followed on its own line by `<sub>🤖 <code>cb-babysit:followup v1 core@3.
|
|
266
|
+
- Under **Deferred (out of scope)**, list each deferred item as a bullet, followed on its own line by `<sub>🤖 <code>cb-babysit:followup v1 core@3.14.0</code></sub>` so grep catches them individually.
|
|
267
267
|
- Include the commit URL for fixes.
|
|
268
268
|
- End with a fenced fingerprint block listing every current fingerprint (addressed and deferred) one per line. Include both `reviewBodyComments[].fingerprint` (whole-body, one per automated review) and `activeIssueComments[].fingerprint` (per Conversation-tab comment). Future runs dedupe by matching these against `priorBabysitSentinels`.
|
|
269
269
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
SENTINEL_PREFIX='cb-babysit:addressed v1 '
|
|
12
12
|
LEGACY_SENTINEL_PREFIX='babysit-pr:addressed v1 '
|
|
13
|
-
SENTINEL='<sub>🤖 <code>cb-babysit:addressed v1 core@3.
|
|
13
|
+
SENTINEL='<sub>🤖 <code>cb-babysit:addressed v1 core@3.14.0</code></sub>'
|
|
14
14
|
|
|
15
15
|
# Bot author allowlist (JSON array literal). Used by unresolvedPrComments.sh
|
|
16
16
|
# as a fallback when GraphQL's `author.__typename == "Bot"` misses a GitHub
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cb-review
|
|
3
|
+
description: Code review of the current branch or a PR — single-pass by default, parallel reviewer agents that debate at high effort, plus a spec-compliance lens when the originating ticket or PRD is available; findings posted as anchored PR comments. Use when the user asks to review a diff, branch, or PR, or runs /cb-review [pr-number-or-url] [--effort low|high].
|
|
4
|
+
argument-hint: "[pr-number-or-url] [--effort low|high]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CB Review
|
|
8
|
+
|
|
9
|
+
Review a diff against one rubric, filter to the few findings worth raising, gate with the user, optionally post as anchored PR comments. Two engines share everything except how the rubric is applied:
|
|
10
|
+
|
|
11
|
+
- **low** — single pass by you (the main agent), no subagents. Default.
|
|
12
|
+
- **high** — parallel reviewer agents, one debate round, moderator filter. For large or high-stakes diffs.
|
|
13
|
+
|
|
14
|
+
## Invocation
|
|
15
|
+
|
|
16
|
+
- `/cb-review` — review the current branch (resolves the open PR for the branch if any, otherwise diffs against the default branch).
|
|
17
|
+
- `/cb-review <pr-number-or-url>` — review that PR without checking it out; forces reviewer mode. Accepts a bare number (current repo) or full GitHub URL (identifies owner/repo).
|
|
18
|
+
- `--effort low|high` — pick the engine explicitly. Phrases also select: "quick"/"fast" → low; "deep"/"thorough"/"multi-perspective" → high.
|
|
19
|
+
|
|
20
|
+
**Effort auto-select** (no flag, no phrase): `high` when the diff exceeds 20 changed files or 600 changed lines, else `low`. Before reviewing, print one line — `Effort: <low|high> (<N> files, <M> lines; override with --effort <other>)` — so the user can interrupt.
|
|
21
|
+
|
|
22
|
+
## Scope
|
|
23
|
+
|
|
24
|
+
### Path A — PR-argument fast path (argument provided)
|
|
25
|
+
|
|
26
|
+
- Parse the argument: bare integer → `gh repo view --json nameWithOwner --jq .nameWithOwner`; URL → extract `<owner>/<repo>` and `<N>`.
|
|
27
|
+
- Do **not** check out the PR branch.
|
|
28
|
+
- Mode is **locked to reviewer**.
|
|
29
|
+
|
|
30
|
+
Gather in parallel:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gh pr view <N> --repo <owner>/<repo> --json number,url,title,body,baseRefName,author,headRefOid,files
|
|
34
|
+
gh pr diff <N> --repo <owner>/<repo>
|
|
35
|
+
gh api user --jq .login
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If `pr.author.login == viewer.login`, still proceed in reviewer mode but flag this in Summary so the user can switch to a current-branch run on their checkout if they meant to implement.
|
|
39
|
+
|
|
40
|
+
### Path B — current-branch path (no argument)
|
|
41
|
+
|
|
42
|
+
- Open PR for current branch → review that PR. Base = `baseRefName`, diff = `git diff $base...HEAD`. Context = PR title + body.
|
|
43
|
+
- No PR → diff vs default branch (`main`, fall back to `master`). Context = `git log --format='%h %s%n%n%b' $base..HEAD`.
|
|
44
|
+
- **Never** review uncommitted working-tree changes. Empty diff → stop and report.
|
|
45
|
+
|
|
46
|
+
Determine **mode**:
|
|
47
|
+
|
|
48
|
+
- PR exists and `pr.author.login == viewer.login` → **author mode**.
|
|
49
|
+
- PR exists and authors differ → **reviewer mode**.
|
|
50
|
+
- No PR → **author mode**.
|
|
51
|
+
|
|
52
|
+
**Persistence:** low effort holds everything in-context. High effort persists for subagents: diff → `/tmp/cb-review-diff.patch`, context → `/tmp/cb-review-context.md`, changed files → `/tmp/cb-review-files.txt`, metadata (PR number/url/base/author, viewer, head SHA, owner/repo, mode, `context_ref`, dispatched agent set) → `/tmp/cb-review-meta.json`.
|
|
53
|
+
|
|
54
|
+
## Freshness preflight (mandatory before reading code)
|
|
55
|
+
|
|
56
|
+
Stale local state produces false-positive findings. Before reviewing, verify the ref you'll read for context is current.
|
|
57
|
+
|
|
58
|
+
Run on the **primary repo** (the one containing the diff):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git fetch origin "$base" --quiet
|
|
62
|
+
git rev-parse --abbrev-ref HEAD
|
|
63
|
+
git status --porcelain
|
|
64
|
+
git rev-list --left-right --count "HEAD...origin/${base}"
|
|
65
|
+
git log -1 --format='%h %ci' "origin/${base}"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Decide `context_ref`:
|
|
69
|
+
|
|
70
|
+
- **Path A (reviewer):** `context_ref = origin/${base}`. The worktree may be used only when `HEAD == origin/${base}` AND clean AND fetch succeeded.
|
|
71
|
+
- **Path B (author):** `context_ref = origin/${base}`. The local feature branch IS the diff; pre-PR context comes from `origin/${base}`.
|
|
72
|
+
|
|
73
|
+
Stop and ask the user when:
|
|
74
|
+
|
|
75
|
+
1. `git fetch` failed (offline, auth).
|
|
76
|
+
2. Path B: working tree is dirty AND dirty paths overlap the diff's changed-file list or anything you'll need to read.
|
|
77
|
+
3. Path B: `HEAD` is behind `origin/${base}` (any non-zero "behind") — findings may target code the base has already changed.
|
|
78
|
+
|
|
79
|
+
Warn template (substitute verified state):
|
|
80
|
+
|
|
81
|
+
> Freshness check for `<owner>/<repo>` at `<worktree-path>`:
|
|
82
|
+
>
|
|
83
|
+
> - on branch `<HEAD-branch>`
|
|
84
|
+
> - `<N>` ahead, `<M>` behind `origin/<base>` (last: `<short-sha> <iso-date>`)
|
|
85
|
+
> - working tree: `<clean | dirty: N file(s)>`
|
|
86
|
+
>
|
|
87
|
+
> Reading from this worktree may surface findings based on stale state.
|
|
88
|
+
> Reply: `proceed` (use worktree, accept the risk), `use-origin` (read context via `git show origin/<base>:<path>` — recommended), or `stop`.
|
|
89
|
+
|
|
90
|
+
**Never** run `git checkout`, `stash`, `reset`, or other state-modifying git on the user's behalf. The skill warns and asks; the user resolves local state. `git fetch` is allowed (read-only).
|
|
91
|
+
|
|
92
|
+
On Path A, local branch state never blocks the review — all reads go through origin refs, no checkout needed. When `HEAD` differs from `origin/${base}` or the tree is dirty, fetch the PR head to a local ref:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git fetch origin "pull/<N>/head:refs/remotes/origin/pr-<N>" --quiet
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Then read PR-head content via `git show origin/pr-<N>:<path>` and base context via `git show origin/<base>:<path>`. No checkout needed.
|
|
99
|
+
|
|
100
|
+
### Reading code
|
|
101
|
+
|
|
102
|
+
When `context_ref = origin/<base>` (or `origin/pr-<N>` for PR head):
|
|
103
|
+
|
|
104
|
+
- Read via `git show "${context_ref}:<path>"` (whole files) or `git grep -n <pattern> "${context_ref}" -- <paths>` (search).
|
|
105
|
+
- Avoid reading the worktree filesystem for tracked content — it may be stale.
|
|
106
|
+
- Worktree reads are OK only for files brand-new in the diff (untracked at `context_ref`); note "verified against: worktree" in the finding.
|
|
107
|
+
|
|
108
|
+
When `context_ref = worktree (stale, user accepted risk)`:
|
|
109
|
+
|
|
110
|
+
- Worktree reads are OK but every CRITICAL/MAJOR finding is downgraded to MINOR unless evidence is internal to the diff itself. Tag each finding with "verified against: worktree-stale".
|
|
111
|
+
|
|
112
|
+
## Cross-repo evidence policy
|
|
113
|
+
|
|
114
|
+
A finding's evidence is "cross-repo" when its load-bearing claim depends on code in any repo other than the one containing the diff. **Never silently read external repos and never claim a downstream impact you have not verified** — speculating that "the FE will break" without reading the consumer is a top source of false-positive findings. Cap any cross-repo finding at **MAJOR** until verified.
|
|
115
|
+
|
|
116
|
+
Read [references/cross-repo-evidence.md](references/cross-repo-evidence.md) before raising or finalizing any cross-repo finding — it has when the policy fires, the verify-or-downgrade procedure, the access-request template, and what "verify" means for contract/schema vs API-response changes.
|
|
117
|
+
|
|
118
|
+
## Diff classification (pick which lenses apply)
|
|
119
|
+
|
|
120
|
+
Walk the changed-file list. Activate lenses that match:
|
|
121
|
+
|
|
122
|
+
- **Security** triggers on: `routes/`, `controllers/`, `middleware*/`, files matching `auth*`/`*permission*`/`*acl*`/`*token*`/`*session*`, response serializers, OpenAPI/contract definitions, new API endpoint files.
|
|
123
|
+
- **Database** triggers on: `migrations/`, `*.sql`, files matching `schema*`, Mongoose/Prisma model files (`models/`, `*.model.ts`, `*.schema.ts`), repository/DAL files, query builders.
|
|
124
|
+
- **Frontend** triggers on: `*.tsx`, `*.jsx`, `*.css`, `*.scss`, `pages/`, `components/`, `hooks/`, or anything importing from `react`, `@tanstack/react-query`, or a design-system package.
|
|
125
|
+
- **Spec** triggers when a spec source exists. Look in order: (1) issue/ticket references in the PR body or commit messages (`#123`, `Closes #45`, Linear/Jira keys) — fetch via `gh` or the tracker; (2) a path the user passed as an argument; (3) a plan/PRD file under `docs/`, `specs/`, or `.scratch/` matching the branch or feature name. Nothing found → skip the lens and note "no spec available" in Summary.
|
|
126
|
+
|
|
127
|
+
Always-on lenses: **Engineering**, **Minimalism**, **Conventions**, **AntiSlop**.
|
|
128
|
+
|
|
129
|
+
## Review
|
|
130
|
+
|
|
131
|
+
The rubric — severity ladder, `failure_mode` contract, do-not-raise list, NIT gate, caps, `suggested_fix` schema, and every lens checklist — lives in [references/review-rubric.md](references/review-rubric.md). It is binding for both engines.
|
|
132
|
+
|
|
133
|
+
### Low effort
|
|
134
|
+
|
|
135
|
+
Read the full rubric, then walk the diff once, applying the active lenses yourself. You're looking for _the smallest number of high-signal findings_, not exhaustive coverage. **No subagents** — if a finding needs deeper independent investigation than you can do confidently in-line, surface it as a flagged finding rather than guess.
|
|
136
|
+
|
|
137
|
+
### High effort
|
|
138
|
+
|
|
139
|
+
Follow [references/multi-agent.md](references/multi-agent.md): dispatch one reviewer agent per active lens in parallel (each reads only its own rubric section), run one debate round, then moderator-filter. Return here for Synthesize.
|
|
140
|
+
|
|
141
|
+
## Filter (before synthesis)
|
|
142
|
+
|
|
143
|
+
Low effort: apply to your own candidates. High effort: the moderator filter in multi-agent.md extends this list — apply that version.
|
|
144
|
+
|
|
145
|
+
1. **Drop findings with empty or hypothetical `failure_mode`.** "A future caller might…", "in case someone…" → drop.
|
|
146
|
+
2. **Self-audit for slop.** For every finding, ask: does it match a slop pattern (asks-for-defensive-guard on an already-narrowed value — but only if an _enforced_ check narrows it, not merely a declared type or cast; hypothetical future caller; restating-obvious comment request; abstract refactor with no concrete cost-of-keeping; observability without named failure mode; test for trivially-verifiable code; defends against a state the product cannot produce)? If yes and you can't write a concrete, product-specific cost in one sentence — drop it.
|
|
147
|
+
3. **Cross-repo audit.** Does the failure_mode reference a downstream actor or a contract/schema/public-artifact boundary? If you didn't actually read the external code, route through the cross-repo evidence policy (verify, ask for access, or downgrade to a labeled "speculative" MINOR).
|
|
148
|
+
4. **Drop do-not-raise items** that slipped through.
|
|
149
|
+
5. **Apply the NIT gate.** NITs that don't meet it → drop. Kept NITs stay internal, hidden by default.
|
|
150
|
+
6. **Merge near-duplicates** under one finding (note which lens(es) surfaced it).
|
|
151
|
+
7. **Apply hard caps.** 6 actionable, 8 NITs retained; rest summarized as "N additional items omitted; ask for the full list."
|
|
152
|
+
|
|
153
|
+
Track dropped items in Withdrawn (one-liner each) so the user can see what was filtered.
|
|
154
|
+
|
|
155
|
+
## Synthesize
|
|
156
|
+
|
|
157
|
+
### Summary
|
|
158
|
+
|
|
159
|
+
One short paragraph: what the change does and your overall recommendation (ship / ship with changes / do not ship). When the Spec lens ran, add one line with its verdict — requirements met, missing, or diverging — kept separate so a standards-clean diff can't mask a spec miss (and vice versa). When it was skipped, add "no spec available". Do not state the mode, engine, lenses applied, or convention sources consulted — that metadata is noise.
|
|
160
|
+
|
|
161
|
+
### Actionable
|
|
162
|
+
|
|
163
|
+
Up to 6 items. Format each:
|
|
164
|
+
|
|
165
|
+
- **[SEVERITY] Title** — `file:lines`
|
|
166
|
+
- **Point:** one sentence. Spec findings quote the spec line they're grounded in.
|
|
167
|
+
- **Why it matters:** the `failure_mode`.
|
|
168
|
+
- **Suggested fix (before → after):** two stacked fenced code blocks — first `// Before` (current code), then `// After` (replacement). Same language tag for both. For a pure deletion, show only `Before` and write "_Delete these lines._" For a pure addition, show only `After` prefixed with `// Add after line <N>`. Omit when structural; explain in prose.
|
|
169
|
+
- **Lens(es):** which lens(es) surfaced this (high effort: which agents raised/agreed, by name).
|
|
170
|
+
|
|
171
|
+
Order by severity (CRITICAL → MAJOR → MINOR), then by file. Number items 1..N — these ids drive the gates.
|
|
172
|
+
|
|
173
|
+
### Disagreements (high effort only)
|
|
174
|
+
|
|
175
|
+
Items where agents substantively disagreed and did not converge. One sentence per side, attributed by agent name, then the moderator's call with a one-line reason.
|
|
176
|
+
|
|
177
|
+
### Nits
|
|
178
|
+
|
|
179
|
+
Do **not** print by default. Print only: _"N nit(s) available (M from convention audit)."_ Surface a "show the N NIT(s) first" option in gate 1. If N is 0, omit this section.
|
|
180
|
+
|
|
181
|
+
### Withdrawn
|
|
182
|
+
|
|
183
|
+
Terse one-liners of items dropped by the filter. Transparency only.
|
|
184
|
+
|
|
185
|
+
## User gate 1 — select items
|
|
186
|
+
|
|
187
|
+
Ask via the host's structured picker (`AskUserQuestion` in Claude Code; hosts without one: a numbered text prompt with the same options). Render each actionable finding as an option, plus a trailing "Show the N NIT(s) first" option when N > 0. Set `multiSelect: true`. Do not proceed until the user answers.
|
|
188
|
+
|
|
189
|
+
Option-label format: `<id> - <short title> (<SEVERITY>)`. The `description` is the one-sentence failure_mode, not the fix.
|
|
190
|
+
|
|
191
|
+
- **Zero findings selected** → end with a one-line "no findings selected — nothing to do".
|
|
192
|
+
- **Only "Show the N NIT(s) first"** → print the NITs (one-liners with `file:lines` and `[CONVENTION]`/`[SPEC]` tags), then re-ask this gate.
|
|
193
|
+
- **Findings selected** → proceed to gate 2 (print NITs first if the toggle was also checked).
|
|
194
|
+
|
|
195
|
+
## Branch on mode
|
|
196
|
+
|
|
197
|
+
### Author mode
|
|
198
|
+
|
|
199
|
+
**Plan.** For the selected ids only, produce an ordered implementation plan: steps, files touched per step, tests to add/update, verification commands. Do **not** edit files yet.
|
|
200
|
+
|
|
201
|
+
**User gate 2 (author)** — structured picker, `multiSelect: false`, options:
|
|
202
|
+
|
|
203
|
+
- `Implement locally` — apply the plan against the checkout.
|
|
204
|
+
- `Post as review` — post anchored comments on your own PR, then stop.
|
|
205
|
+
- `Both` — post the review first, then execute the plan.
|
|
206
|
+
- `Edit the plan` — revise, re-ask this gate.
|
|
207
|
+
- `Cancel` — stop.
|
|
208
|
+
|
|
209
|
+
**Execute.** Track each step with the host's task tracker. Apply the plan, run verification, report results. If a step surfaces a new substantive issue not in the selected items, stop and ask before expanding scope.
|
|
210
|
+
|
|
211
|
+
### Reviewer mode
|
|
212
|
+
|
|
213
|
+
Skip the plan step — you are not implementing someone else's code.
|
|
214
|
+
|
|
215
|
+
**User gate 2 (reviewer)** — structured picker, `multiSelect: false`, options:
|
|
216
|
+
|
|
217
|
+
- `Post` — submit a single COMMENT review with the selected anchored comments.
|
|
218
|
+
- `Edit` — ask which to drop or refine, then re-ask.
|
|
219
|
+
- `Cancel` — stop.
|
|
220
|
+
|
|
221
|
+
## Posting an anchored PR review (both modes)
|
|
222
|
+
|
|
223
|
+
When the user approves items to post, submit a **single** review via the GitHub Reviews API (`event: COMMENT` — never `APPROVE`/`REQUEST_CHANGES`), with every selected item as an inline comment anchored to its diff line. Never use loose issue comments.
|
|
224
|
+
|
|
225
|
+
Follow [references/posting-pr-review.md](references/posting-pr-review.md) exactly — it has the `gh api` call, the payload shape, the three-block review body, and the per-comment body budget and format. After posting, print the review `html_url` and a one-line summary of how many comments were posted (and fallback general-notes count if any).
|
|
226
|
+
|
|
227
|
+
## Rules
|
|
228
|
+
|
|
229
|
+
- The rubric file is binding for both engines: empty `failure_mode` → drop, NIT not meeting the gate → drop, do-not-raise items → drop, caps applied (6 actionable, 8 nits).
|
|
230
|
+
- Issue independent `gh`/`git` commands in parallel (PR view, diff, files, viewer).
|
|
231
|
+
- Freshness preflight is mandatory before reading code. Read repo context via `git show "<context_ref>:<path>"`, not the worktree filesystem, unless `context_ref = worktree (stale, user accepted risk)`.
|
|
232
|
+
- Never run state-modifying git commands on the user's behalf (`checkout`, `stash`, `reset`, `clean`, `pull` with merge). Warn and ask. `git fetch` is allowed.
|
|
233
|
+
- Cross-repo evidence: **never raise a "consumer will break" finding without reading the consumer.** Cap at MAJOR until verified; user `skip` → drop or keep as MINOR with a visible "speculative — assumes `<X>`" prefix.
|
|
234
|
+
- Conditional lenses (Security/Database/Frontend/Spec) run only when classification matches.
|
|
235
|
+
- Hide nits by default. Print only when the user selects the NIT toggle in gate 1.
|
|
236
|
+
- Reviews are always `event: COMMENT`. Never approve or request changes on the user's behalf.
|
|
237
|
+
- Both user gates are mandatory. Do not auto-apply recommendations and do not auto-post reviews.
|
|
238
|
+
- If the diff is empty, stop with a one-line message — do not invent findings.
|
|
@@ -30,6 +30,19 @@ For each cross-repo finding:
|
|
|
30
30
|
|
|
31
31
|
Severity cap on any cross-repo finding is **MAJOR** until verified. Anything verified as actual breakage can be raised to its true severity.
|
|
32
32
|
|
|
33
|
+
## High effort — subagent and moderator flow
|
|
34
|
+
|
|
35
|
+
Reviewer agents must **not** do the verification themselves. An agent whose finding is cross-repo emits it with:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
"evidence_required": {
|
|
39
|
+
"repos": ["cbh-admin-frontend", "payment-service"],
|
|
40
|
+
"what_to_verify": "concrete grep / file path / question the moderator should answer to confirm or kill the finding"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
…and caps severity at MAJOR. After Round 1 the moderator (main agent) collects every `evidence_required` block, runs **Verify or downgrade** above for each (including the user access-request when needed), then re-dispatches only the affected agents with the verified evidence (or its absence) inlined so they can finalize severity in Round 2. Findings whose evidence the user `skip`s are kept but capped per step 5.
|
|
45
|
+
|
|
33
46
|
## Access-request template
|
|
34
47
|
|
|
35
48
|
When asking the user for access to external repos, name the specific file and the specific question. Do not ask vaguely for "access to repo X" — ask for the evidence that would change your mind.
|
|
@@ -43,7 +56,7 @@ When asking the user for access to external repos, name the specific file and th
|
|
|
43
56
|
> Options:
|
|
44
57
|
>
|
|
45
58
|
> - **Local path** — give me an absolute path to a checkout (I'll run freshness preflight and read via `git show`).
|
|
46
|
-
> - **`gh:<owner>/<repo
|
|
59
|
+
> - **`gh:<owner>/<repo>[@<ref>]`** — I'll fetch via `gh api repos/<owner>/<repo>/contents/<path>?ref=<ref>`, defaulting `<ref>` to the repo's default branch.
|
|
47
60
|
> - **`skip`** — I'll either drop the finding or keep it as MINOR with a "speculative — assumes `<assumption>`" prefix; you can confirm/refute.
|
|
48
61
|
> - **`skip all`** — apply `skip` to every remaining cross-repo finding.
|
|
49
62
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# High-effort engine — parallel reviewers, one debate round, moderator filter
|
|
2
|
+
|
|
3
|
+
Read this only when effort is `high`. Referenced from `SKILL.md`. The rubric in `review-rubric.md` is binding for every agent.
|
|
4
|
+
|
|
5
|
+
## Roster
|
|
6
|
+
|
|
7
|
+
One reviewer agent per active lens. **Refer to agents by name in everything the user sees**; the letter is only a compact prefix for internal finding IDs (`B1`, `C3`, …).
|
|
8
|
+
|
|
9
|
+
| Letter | Name | Rubric section | When it runs |
|
|
10
|
+
| ------ | ----------- | -------------- | ---------------------- |
|
|
11
|
+
| B | Engineering | §Engineering | Always |
|
|
12
|
+
| C | Minimalist | §Minimalism | Always |
|
|
13
|
+
| D | Conventions | §Conventions | Always |
|
|
14
|
+
| E | Security | §Security | Classification trigger |
|
|
15
|
+
| F | Database | §Database | Classification trigger |
|
|
16
|
+
| G | Frontend | §Frontend | Classification trigger |
|
|
17
|
+
| H | AntiSlop | §AntiSlop | Always |
|
|
18
|
+
| S | Spec | §Spec | Spec source found |
|
|
19
|
+
|
|
20
|
+
Record the dispatched set in `/tmp/cb-review-meta.json` so Round 2 knows the full agent list.
|
|
21
|
+
|
|
22
|
+
## Dispatch mechanics
|
|
23
|
+
|
|
24
|
+
Use the host's parallel subagent mechanism (in Claude Code: the `Agent` tool with `subagent_type: general-purpose`, all calls in a single message so they run truly in parallel). Use fresh agents for every round. If one agent fails or returns malformed output, re-dispatch **that agent only** — do not restart the round. If the host cannot run subagents, high effort's independence guarantee is unattainable — fall back to the low-effort engine and tell the user why.
|
|
25
|
+
|
|
26
|
+
Keep large content on disk (`/tmp/cb-review-*`) so Round 2 prompts stay compact.
|
|
27
|
+
|
|
28
|
+
Every agent prompt includes:
|
|
29
|
+
|
|
30
|
+
- The file paths from Scope (`/tmp/cb-review-diff.patch`, `-context.md`, `-files.txt`).
|
|
31
|
+
- An instruction to read `review-rubric.md` **§Admission** plus the agent's own lens section (give the absolute path to this skill's `references/review-rubric.md`), and — for Spec — the spec source content or how to fetch it.
|
|
32
|
+
- The two contracts below, verbatim (substitute `<context_ref>` from the freshness preflight):
|
|
33
|
+
|
|
34
|
+
> **Context-read contract.** The verified-fresh ref for repo context is `<context_ref>`. For any file that is part of the primary repo's tracked content (i.e. _not_ a brand-new file in this PR's diff), use `git show "<context_ref>:<path>"` or `git grep -n <pattern> "<context_ref>" -- <paths>` rather than reading the worktree. If you read from the worktree because the file is new in the diff or untracked at `<context_ref>`, say so in the finding's `point`.
|
|
35
|
+
>
|
|
36
|
+
> **Cross-repo evidence contract.** If a finding's load-bearing evidence is in any repo other than this one, do NOT silently read another local checkout — those checkouts may be stale or on unrelated branches. Instead, emit the finding with an `evidence_required` field naming the repo(s) and the specific verification question, and cap your severity at MAJOR. The moderator will ask the user for verified access before Round 2 and re-dispatch you if needed.
|
|
37
|
+
|
|
38
|
+
## Round 1 — parallel independent reviews
|
|
39
|
+
|
|
40
|
+
Agents must not see each other's output in this round. Each agent emits at most 8 findings, prioritized — not exhaustive.
|
|
41
|
+
|
|
42
|
+
Required output per finding: `id` (letter-prefixed: `B1`, `C3`, `S1`, …), `severity`, `file:lines`, `title` (one sentence), `point` (one short paragraph), `failure_mode` (one sentence), optional `suggested_fix` (schema in rubric §Admission), and optional `evidence_required` (`{ repos: string[], what_to_verify: string }` — required whenever the finding depends on cross-repo state).
|
|
43
|
+
|
|
44
|
+
After Round 1, collect every `evidence_required` block and resolve it with the user per `cross-repo-evidence.md` **before** Round 2.
|
|
45
|
+
|
|
46
|
+
## Round 2 — debate (last round; hard cap: 2)
|
|
47
|
+
|
|
48
|
+
Dispatch the same agent set, in parallel. Each agent receives **all Round 1 outputs** and must:
|
|
49
|
+
|
|
50
|
+
- Re-examine their own findings and **withdraw** any that don't survive scrutiny.
|
|
51
|
+
- For each other agent's finding: mark `agree`, `disagree` (with reasoning), or `refine` (propose a tighter version).
|
|
52
|
+
- Flag items where disagreement is substantive and unlikely to resolve.
|
|
53
|
+
|
|
54
|
+
Output per item: `id`, `original_author` (agent **name**, not letter), `verdict` (keep | withdraw | agree | disagree | refine), `final_severity`, `final_title`, `final_failure_mode`, `reasoning`, `suggested_fix`, and rebuttals `[{from, stance, reasoning}]` (`from` is also a name).
|
|
55
|
+
|
|
56
|
+
**AntiSlop plays an expanded Round 2 role**: beyond defending or withdrawing its own findings, it audits every other agent's finding for the slop patterns it scans code for, and marks matches `disagree` with a one-line `slop:` label in `reasoning`:
|
|
57
|
+
|
|
58
|
+
- "Add a null check / try-catch / validation" on a value already typed, validated upstream, or guaranteed by a preceding call → `slop: asks for defensive guard on already-narrowed value`.
|
|
59
|
+
- "What if a future caller / someone later…" with no current realistic input → `slop: hypothetical future caller — no current path`.
|
|
60
|
+
- Comment/JSDoc requests where name + signature already convey intent → `slop: restating-the-obvious comment request`.
|
|
61
|
+
- Refactor suggestions whose failure_mode is shape-of-the-code → `slop: refactor with no concrete cost-of-keeping`.
|
|
62
|
+
- "Add a log/metric" without the specific failure it would debug → `slop: observability without named failure mode`.
|
|
63
|
+
- Test demands on type-evident or already-exercised code → `slop: test for trivially-verifiable code`.
|
|
64
|
+
- Worries about states the product cannot produce → `slop: defends against a state the product cannot produce`.
|
|
65
|
+
|
|
66
|
+
When an agent sees a slop tag on its own finding, it either rebuts with a concrete, product-specific `final_failure_mode` or withdraws — not both stand on the original framing. Even when AntiSlop's own Round 1 findings are sparse because the diff is clean, it must not under-spend on this audit — stopping slop _suggestions_ from polluting the review is often the bigger lever.
|
|
67
|
+
|
|
68
|
+
Residual disagreement goes to the Disagreements section of the synthesis.
|
|
69
|
+
|
|
70
|
+
## Moderator filter (main agent — after Round 2, before synthesis)
|
|
71
|
+
|
|
72
|
+
Apply the shared Filter list from `SKILL.md`, with these additions in order:
|
|
73
|
+
|
|
74
|
+
1. **Apply AntiSlop slop tags.** For every finding AntiSlop tagged `slop:`, check the original author's Round 2 rebuttal. No concrete, product-specific `final_failure_mode` addressing the specific tag → **drop the finding**. AntiSlop's audit is not a unilateral veto, but the burden of proof shifts to the author once tagged. Record drops in Withdrawn with the slop label (e.g. `B4 — dropped: AntiSlop tagged "slop: asks for defensive guard on already-narrowed value", Engineering did not rebut`).
|
|
75
|
+
2. **Merge near-duplicates across agents** into one item, preserving all attributions in `Raised by:`.
|
|
76
|
+
3. Renumber the survivors 1..N for the user-facing Actionable list and gates; keep agent names in `Raised by` / `agreed by`.
|
|
@@ -13,8 +13,8 @@ Read this once the user approves items to post. Referenced from `SKILL.md`.
|
|
|
13
13
|
Post via the GitHub Reviews API as a **single** review with all selected actionable items as inline review comments anchored to specific diff lines. **Never** loose issue comments.
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
# /tmp/
|
|
17
|
-
gh api -X POST "repos/<owner>/<repo>/pulls/<N>/reviews" --input /tmp/
|
|
16
|
+
# /tmp/cb-review-payload.json contains: {event, commit_id, body, comments: [...]}
|
|
17
|
+
gh api -X POST "repos/<owner>/<repo>/pulls/<N>/reviews" --input /tmp/cb-review-payload.json
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Payload shape:
|
|
@@ -54,9 +54,9 @@ Three blocks, in order:
|
|
|
54
54
|
|
|
55
55
|
> _These comments were generated by @\<viewer-login\> using Claude Code._
|
|
56
56
|
|
|
57
|
-
Italicized. Substitute `<viewer-login>` from `gh api user --jq .login`. Non-negotiable — collaborators must be able to tell at a glance the review is AI-assisted. Do **not** mention the
|
|
57
|
+
Italicized. Substitute `<viewer-login>` from `gh api user --jq .login`. Non-negotiable — collaborators must be able to tell at a glance the review is AI-assisted. Do **not** mention the CB Review skill by name; it's a local skill the PR author can't see or use.
|
|
58
58
|
|
|
59
|
-
**2. Summary.** Same paragraph from the in-chat synthesis: what the change does and your recommendation. Do not include mode, lenses applied, or convention sources consulted — that's noise.
|
|
59
|
+
**2. Summary.** Same paragraph from the in-chat synthesis: what the change does and your recommendation (including the Spec verdict line when that lens ran). Do not include mode, engine, lenses applied, or convention sources consulted — that's noise.
|
|
60
60
|
|
|
61
61
|
**3. "Apply all comments at once" prompt.** A fenced block with a self-contained Claude Code prompt the PR author can paste into a Claude Code session on a checkout of this branch:
|
|
62
62
|
|
|
@@ -64,7 +64,7 @@ Italicized. Substitute `<viewer-login>` from `gh api user --jq .login`. Non-nego
|
|
|
64
64
|
**Apply all comments at once** — paste this into Claude Code on a checkout of this branch:
|
|
65
65
|
|
|
66
66
|
```
|
|
67
|
-
Fetch the
|
|
67
|
+
Fetch the review by @<viewer-login> on PR <PR_URL> that contains this exact prompt. For every inline comment in that review, address the issue: when the comment includes a `suggestion` block, apply it verbatim; otherwise implement an equivalent fix that satisfies the comment's "Why it matters" rationale. After resolving each thread, post a reply on that thread with a one-line summary of what you changed. When all comments are handled, run the project's tests, commit the changes with a message that references the review, and report back any threads you could not resolve and why.
|
|
68
68
|
```
|
|
69
69
|
````
|
|
70
70
|
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Review rubric (binding for both engines)
|
|
2
|
+
|
|
3
|
+
Low effort: the main agent reads this whole file and walks every active lens. High effort: each reviewer agent reads **Admission** plus its own lens section. Referenced from `SKILL.md`.
|
|
4
|
+
|
|
5
|
+
## Admission
|
|
6
|
+
|
|
7
|
+
### Severity ladder
|
|
8
|
+
|
|
9
|
+
- **CRITICAL** — realistic input causes incorrect behavior, data loss, security regression, broken contract, or a paging incident.
|
|
10
|
+
- **MAJOR** — meaningful degradation of correctness/UX/observability under realistic conditions; OR a documented-convention violation with concrete downstream impact.
|
|
11
|
+
- **MINOR** — cheap, concrete improvement with a named benefit.
|
|
12
|
+
- **NIT** — only admissible if (a) repeats ≥2× in the diff, (b) conflicts with a documented convention, or (c) is a one-line trivial fix.
|
|
13
|
+
|
|
14
|
+
### failure_mode contract
|
|
15
|
+
|
|
16
|
+
Every finding **must** include a `failure_mode`: one sentence on the concrete user-, oncall-, or maintainer-visible bad outcome that would occur if not fixed. Hypotheticals like "a future caller might…" do **NOT** satisfy this — drop the finding.
|
|
17
|
+
|
|
18
|
+
Litmus test before keeping any candidate: _"What is the concrete, current, product-visible cost of leaving this code in?"_ If you can't answer in one sentence, drop it.
|
|
19
|
+
|
|
20
|
+
### Do-not-raise list
|
|
21
|
+
|
|
22
|
+
- Speculative defensiveness at trusted internal boundaries (only where the boundary's guarantee is actually _enforced_ — a declared or cast type is not enforcement; see AntiSlop).
|
|
23
|
+
- Restating the obvious ("consider a comment explaining what this does").
|
|
24
|
+
- Hypothetical future-caller scenarios with no current caller.
|
|
25
|
+
- Style/formatting a linter or formatter covers.
|
|
26
|
+
- Test-coverage demands on trivially-verifiable code.
|
|
27
|
+
- "Add observability" without naming a concrete failure mode it would help debug.
|
|
28
|
+
- Abstract SOLID-style "consider extracting…" without a concrete failure mode.
|
|
29
|
+
- Aesthetic naming preferences — only raise names that mislead about behavior.
|
|
30
|
+
|
|
31
|
+
### Caps
|
|
32
|
+
|
|
33
|
+
6 actionable items (CRITICAL/MAJOR/MINOR) plus 8 NITs retained internally; anything beyond is "N additional items omitted; ask for the full list." High effort: each reviewer agent additionally caps its own output at 8 findings, prioritized — not exhaustive.
|
|
34
|
+
|
|
35
|
+
### suggested_fix schema (when present)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
"suggested_fix": {
|
|
39
|
+
"before": "<exact current code at file:lines>",
|
|
40
|
+
"after": "<replacement code at the same anchor>"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Pure deletion: `after` is `""`.
|
|
45
|
+
- Pure addition at a new line: `before` is `""`, prefix `after` with `// add after line <N>`.
|
|
46
|
+
- Structural change with no clean drop-in: omit `suggested_fix`, describe in `point`/`failure_mode`.
|
|
47
|
+
- Both must be code (not prose), preserving the file's indentation. Don't elide with `// ...`.
|
|
48
|
+
|
|
49
|
+
## Engineering (always)
|
|
50
|
+
|
|
51
|
+
For each change name a realistic input or condition that would expose a bug. If you cannot, do not raise it.
|
|
52
|
+
|
|
53
|
+
- **Declared ≠ enforced — check what actually guarantees a precondition.** When new code performs an operation that faults on a missing or malformed input (destructuring, property/array access, non-null assertion, iteration, parsing, arithmetic), do not accept a declared type, function signature, cast, or `as` as proof the input is safe — those _label_ a value, they don't _check_ it. Ask what enforces the precondition on this path: a runtime validator, a preceding explicit check, or a constructor/factory invariant. If nothing does, trace the value to its origin — it can fault on real data even though it compiles. Look at the operation that would actually throw, not only the named field beside it.
|
|
54
|
+
- **Asymmetric handling across sibling call sites is a likely bug, not a style nit.** When the diff guards, validates, converts, or error-wraps a value in one place but consumes the same value or shape bare elsewhere, exactly one side is usually right. Compare the call sites against each other instead of reviewing each in isolation, and resolve the inconsistency: guard missing where it's absent → bug; guard unnecessary everywhere → slop to remove.
|
|
55
|
+
- Edge cases, error paths, observability of real failure modes.
|
|
56
|
+
- Tests cover real risk, not lines.
|
|
57
|
+
- Concurrency, performance at real scale, data integrity.
|
|
58
|
+
- Backward compatibility, on-call implications, degraded-mode behavior.
|
|
59
|
+
- Async/await ordering matches actual data dependencies.
|
|
60
|
+
- Timezone correctness in date code; currency variables explicitly in minor units.
|
|
61
|
+
- API surface changed → AuthN/AuthZ/PII (deep dive under Security).
|
|
62
|
+
- Migration → rollback, rolling-deploy compat, ETL/downstream impact (deep dive under Database).
|
|
63
|
+
- Schema/query changed → indexes for new patterns, N+1, cascade semantics, type fit (deep dive under Database).
|
|
64
|
+
- Telemetry covers business/product value, not only engineering surface metrics.
|
|
65
|
+
- Contract/backward-compat for any consumer-visible response shape change. **If you suspect a consumer break, the finding is cross-repo — follow the cross-repo evidence policy before raising it.**
|
|
66
|
+
|
|
67
|
+
## Minimalism (always)
|
|
68
|
+
|
|
69
|
+
The smallest diff that ships the intent is the best diff.
|
|
70
|
+
|
|
71
|
+
- Unneeded abstractions, speculative generality, dead branches.
|
|
72
|
+
- Redundant validation, defensive code at trusted internal boundaries.
|
|
73
|
+
- Comments that restate code; new files/utilities that duplicate existing ones.
|
|
74
|
+
- Tests that exercise the framework, not behavior.
|
|
75
|
+
- Flags/config knobs without a concrete caller.
|
|
76
|
+
- Duplicated error handlers in the same scope.
|
|
77
|
+
- Commented-out code or dead branches.
|
|
78
|
+
|
|
79
|
+
For every "delete this" finding, `failure_mode` must state the **concrete cost of keeping the code**.
|
|
80
|
+
|
|
81
|
+
### Named-smell vocabulary
|
|
82
|
+
|
|
83
|
+
Use these names (Fowler, _Refactoring_ ch.3) to label structural findings — a shared name makes the finding legible and the fix direction obvious. Three rules bind them: a smell label alone is **not** a finding (the `failure_mode` contract still applies); a documented repo standard overrides the baseline; each is a judgement call, never a hard violation.
|
|
84
|
+
|
|
85
|
+
- **Mysterious Name** — name doesn't reveal what it does or holds → rename; if no honest name comes, the design's murky.
|
|
86
|
+
- **Duplicated Code** — same logic shape in more than one hunk/file of the change → extract the shared shape.
|
|
87
|
+
- **Feature Envy** — method reaches into another object's data more than its own → move it onto the data it envies.
|
|
88
|
+
- **Data Clumps** — the same few fields/params keep traveling together → bundle into one type.
|
|
89
|
+
- **Primitive Obsession** — a primitive standing in for a domain concept → give the concept its own small type.
|
|
90
|
+
- **Repeated Switches** — same `switch`/`if`-cascade on the same type recurs across the change → polymorphism or one shared map.
|
|
91
|
+
- **Shotgun Surgery** — one logical change forces scattered edits across many files → gather what changes together.
|
|
92
|
+
- **Divergent Change** — one module edited for several unrelated reasons → split by reason for change.
|
|
93
|
+
- **Speculative Generality** — abstraction/hooks for needs nothing has → delete; inline back until a real need shows.
|
|
94
|
+
- **Message Chains** — long `a.b().c().d()` navigation → hide the walk behind one method.
|
|
95
|
+
- **Middle Man** — a class/function that mostly delegates onward → cut it, call the target direct.
|
|
96
|
+
- **Refused Bequest** — implementer ignores/overrides most of what it inherits → drop inheritance, use composition.
|
|
97
|
+
|
|
98
|
+
## Conventions (always)
|
|
99
|
+
|
|
100
|
+
You are the convention owner — validate against what the repo actually documents, not a memorized list. The `@clipboard-health/ai-rules` sync drops a Coding Rules table into each consuming repo's `AGENTS.md`, mapping every rule file that repo adopted to a "When to Read" trigger — those pointers are the source of truth for which conventions apply. Consult, in order of priority:
|
|
101
|
+
|
|
102
|
+
- `git show ${context_ref}:AGENTS.md` and `CLAUDE.md` (if present). Read every rule file in the Coding Rules table whose "When to Read" trigger matches the diff, via `git show ${context_ref}:<rule-path>`.
|
|
103
|
+
- Neighboring files in the same module/service for in-practice patterns.
|
|
104
|
+
- Package READMEs in the touched paths.
|
|
105
|
+
|
|
106
|
+
Flag only violations of what those sources document — do not import conventions from other repos or from memory. One check needs no documented rule: **internal inconsistency within the diff itself** (e.g. half of imports from one package family, half from upstream; same persisted shape written three different ways across three call sites).
|
|
107
|
+
|
|
108
|
+
This lens owns all documented-rule checking, whatever domains the table covers (common, backend, frontend, data). Tag every convention finding with `[CONVENTION]` in the title. Cap severity at MAJOR (only when behavior diverges as a result) or MINOR otherwise.
|
|
109
|
+
|
|
110
|
+
## AntiSlop (always)
|
|
111
|
+
|
|
112
|
+
This PR may have been written or assisted by an LLM. For each addition, ask: _"Is this line earning its keep, or is it pattern-matching what code is supposed to look like?"_ Push back on what other lenses are too polite to flag. Apply to additions **inside the diff itself**.
|
|
113
|
+
|
|
114
|
+
- **Defensive code at trusted internal boundaries.** Null guards on private helpers whose callers' types guarantee non-null; `try`/`catch` wrapping a single non-throwing call, or that re-throws unchanged, or that "logs and swallows" without naming what to do next; optional-chaining through types that don't include optionality. **But "trusted" means the guarantee is _enforced_** — by a validator on this path, a constructor/factory invariant, or a preceding check you can point to. A type, signature, cast, or `as`/non-null-assertion only _asserts_ the guarantee; a guard backing a merely-asserted guarantee is load-bearing, not slop. Confirm what enforces the type at the call site before flagging the guard.
|
|
115
|
+
- **Defensiveness against unrealistic product scenarios.** Litmus: _"In the real product flow this code participates in, what user action / system event / upstream call could land us in this branch?"_ If the answer is "none" or "I had to invent one to justify the guard", it's slop. Concrete shapes:
|
|
116
|
+
- Null/undefined guard on an ID immediately after that ID was used to load (and find) the entity.
|
|
117
|
+
- A `null`/`undefined`/`""`/`0` branch on a field whose TypeScript or Zod/class-validator already rejects those.
|
|
118
|
+
- Re-validation of a value the request DTO already validated upstream in the same lifecycle.
|
|
119
|
+
- Consistency check (`if (a !== b) throw`) between two fields the data model forbids being unequal.
|
|
120
|
+
- Branch for a product-impossible state.
|
|
121
|
+
- Retry/fallback around an SDK call that already retries or returns a typed error.
|
|
122
|
+
- `catch` for an error class statically known not to throw, or that logs+swallows.
|
|
123
|
+
- "Future-proof" code path with no current `v2`.
|
|
124
|
+
|
|
125
|
+
Don't accept "but what if upstream changes?" as a defense — that's the hypothetical-future-caller anti-pattern. The fix when upstream genuinely changes is a typed-error / schema update in that PR, not preemptive guards.
|
|
126
|
+
|
|
127
|
+
- **Restating-the-code comments.** `// fetch the user`, JSDoc on private helpers that only restates the signature, `// Note: this is important`, section banners in short files.
|
|
128
|
+
- **Empty scaffolding.** `// TODO` with no owner/ticket; redundant pre-conditions; debug logs that survived to the PR; default `else { return undefined; }` after exhaustive branching; `_unused` prefixes that should be deletions.
|
|
129
|
+
- **Speculative generality.** Helper called once that wraps two trivial lines; `Map`/`Set`/config keyed by a single hardcoded value; "strategy"/"registry" pattern with one strategy; union types whose only second case is `never`/placeholder.
|
|
130
|
+
- **Unused parameters/overloads/fields.** Args destructured but never read; interface methods with empty implementations; new optional fields with no producer or consumer.
|
|
131
|
+
- **Tests that exercise the framework, not the code.** `jest.fn().mockReturnValue(x); expect(fn()).toBe(x)`; snapshot tests with no semantic assertion; tests that mock the unit under test; test names that describe the implementation (`it("calls foo.bar"…)`) instead of behavior.
|
|
132
|
+
- **Dead AI breadcrumbs.** Variables whose only use is logging or debug branches; `console.log`/`console.error` that should have been removed before commit; commented-out alternative implementations.
|
|
133
|
+
- **Tone/description mismatch.** PR description claims behavior the diff doesn't have; variable/function names that pattern-match engineering writing without naming the role (`data`, `result`, `processed`, `_handle`, `doStuff`).
|
|
134
|
+
|
|
135
|
+
Default `suggested_fix` is **delete** (empty `after`) or **simplify** (smaller `after`). Suggesting "add a justifying comment" is itself slop — do not propose it.
|
|
136
|
+
|
|
137
|
+
Stay in your lane: do not raise items the Conventions lens owns (anything a documented rule file covers); do not demand observability, tests, or error handling that does **not** exist in the diff — you only call out what's _present_ and unnecessary; do not challenge whether the PR solves the right problem. A change that's small but unnecessary is still slop; a change that's large but earns each line is not.
|
|
138
|
+
|
|
139
|
+
## Spec (when a spec source exists)
|
|
140
|
+
|
|
141
|
+
Does the diff faithfully implement what was asked? Read the spec (issue, ticket, PRD, plan file) in full, then check:
|
|
142
|
+
|
|
143
|
+
- **Missing or partial requirements** — things the spec asked for that the diff doesn't do, or half-does.
|
|
144
|
+
- **Scope creep** — behavior in the diff the spec didn't ask for. (Mechanical enablers of requested behavior are fine; new user-visible behavior is not.)
|
|
145
|
+
- **Implemented but wrong** — requirements that look addressed but whose implementation diverges from what the spec actually says (wrong threshold, wrong actor, wrong ordering, wrong default).
|
|
146
|
+
|
|
147
|
+
Quote the spec line each finding is grounded in. Tag every finding `[SPEC]`. The `failure_mode` contract applies — the concrete cost is usually "ships behavior that diverges from what was agreed" made specific (which user, which flow, which wrong outcome). Spec findings report separately in the Summary so a standards-clean diff can't mask a spec miss.
|
|
148
|
+
|
|
149
|
+
## Security (when triggered)
|
|
150
|
+
|
|
151
|
+
Where could this change leak data, bypass authorization, or expand the trust boundary?
|
|
152
|
+
|
|
153
|
+
- AuthN: every new/modified endpoint has authenticated identity unless explicitly public.
|
|
154
|
+
- AuthZ: per-endpoint AND per-resource permission checks; cross-tenant/cross-user access.
|
|
155
|
+
- Secrets in configuration — no hardcoded keys, no secrets in client bundles.
|
|
156
|
+
- No self-made or client-side cryptography.
|
|
157
|
+
- SQL/NoSQL injection: string-concatenated queries, unvalidated `$where`, raw aggregation pipelines from user input.
|
|
158
|
+
- Sensitive data in response payloads — over-fetching, over-serialization, internal IDs leaked.
|
|
159
|
+
- PII fields logged, cached, or sent to telemetry.
|
|
160
|
+
- Input validation at the boundary (Zod / class-validator) on every new endpoint.
|
|
161
|
+
- For numeric inputs: explicit `min`/`max` bounds. Verify what the JS engine does with `Number.MAX_SAFE_INTEGER + 1`, BigInt conversion, negative values, and whether the catch path returns a structured 400 or a 500 with stack log.
|
|
162
|
+
|
|
163
|
+
## Database (when triggered)
|
|
164
|
+
|
|
165
|
+
What breaks at production scale or during deploy?
|
|
166
|
+
|
|
167
|
+
- Index coverage for new query patterns; missing compound indexes.
|
|
168
|
+
- N+1 query shapes; loops issuing per-iteration queries.
|
|
169
|
+
- Schema normalization — denormalization that creates write-amplification or update anomalies.
|
|
170
|
+
- Data types — range, precision, locale (numeric, date, money). Mongoose `Number` is IEEE 754 double; safe for integers up to 2^53.
|
|
171
|
+
- Cascade-delete and FK-on-delete semantics; orphan risk.
|
|
172
|
+
- Migration rollback strategy; online without downtime.
|
|
173
|
+
- Backward compatibility during a rolling deploy (old code reads new schema; new code reads old schema).
|
|
174
|
+
- ETL / downstream consumer impact when schema or field semantics change.
|
|
175
|
+
- New collections / tables ship with the indexes their query patterns need on day one.
|
|
176
|
+
- **Dual-write fields** (storing both `amount` and `amountInMinorUnits`-style pairs): is canonicalization happening on write, or does the on-disk record encode two contradictory values?
|
|
177
|
+
- Backfill for new fields on existing records: present, deferred (with ticket), or missing?
|
|
178
|
+
|
|
179
|
+
## Frontend (when triggered)
|
|
180
|
+
|
|
181
|
+
Will this behave correctly under realistic user conditions? FE conventions (data fetching, component patterns, styling, feature flags, FE testing) are documented rules — the Conventions lens owns them via the repo's Coding Rules table; do not re-derive them from memory here. This lens owns behavior:
|
|
182
|
+
|
|
183
|
+
- Loading / empty / error states for any new data-fetching surface.
|
|
184
|
+
- Accessibility: keyboard navigation, ARIA roles, labels on interactive controls.
|
|
185
|
+
- State that survives realistic interaction: rapid re-clicks, back navigation, stale cache, concurrent mutations, slow networks.
|
|
186
|
+
- Render correctness with realistic data: long strings, empty lists, zero/negative values, missing optional fields.
|