@sitar_fiercer4c/skills 0.1.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/LICENSE +5 -0
- package/README.md +75 -0
- package/bin/install.js +45 -0
- package/package.json +29 -0
- package/skills/architecture-walkthrough/SKILL.md +223 -0
- package/skills/architecture-walkthrough/references/sections.md +29 -0
- package/skills/architecture-walkthrough/scripts/check_structure.py +200 -0
- package/skills/autotest-webapp-ui/SKILL.md +58 -0
- package/skills/backend-code-review/SKILL.md +386 -0
- package/skills/backend-code-review/references/report-format.md +333 -0
- package/skills/backend-code-review/scripts/list_routes.py +269 -0
- package/skills/backend-code-review/scripts/sweep.py +550 -0
- package/skills/backend-code-review/scripts/verify_citations.py +201 -0
- package/skills/be-brief/SKILL.md +18 -0
- package/skills/clarke-list-excel/SKILL.md +51 -0
- package/skills/clarke-list-excel/references/output-schema.md +125 -0
- package/skills/clarke-list-excel/scripts/clarke_common.py +251 -0
- package/skills/clarke-list-excel/scripts/clarke_extract.py +487 -0
- package/skills/clarke-list-excel/scripts/load_clarke.py +322 -0
- package/skills/clarke-list-excel/scripts/run_all.py +63 -0
- package/skills/datalab-api/SKILL.md +163 -0
- package/skills/datalab-api/references/parameters-and-payload.md +121 -0
- package/skills/datalab-api/references/table-selection.md +35 -0
- package/skills/datalab-api/scripts/datalab_tables.py +365 -0
- package/skills/find-test-seam/SKILL.md +41 -0
- package/skills/frontend-code-review/SKILL.md +247 -0
- package/skills/frontend-code-review-2/SKILL.md +192 -0
- package/skills/frontend-code-review-2/scripts/fetch_pr_comments.py +65 -0
- package/skills/frontend-code-review-2/scripts/render_report.py +139 -0
- package/skills/murtaza-breif/SKILL.md +143 -0
- package/skills/murtaza-breif/scripts/save_brief.py +128 -0
- package/skills/pdf-to-json/SKILL.md +42 -0
- package/skills/pdf-to-json/references/output-schema.md +168 -0
- package/skills/pdf-to-json/scripts/extract_figures.py +319 -0
- package/skills/pdf-to-json/scripts/load_mongo.py +287 -0
- package/skills/pdf-to-json/scripts/pdf_extract.py +1313 -0
- package/skills/record-api-traffic/SKILL.md +434 -0
- package/skills/record-api-traffic/references/reading-recordings.md +224 -0
- package/skills/record-api-traffic/scripts/check-schema.mjs +184 -0
- package/skills/record-api-traffic/scripts/dump-quotation.mjs +67 -0
- package/skills/record-api-traffic/scripts/dump-source-excel.mjs +75 -0
- package/skills/record-api-traffic/scripts/lib/repo.mjs +109 -0
- package/skills/record-api-traffic/scripts/preflight.py +528 -0
- package/skills/record-api-traffic/scripts/record-api-traffic.py +720 -0
- package/skills/refac-wrt-business-goal/SKILL.md +305 -0
- package/skills/refac-wrt-business-goal/references/critic.md +170 -0
- package/skills/system-resource-triage/SKILL.md +180 -0
- package/skills/system-resource-triage/scripts/reap.sh +116 -0
- package/skills/system-resource-triage/scripts/triage.sh +111 -0
- package/skills/using-git-worktrees/SKILL.md +167 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-code-review
|
|
3
|
+
description: Review an MVCS backend's HTTP routes against the repo's own code-rules.md, producing a severity-graded report organised by the layering that rules file defines. Use this skill whenever the user asks to review, audit, or critique backend code against architecture or code rules — "review the routes", "check this against code-rules", "audit the API surface", "does this follow our MVCS structure", "is the routes layer clean" — and whenever they point at a code-rules.md, an architecture-rules file, or a routes/ directory and ask what is wrong with it. Also use it when the user asks for a security or layering pass over an Express API, or asks which endpoints lack auth, validation, or rate limiting. Only the routes layer has an established methodology; see Scope.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend code review
|
|
7
|
+
|
|
8
|
+
## What this produces
|
|
9
|
+
|
|
10
|
+
One markdown file: a review of a backend's HTTP routes, judged against that repo's own
|
|
11
|
+
`code-rules.md`, organised by the layering that file defines. Severity counts and a route
|
|
12
|
+
inventory at the top, per-file and per-route finding tables in the middle, a prioritised fix
|
|
13
|
+
list at the bottom.
|
|
14
|
+
|
|
15
|
+
`references/report-format.md` is the output contract — read it before writing anything. It
|
|
16
|
+
carries the skeleton, the severity model, the table columns, and worked example rows showing the
|
|
17
|
+
level of specificity expected.
|
|
18
|
+
|
|
19
|
+
Write the file, report the path, stop. Do not also summarise the findings in chat: the report is
|
|
20
|
+
long and a chat summary either duplicates it or quietly becomes the thing the user reads instead,
|
|
21
|
+
which defeats the point of a document they can return to and check.
|
|
22
|
+
|
|
23
|
+
## Scope
|
|
24
|
+
|
|
25
|
+
**Only the routes layer has an established methodology.** It was worked out against a real
|
|
26
|
+
codebase and revised until a reader was satisfied with it.
|
|
27
|
+
|
|
28
|
+
The other sections a `code-rules.md` typically names — services, controllers, models, config,
|
|
29
|
+
utils, validators, middleware — do not have one yet. If asked to review those, say so plainly and
|
|
30
|
+
offer to work one out together, the way the routes methodology was worked out. Then stop.
|
|
31
|
+
|
|
32
|
+
Improvising a methodology and shipping it under this skill's name is worse than an honest gap.
|
|
33
|
+
The user cannot tell, from the output, which parts of the approach were validated and which were
|
|
34
|
+
invented on the spot, so the unvetted section contaminates their trust in the reviewed one.
|
|
35
|
+
|
|
36
|
+
This does not mean ignoring those layers. Reviewing a route *requires* reading the service it
|
|
37
|
+
calls — see "Trace every route to its floor". The boundary is about which layer the findings are
|
|
38
|
+
organised around and which layer gets a systematic pass, not about which files you may open.
|
|
39
|
+
|
|
40
|
+
## The protocol
|
|
41
|
+
|
|
42
|
+
### 1. Read `code-rules.md` first, before any code
|
|
43
|
+
|
|
44
|
+
It is the specification, not background. It gives you two things:
|
|
45
|
+
|
|
46
|
+
- **The section list**, in order — these become the report's H2 headings. Take them from the file,
|
|
47
|
+
not from a fixed list, so a repo naming `jobs` or `adapters` gets those headings.
|
|
48
|
+
- **The rule for each layer** — what routes are allowed to do, what belongs in controllers versus
|
|
49
|
+
services, what validators are for, and any explicit DOS/DONTS.
|
|
50
|
+
|
|
51
|
+
Every finding names the rule it is judged against. That is what makes this a review against *this
|
|
52
|
+
repo's stated architecture* rather than a stream of generic advice the user did not ask for. A
|
|
53
|
+
criticism that cannot be tied to a rule, and is not a security or correctness issue, is a
|
|
54
|
+
preference — leave it out.
|
|
55
|
+
|
|
56
|
+
If there is no rules file, ask for one or ask which conventions to review against. Do not
|
|
57
|
+
substitute your own idea of good layering; the user chose theirs.
|
|
58
|
+
|
|
59
|
+
Also check whether this code has been reviewed before — an existing review document, a decisions
|
|
60
|
+
record, PR threads on a previous round. Maintainers reject findings for reasons that are not
|
|
61
|
+
visible in the code, and a reviewer who re-raises a settled point spends credibility needed for
|
|
62
|
+
the real findings. Where a prior decision exists, honour it or argue against it explicitly; do
|
|
63
|
+
not silently re-litigate it.
|
|
64
|
+
|
|
65
|
+
### 2. Build the route inventory mechanically
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
python3 scripts/list_routes.py <backend>/src
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This resolves each route's **real** path by combining the router's mount prefix from the app
|
|
72
|
+
entrypoint with the path in the route file, and it flags routes defined inline in the entrypoint
|
|
73
|
+
and middleware applied by path prefix.
|
|
74
|
+
|
|
75
|
+
Run it rather than assembling the inventory by hand. Deriving paths by reading is where reviews
|
|
76
|
+
go wrong before they start, in two specific ways:
|
|
77
|
+
|
|
78
|
+
- **Doc comments naming a route's path drift from the mount point and are frequently wrong.** In
|
|
79
|
+
one backend, nine comments across a single controller named a URL segment that did not exist —
|
|
80
|
+
the router was mounted a level up from what every comment claimed. A whole review was drafted
|
|
81
|
+
from those comments before anyone opened the entrypoint.
|
|
82
|
+
- **Hand-counting gets the aggregate wrong.** The derived statistics — how many routes carry a
|
|
83
|
+
validator, how many carry auth — are the lines a reader is most likely to quote, and the most
|
|
84
|
+
embarrassing to get wrong. A hand-built inventory of one backend reported four routes with
|
|
85
|
+
validators; the script found three, because a route with `auth` middleware had been miscounted.
|
|
86
|
+
|
|
87
|
+
Treat the output as a scaffold, not a finding. Regex parsing misses dynamic mounts, conditionally
|
|
88
|
+
registered routes, and re-exported routers. Confirm each row against the file as you trace it.
|
|
89
|
+
|
|
90
|
+
Two things in the script's output are findings in their own right, worth checking before you go
|
|
91
|
+
further:
|
|
92
|
+
|
|
93
|
+
- **Routes defined inline in the entrypoint**, marked `(inline)`. `code-rules.md` files that name
|
|
94
|
+
a `routes` section almost always mean endpoints live there. An inline handler typically has no
|
|
95
|
+
controller, no validator, and its own ad-hoc error envelope that bypasses the error middleware.
|
|
96
|
+
- **Middleware applied by path prefix**, listed separately. `app.use('/some/path', limiter)` is
|
|
97
|
+
invisible when reading the route file, and it matches all methods and all subpaths — so a
|
|
98
|
+
limiter meant for `POST /login` also throttles `GET /login/anything`.
|
|
99
|
+
|
|
100
|
+
**Then walk the inventory once for cost, before you start tracing.** Take the full route list —
|
|
101
|
+
every row, in order — and mark each one that reaches a third-party billed call, a subprocess, an
|
|
102
|
+
LLM or OCR service, or heavy CPU. Cross that column against the limiter column. This is a pass
|
|
103
|
+
over a list, not a recall exercise, and it has to be done that way: a review that names the
|
|
104
|
+
throttling gaps it happened to notice reads as an exhaustive list to the person acting on it, so
|
|
105
|
+
the endpoint it skipped is the one that stays unprotected. One review missed
|
|
106
|
+
`POST /api/messages/:messageId/attachments/:attachmentId/quotation` — a paid extraction call —
|
|
107
|
+
entirely, while correctly listing four cheaper siblings.
|
|
108
|
+
|
|
109
|
+
### 3. Sweep the tree for what reading cannot see
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python3 scripts/sweep.py <backend>/src --repo-root <backend>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Tracing a route down through its layers is how you find what a single file does wrong. It is
|
|
116
|
+
structurally blind to a second class of finding, the kind that exists only across files: an
|
|
117
|
+
environment variable declared in `.env` that nothing reads, a helper defined identically in two
|
|
118
|
+
config modules, an export with no importer, a block of code pasted at three sites. Nobody spots
|
|
119
|
+
those by reading carefully, because every individual file looks fine. You spot them by counting.
|
|
120
|
+
|
|
121
|
+
This matters more than it sounds. An earlier version of this skill relied on careful reading
|
|
122
|
+
alone and rediscovered a *different subset* of these findings on each run — the same review, run
|
|
123
|
+
twice, would report the unused credentials one time and the duplicated helper the next. Coverage
|
|
124
|
+
that depends on what you happened to notice is sampling, not review. The sweep makes it
|
|
125
|
+
repeatable.
|
|
126
|
+
|
|
127
|
+
Five sections come back:
|
|
128
|
+
|
|
129
|
+
- **`ENV`** — every `process.env` read with its site, against what the `.env` files declare.
|
|
130
|
+
`DECLARED-NEVER-READ` is often a stale credential sitting in a checked-in file with no consumer.
|
|
131
|
+
`READ-NEVER-DECLARED` is a tunable no one can discover. `READS-OUTSIDE-CONFIG` is the layering
|
|
132
|
+
question, if the rules file gives config ownership of environment access.
|
|
133
|
+
- **`EXPORTS`** — for each exported identifier, its in-file reference count and its external
|
|
134
|
+
importers. The distinction between the two numbers is the whole point; see "no importer is not
|
|
135
|
+
the same as no caller" below. A separate `same-name-elsewhere` line lists files that merely
|
|
136
|
+
contain the word — that is a duplicate-definition lead, not an importer.
|
|
137
|
+
- **`REQUEST INPUTS`** — every request-derived key name in the tree, each marked `MASKED` or
|
|
138
|
+
`UNMASKED` against the masking array the sweep finds, plus the sites that log or pass a whole
|
|
139
|
+
`req.query`/`req.body` object.
|
|
140
|
+
- **`DUPES`** — the same identifier defined at top level in more than one file.
|
|
141
|
+
- **`BLOCKS`** — normalised line windows appearing verbatim in more than one place.
|
|
142
|
+
|
|
143
|
+
Every row is a lead, not a finding. The parsing is regex-based, so it mis-handles dynamic property
|
|
144
|
+
access, re-exports, and identifiers built from strings, and a name appearing in two files is
|
|
145
|
+
sometimes two unrelated things. Open the file and confirm before any of it goes in the report.
|
|
146
|
+
|
|
147
|
+
**`REQUEST INPUTS` exists because the natural grep misses keys.** A masking list is only correct
|
|
148
|
+
if it covers every sensitive key the app actually receives, and that set is not greppable: a
|
|
149
|
+
callback handler often never writes `req.query.code` at all — the controller passes the whole
|
|
150
|
+
`req.query` object into a service, which destructures `{ state, code, error }` out of its own
|
|
151
|
+
parameter, a file and a layer away. The sweep follows that one hop. In one backend this was the
|
|
152
|
+
difference between reporting two unmasked keys and reporting three; the third, `t`, was a signed
|
|
153
|
+
image-proxy token read on a different route entirely. Take the enumeration from this output rather
|
|
154
|
+
than from what you remember reading.
|
|
155
|
+
|
|
156
|
+
**Then cross-reference your own notes.** Some of the best findings are two ordinary observations
|
|
157
|
+
that only mean something together, and depth-first reading files them in separate sections where
|
|
158
|
+
they never meet. One review noted that a config module's header comment cites another file as the
|
|
159
|
+
model lazy-environment pattern, and noted elsewhere that this same file reads its environment at
|
|
160
|
+
module load — the bug the comment holds up as the fix. Both facts were in the document; the
|
|
161
|
+
finding was not. Before writing, reread what you have collected and look for pairs like that: a
|
|
162
|
+
comment that claims something you disproved elsewhere, a helper recommended in one section that
|
|
163
|
+
you flagged in another, a guard present on one route and absent on its sibling.
|
|
164
|
+
|
|
165
|
+
### 4. Trace every route to its floor
|
|
166
|
+
|
|
167
|
+
For each route, follow the whole chain: route → middleware → validator → controller → service(s)
|
|
168
|
+
→ utils and models. Judge each layer against its own rule.
|
|
169
|
+
|
|
170
|
+
**Violations hide one layer below where they appear.** A controller can look perfectly thin while
|
|
171
|
+
the service it delegates to does another layer's job — reaching into a model it should not own,
|
|
172
|
+
or running two hundred lines of input sanitisation that the rules file says belongs in
|
|
173
|
+
`validators/`. A review that reads the route file and the controller and stops will miss most of
|
|
174
|
+
what is actually there, and will read as superficial to anyone who knows the code.
|
|
175
|
+
|
|
176
|
+
This is the expensive step and there is no shortcut. Budget for it.
|
|
177
|
+
|
|
178
|
+
When two routes share a handler shape — paired exports, paired list endpoints — read both rather
|
|
179
|
+
than assuming symmetry. Near-duplicate handlers are where copy-paste divergence lives, and the
|
|
180
|
+
duplication itself is usually worth a finding.
|
|
181
|
+
|
|
182
|
+
### 5. Judge, and record what holds
|
|
183
|
+
|
|
184
|
+
Severity model:
|
|
185
|
+
|
|
186
|
+
| Marker | Means |
|
|
187
|
+
|---|---|
|
|
188
|
+
| 🔴 | Rule violation, security defect, or correctness bug |
|
|
189
|
+
| 🟡 | Rule drift, duplication, fragile coupling |
|
|
190
|
+
| ⚠️ | Observation, nit, inconsistency |
|
|
191
|
+
| ✅ | Verified correct — worth keeping as-is |
|
|
192
|
+
|
|
193
|
+
**Before you write 🔴 or 🟡 on any row, answer this out loud in your notes:**
|
|
194
|
+
|
|
195
|
+
> **Does the harm require a fact that is not in this repository?**
|
|
196
|
+
|
|
197
|
+
Facts that are not in the repository include: the `NODE_ENV` value the deployment actually sets,
|
|
198
|
+
the number of proxies in front of the app, whether a permissive default someone explicitly wrote
|
|
199
|
+
down was intended, which hosts are on the allow-list at the load balancer.
|
|
200
|
+
|
|
201
|
+
If the answer is **yes**, the row is capped at ⚠️ and phrased as **"confirm X"**, never "change X".
|
|
202
|
+
If the answer is **no** — the harm follows from code that is in front of you, at any deployment —
|
|
203
|
+
grade it on its merits.
|
|
204
|
+
|
|
205
|
+
Worked through the cases this exists for:
|
|
206
|
+
|
|
207
|
+
| Candidate | Requires an outside fact? | Grade |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| TOTP printed to stdout behind a `NODE_ENV !== 'production'` gate | Yes — what `NODE_ENV` is set to | ⚠️ "confirm production sets it" |
|
|
210
|
+
| `CORS_ORIGIN='*'` written into both `.env` and `.env.example` | Yes — whether the deployed value is the example one | ⚠️ "confirm the deployed origin" |
|
|
211
|
+
| `app.set('trust proxy', 1)` | Yes — how many proxies are actually in front | ⚠️ "confirm the hop count" |
|
|
212
|
+
| One global secret signs password-reset tokens | No — true at every deployment | 🔴 |
|
|
213
|
+
| `/api/health/officecli` reachable unauthenticated | No — no middleware on the route, full stop | 🔴 |
|
|
214
|
+
| One `JWT_SECRET` signs both session and image-proxy tokens | No — visible in the two signing sites | 🔴 |
|
|
215
|
+
|
|
216
|
+
The reason this is a gate and not advice: the rest of this skill sharpens security instincts hard,
|
|
217
|
+
and a paragraph asking for restraint loses to them every time. The question has a yes/no answer
|
|
218
|
+
and a forced consequence, so it survives the pull. A reader who dismisses three re-opened
|
|
219
|
+
decisions reads the fourth finding — the real one — with less attention, and that is the cost.
|
|
220
|
+
|
|
221
|
+
A ⚠️ "confirm" row is not a weaker finding. It is the accurate one: you found something whose
|
|
222
|
+
safety depends on a value you cannot see, and the useful output is to name the value.
|
|
223
|
+
|
|
224
|
+
**A `✅` is a claim that you checked the thing that could go wrong.** Not that the code reads well,
|
|
225
|
+
and not that the mechanism is clever. The happy path working is not evidence; it is the default.
|
|
226
|
+
So say which failure path you followed — the catch block, the second concurrent caller, the
|
|
227
|
+
missing-config case, the malformed input — and what you found there. If you did not follow one,
|
|
228
|
+
that is not a `✅`. Leave it out. Silence is honest; a `✅` is a promise.
|
|
229
|
+
|
|
230
|
+
The reason to be strict here is that `✅` is sold to the reader as "examined and sound", so a wrong
|
|
231
|
+
one is worse than no row at all — it actively stops the next person from looking. Real examples
|
|
232
|
+
from a review of this kind: a streaming proxy marked `✅` for "handling its failure mode well",
|
|
233
|
+
where the mechanism was indeed careful but the `catch` around it collapsed credential, network and
|
|
234
|
+
permission errors alike into `404 Image not found`. A counter marked `✅` for sound concurrency
|
|
235
|
+
reasoning, where the increment was genuinely atomic but the caller sat on a `GET` path and threw
|
|
236
|
+
the reserved number away on every conflict. In both, the praise was accurate about the mechanism
|
|
237
|
+
and blind to the code immediately around it. A `✅` covers what you examined and nothing adjacent.
|
|
238
|
+
|
|
239
|
+
`✅` rows still matter and should be there. A review listing only faults leaves the reader unable
|
|
240
|
+
to tell "examined and sound" from "not examined", and that ambiguity is worst exactly where the
|
|
241
|
+
stakes are highest. Record the non-obvious things you actually probed: a security control you
|
|
242
|
+
tried to get past, a guard you confirmed runs before the expensive work, an idempotency mechanism
|
|
243
|
+
you traced through a retry.
|
|
244
|
+
|
|
245
|
+
## What to look for below the route file
|
|
246
|
+
|
|
247
|
+
Route files are short and mostly fine. The findings are underneath. This list is where the
|
|
248
|
+
non-obvious ones came from — treat it as prompts to check, not a checklist to fill.
|
|
249
|
+
|
|
250
|
+
- **Writes on a `GET` path.** Lazy backfills, self-healing migrations, cache warming, counters
|
|
251
|
+
consumed to build a display value. `GET` is meant to be safe and idempotent; concurrent reads
|
|
252
|
+
then race each other into writes, and work reserved on a read path is usually work discarded.
|
|
253
|
+
- **Whole documents serialised to the client.** A `.lean()` or a raw `res.json(doc)` bypasses the
|
|
254
|
+
model's own `toJSON`, so fields that transform strips — buffers, hashes, internal state — go out
|
|
255
|
+
on the wire. Check what the model deletes and whether the query path honours it.
|
|
256
|
+
- **One secret signing two different token types.** Session tokens and image-proxy or download
|
|
257
|
+
tokens signed with the same key. Often safe only by payload-shape coincidence — one verifier
|
|
258
|
+
requires a field the other's tokens lack. That is not a documented boundary, so a future
|
|
259
|
+
payload change breaks it silently.
|
|
260
|
+
- **Credentials in logs.** Request loggers that dump `req.query` capture OAuth authorization
|
|
261
|
+
codes on callback routes and signed tokens on proxy routes. Check whether the masking helper
|
|
262
|
+
the codebase already has is actually applied at the outermost layer, not just deep in a service.
|
|
263
|
+
Take the unmasked key list from `sweep.py --section request-inputs`, not from reading — the keys
|
|
264
|
+
that leak are exactly the ones no grep finds, because they are destructured a layer away from
|
|
265
|
+
where the request object is read.
|
|
266
|
+
- **Error handlers that flatten distinct failures into one status.** A `catch` mapping everything
|
|
267
|
+
to `404` or `400` hides credential and network failures from whoever has to debug them. Check
|
|
268
|
+
what the handler discriminates on, not just that it has a handler.
|
|
269
|
+
- **Validation living in services.** Length caps, allow-lists, bounds checks, recipient parsing
|
|
270
|
+
inside a service function, when the rules file has a `validators` section. Note where it is and
|
|
271
|
+
how much of it there is; the volume is the argument.
|
|
272
|
+
- **Expensive or paid-API endpoints left unthrottled** — the cost/limiter cross-product from
|
|
273
|
+
step 2. Also check whether a `force`-style query flag bypasses an idempotency short-circuit,
|
|
274
|
+
which converts a bounded operation into unbounded spend.
|
|
275
|
+
- **A helper that exists to consolidate a pattern, bypassed by one caller.** Codebases grow a
|
|
276
|
+
`withX` wrapper and then leave the oldest call site hand-rolling the same sequence. The
|
|
277
|
+
wrapper's own comment often claims universal use — check it.
|
|
278
|
+
- **Redundant round-trips to an external API** inside a single request, usually from a controller
|
|
279
|
+
calling two service functions that each independently fetch the same remote object.
|
|
280
|
+
- **Auth applied inconsistently across sibling routes** — and, where a route deliberately has no
|
|
281
|
+
auth middleware, what actually authorises it. A signed token in a query string can be a correct
|
|
282
|
+
design; say so and explain the trail rather than reporting it as missing auth.
|
|
283
|
+
|
|
284
|
+
## Verify before you write
|
|
285
|
+
|
|
286
|
+
Every claim is checkable, and a reader who finds one wrong claim discounts the rest of the
|
|
287
|
+
report. Four failure modes to guard against specifically:
|
|
288
|
+
|
|
289
|
+
**Comments are claims, not facts.** An export block commented "exported for unit testing" is a
|
|
290
|
+
hypothesis. Grep for the identifiers across `src`, tests, and scripts before repeating it — in one
|
|
291
|
+
backend none of the six such exports was imported anywhere, so the comment was simply false.
|
|
292
|
+
|
|
293
|
+
**"No importer" is not the same as "no caller."** That same finding once produced advice that
|
|
294
|
+
would have broken the file: the review concluded the six exports were dead and said deleting them
|
|
295
|
+
was safe, when every one had four or five callers *inside its own module*. They were over-exported,
|
|
296
|
+
not unused. The repair for an identifier with no external importer is to narrow the export list;
|
|
297
|
+
the repair for one with no references anywhere is to delete the definition. `sweep.py` reports
|
|
298
|
+
both counts side by side precisely because the two look identical from outside the file and the
|
|
299
|
+
remedies are opposite. Check the in-file count before you recommend a deletion.
|
|
300
|
+
|
|
301
|
+
**Sameness is measurable, so measure it.** "Identical", "the same twelve phrases", "differs only
|
|
302
|
+
by X" — these are the claims a duplication finding rests on, and they are the reader's entire
|
|
303
|
+
reason to consolidate. Diff the two sites rather than eyeballing them. A review that reported two
|
|
304
|
+
keyword lists as covering the same phrases, when one had eleven entries and the other twelve, gave
|
|
305
|
+
its reader a merge that would silently drop a case. The same goes for arithmetic in prose: if you
|
|
306
|
+
write "15 of 31", the remainder is 16, and a reader who spots the slip stops trusting the counts
|
|
307
|
+
that matter.
|
|
308
|
+
|
|
309
|
+
**Follow the value to its sink before reporting an injection or traversal.** A path segment
|
|
310
|
+
interpolated into a key looks alarming; if the result is compared for equality against a
|
|
311
|
+
signature-verified payload, it is closed, and reporting it as traversable is the kind of error
|
|
312
|
+
that costs the reader's trust in everything else. Trace it, then either report it as a real
|
|
313
|
+
finding or record it as a `✅` with the reason it holds.
|
|
314
|
+
|
|
315
|
+
Cite `file.js:LINE` for everything, and confirm the line still holds what you say before it goes
|
|
316
|
+
in. Duplication findings cite every site, with line ranges — "this is duplicated" with one
|
|
317
|
+
citation is unactionable and unverifiable.
|
|
318
|
+
|
|
319
|
+
**A citation carries two claims, and checking one does not check the other.** The line number is
|
|
320
|
+
one claim; the assertion riding on it is the other. Confirming that `excel.service.js:10–32`
|
|
321
|
+
exists is not confirming that the function there has no `return results;` — in the review that
|
|
322
|
+
made exactly that mistake, `return results;` was line 33, one past the cited range, and the range
|
|
323
|
+
itself stopped mid-function with an unclosed brace. Nothing about the line check would catch it,
|
|
324
|
+
because the line check passed. So verify the claim against the construct, then cite it.
|
|
325
|
+
|
|
326
|
+
This is sharpest on **negative claims** — "no return", "never calls", "lacks a guard", "no
|
|
327
|
+
importer". An absence is only observable over a complete construct: if your range ends before the
|
|
328
|
+
closing brace, the thing you say is missing may be on the next line. Cite the construct's full
|
|
329
|
+
extent, and quote the closing lines or the command that establishes the absence.
|
|
330
|
+
|
|
331
|
+
### The last thing you do before the report is finished
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
python3 scripts/verify_citations.py <report>.md --root <backend> --problems
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
It resolves every `file:LINE` and `file:LINE–LINE` in the document, prints the real source back,
|
|
338
|
+
and flags three things: `NOT-FOUND` (the path or line does not resolve), `AMBIGUOUS` (a bare
|
|
339
|
+
filename matching several files — say which), and `TRUNCATED-CONSTRUCT` (a range on a row that
|
|
340
|
+
claims an absence, where the braces do not balance across it).
|
|
341
|
+
|
|
342
|
+
Every flag is a row to reopen. `TRUNCATED-CONSTRUCT` in particular is not a formatting nit; it is
|
|
343
|
+
the script telling you the evidence for that row does not cover the claim. Some will be fine on
|
|
344
|
+
inspection — a row citing "9 sites across `:19–176`" is a span, not a construct — but you have to
|
|
345
|
+
look, because this is the one failure mode that reads as authoritative when it is fabricated.
|
|
346
|
+
|
|
347
|
+
Run it against the finished document, fix what it finds, and run it again.
|
|
348
|
+
|
|
349
|
+
## Writing the findings
|
|
350
|
+
|
|
351
|
+
Each finding carries four things in order: a **bold claim in a few words**, the evidence, the
|
|
352
|
+
consequence, and the repair. The reader is scanning a wide table; the bold clause is what they
|
|
353
|
+
read first and it has to carry the point on its own.
|
|
354
|
+
|
|
355
|
+
**The repair is the part reviews drop, and it is the part the reader needs most.** You have just
|
|
356
|
+
spent an hour tracing this code. They have not. A finding that stops at the consequence hands the
|
|
357
|
+
hardest question — what do we actually do — back to the person with the least context to answer
|
|
358
|
+
it, and a finding nobody can act on is one nobody can schedule or argue with either. Name the
|
|
359
|
+
function to extract, the module to create, the import to delete, the signature to change, the
|
|
360
|
+
config key to add. "This is duplicated across three files" is a diagnosis; "extract it to
|
|
361
|
+
`utils/fileName.util.js` and import it at all three" is a review.
|
|
362
|
+
|
|
363
|
+
Where the right repair is genuinely contested, say so *and still give the reader something*: name
|
|
364
|
+
the two options and what each costs, then say which one the rules file favours. That is a repair.
|
|
365
|
+
"Whether it should move is a judgement call" is where the reviewer stopped working.
|
|
366
|
+
|
|
367
|
+
The prioritised fix list at the bottom is not a substitute. Readers work from the tables, and a
|
|
368
|
+
row whose fix lives forty lines away in another section reads as a complaint.
|
|
369
|
+
|
|
370
|
+
**Repair the whole finding, not the instance you noticed.** If the claim is that a block appears
|
|
371
|
+
at three sites, the repair addresses all three — a fix that folds two of them leaves the third
|
|
372
|
+
behind and reads as complete, so nobody comes back for it. If the finding names three concerns in
|
|
373
|
+
one place — a value read, validated, and normalised in the wrong layer — the repair moves all
|
|
374
|
+
three. Partial repairs are how the leftover site survives the cleanup.
|
|
375
|
+
|
|
376
|
+
Prefer naming a place in the same codebase that already does the thing correctly over
|
|
377
|
+
recommending an abstract fix. If one module uses a dedicated secret and another reuses the shared
|
|
378
|
+
one, the fix is "follow `oauthState.js`" — concrete, obviously correct, and already approved by
|
|
379
|
+
whoever wrote it.
|
|
380
|
+
|
|
381
|
+
State consequences in terms of what it costs: unbounded spend, an hour of unrevocable access, a
|
|
382
|
+
credential in a log file. Not "this is bad practice".
|
|
383
|
+
|
|
384
|
+
Close with the prioritised fix list. The tables are organised for lookup, not for action — rank
|
|
385
|
+
the fixes by exploitability and blast radius, so an unauthenticated remote issue outranks an
|
|
386
|
+
internal layering violation even when both are 🔴.
|