opencode-thrum-pro 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/assets/commands/thrum-_deviations-protocol.md +87 -0
- package/assets/commands/thrum-_generator-idempotency-protocol.md +15 -0
- package/assets/commands/thrum-_project-rules-protocol.md +18 -0
- package/assets/commands/thrum-_snapshot-protocol.md +265 -0
- package/assets/commands/thrum-_stamp-protocol.md +112 -0
- package/assets/commands/thrum-_verify-before-write-protocol.md +27 -0
- package/assets/commands/thrum-inbox.md +22 -0
- package/assets/commands/thrum-load-context.md +17 -0
- package/assets/commands/thrum-overview.md +17 -0
- package/assets/commands/thrum-prime.md +30 -0
- package/assets/commands/thrum-quickstart.md +14 -0
- package/assets/commands/thrum-reply.md +28 -0
- package/assets/commands/thrum-restart-extended.md +195 -0
- package/assets/commands/thrum-restart.md +293 -0
- package/assets/commands/thrum-send.md +31 -0
- package/assets/commands/thrum-sleep-extended.md +186 -0
- package/assets/commands/thrum-sleep.md +212 -0
- package/assets/commands/thrum-team.md +12 -0
- package/assets/commands/thrum-update-project.md +259 -0
- package/assets/commands/thrum-wait.md +14 -0
- package/assets/skills/adversarial-critique/SKILL.md +242 -0
- package/assets/skills/brainstorm-queue-aware/SKILL.md +167 -0
- package/assets/skills/brainstorm-steward/SKILL.md +181 -0
- package/assets/skills/choosing-subagent-models/SKILL.md +244 -0
- package/assets/skills/closing-findings-while-warm/SKILL.md +104 -0
- package/assets/skills/configure-roles/SKILL.md +229 -0
- package/assets/skills/coordinator-assessing-agent-completion/SKILL.md +205 -0
- package/assets/skills/coordinator-assessing-agent-completion/scan.py +117 -0
- package/assets/skills/coordinator-branch-split-on-block/SKILL.md +168 -0
- package/assets/skills/coordinator-context-monitoring/SKILL.md +674 -0
- package/assets/skills/coordinator-dispatching-work/SKILL.md +288 -0
- package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +522 -0
- package/assets/skills/coordinator-interacting-with-the-owner/SKILL.md +171 -0
- package/assets/skills/coordinator-maintaining-memory/SKILL.md +89 -0
- package/assets/skills/coordinator-managing-state-and-lifecycle/SKILL.md +192 -0
- package/assets/skills/coordinator-merging-code/SKILL.md +331 -0
- package/assets/skills/coordinator-philosophy-merge-gate/SKILL.md +442 -0
- package/assets/skills/coordinator-plan-reconcile-gate/SKILL.md +198 -0
- package/assets/skills/coordinator-post-restart-sweep/SKILL.md +135 -0
- package/assets/skills/coordinator-running-autonomous-feature-development/SKILL.md +199 -0
- package/assets/skills/coordinator-running-brainstorm-cycles/SKILL.md +654 -0
- package/assets/skills/coordinator-running-review-cycles/SKILL.md +153 -0
- package/assets/skills/coordinator-single-responsibility-merge-gate/SKILL.md +14 -0
- package/assets/skills/diagnosing-fleet-and-daemon/SKILL.md +255 -0
- package/assets/skills/efficient-multi-agent-research/SKILL.md +177 -0
- package/assets/skills/fixing-documentation-errors/SKILL.md +258 -0
- package/assets/skills/implementer-maintaining-memory/SKILL.md +84 -0
- package/assets/skills/implementer-orchestrating-execution/SKILL.md +125 -0
- package/assets/skills/implementer-receiving-dispatch/SKILL.md +127 -0
- package/assets/skills/implementer-receiving-review-feedback/SKILL.md +125 -0
- package/assets/skills/implementer-status-and-handoff/SKILL.md +150 -0
- package/assets/skills/implementer-tdd-and-quality/SKILL.md +99 -0
- package/assets/skills/memory-audit-recover/SKILL.md +153 -0
- package/assets/skills/memory-curate/SKILL.md +139 -0
- package/assets/skills/memory-maintenance/SKILL.md +87 -0
- package/assets/skills/memory-read-discipline/SKILL.md +164 -0
- package/assets/skills/memory-search-advanced/SKILL.md +217 -0
- package/assets/skills/memory-write-discipline/SKILL.md +135 -0
- package/assets/skills/orchestrate/SKILL.md +795 -0
- package/assets/skills/persistent-watcher-archetype/SKILL.md +225 -0
- package/assets/skills/prime-agent/SKILL.md +92 -0
- package/assets/skills/project-hotpath-gate/SKILL.md +255 -0
- package/assets/skills/project-philosophy/SKILL.md +418 -0
- package/assets/skills/project-philosophy/resources/philosophy-template.md +155 -0
- package/assets/skills/project-philosophy/resources/reliability-class-library.md +653 -0
- package/assets/skills/project-setup/SKILL.md +1007 -0
- package/assets/skills/project-setup/resources/implementation-agent.md +826 -0
- package/assets/skills/researcher-answering-queries/SKILL.md +100 -0
- package/assets/skills/researcher-investigating/SKILL.md +152 -0
- package/assets/skills/researcher-maintaining-memory/SKILL.md +176 -0
- package/assets/skills/state-migration/SKILL.md +54 -0
- package/assets/skills/thrum/SKILL.md +161 -0
- package/assets/skills/thrum/resources/ANTI_PATTERNS.md +132 -0
- package/assets/skills/thrum/resources/BOUNDARIES.md +56 -0
- package/assets/skills/thrum/resources/CLI_REFERENCE.md +1066 -0
- package/assets/skills/thrum/resources/IDENTITY.md +49 -0
- package/assets/skills/thrum/resources/LISTENER_PATTERN.md +64 -0
- package/assets/skills/thrum/resources/MESSAGING.md +163 -0
- package/assets/skills/thrum/resources/TMUX_SESSIONS.md +118 -0
- package/assets/skills/thrum/resources/WORKTREES.md +117 -0
- package/assets/skills/thrum-memory/SKILL.md +138 -0
- package/assets/skills/trace-spec-to-plan/SKILL.md +104 -0
- package/assets/skills/trace-spec-to-plan/resources/sweep-agent-prompt.md +57 -0
- package/assets/skills/update-queue/SKILL.md +149 -0
- package/assets/skills/using-the-queue/SKILL.md +172 -0
- package/assets/skills/using-thrum-state/SKILL.md +104 -0
- package/assets/skills/verify-against-plan/SKILL.md +323 -0
- package/assets/skills/verify-against-source/SKILL.md +237 -0
- package/assets/skills/watcher-archetype/SKILL.md +84 -0
- package/assets/skills/writing-durable-markdown/SKILL.md +66 -0
- package/dist/hooks/block-sync-cd.d.ts +9 -0
- package/dist/hooks/block-sync-cd.js +71 -0
- package/dist/hooks/block-sync-cd.test.d.ts +1 -0
- package/dist/hooks/block-sync-cd.test.js +61 -0
- package/dist/hooks/shell-env.d.ts +8 -0
- package/dist/hooks/shell-env.js +22 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +25 -0
- package/dist/installer.d.ts +2 -0
- package/dist/installer.js +86 -0
- package/package.json +49 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
# Reliability Class Library
|
|
2
|
+
|
|
3
|
+
Shared reference loaded by `project-philosophy` (all 7 classes) and
|
|
4
|
+
`project-hotpath-gate` (classes tagged `hotpath` below, via their hot-path
|
|
5
|
+
facet). Distilled from thrum's own anti-patterns and SQLite crash-recovery lessons.
|
|
6
|
+
Each class is a generalizable
|
|
7
|
+
failure SHAPE, not a thrum-specific rule — adapt the probes to the target
|
|
8
|
+
repo's detected language before writing anything into a generated doc.
|
|
9
|
+
|
|
10
|
+
For each class found in the target repo, render it as a BAD/GOOD anti-pattern
|
|
11
|
+
using the repo's own language and idioms — never paste thrum's Go examples
|
|
12
|
+
into a non-Go repo's philosophy.md.
|
|
13
|
+
|
|
14
|
+
## Class 1 — Injectable fault seam
|
|
15
|
+
|
|
16
|
+
**Consumers:** philosophy, hotpath (hot-path facet: per-request expensive-call)
|
|
17
|
+
**thrum source:** the VFS lesson: SQLite's OS layer is a swappable driver, so
|
|
18
|
+
every I/O failure mode is testable without touching a real disk.
|
|
19
|
+
|
|
20
|
+
**Definition:** A call into the OS, filesystem, or network that can fail
|
|
21
|
+
(process exec, file open/read/write, socket dial, DNS) is made directly from
|
|
22
|
+
business logic, with no seam through which a test can inject failure.
|
|
23
|
+
Production and test share one code path with zero swap point.
|
|
24
|
+
|
|
25
|
+
**Why it's expensive:** The only tested failure mode is "call succeeds."
|
|
26
|
+
Real failures (ENOSPC, EMFILE, a hung subprocess, a network partition) are
|
|
27
|
+
exercised for the first time in production, and the fix usually lands as an
|
|
28
|
+
ad hoc special case rather than a reusable fault-injection seam.
|
|
29
|
+
|
|
30
|
+
**Detection heuristic:** grep the language's process-exec, file-open, and
|
|
31
|
+
network-dial primitives; a hit directly inside a request-handler or
|
|
32
|
+
business-logic file (not an isolated adapter/driver/VFS-shaped module) is a
|
|
33
|
+
finding.
|
|
34
|
+
|
|
35
|
+
**Per-language probes:**
|
|
36
|
+
|
|
37
|
+
- Go: `grep -rn 'exec\.Command\|exec\.CommandContext\|os\.Open\|net\.Dial'
|
|
38
|
+
--include='*.go'`, excluding `*_test.go` and any package with
|
|
39
|
+
`driver`/`fault`/`vfs` in its path.
|
|
40
|
+
- Python: `grep -rn 'subprocess\.\(run\|Popen\|call\)\|open(\|socket\.\(connect\|create_connection\)'
|
|
41
|
+
--include='*.py'`
|
|
42
|
+
- JS/TS: `grep -rn 'child_process\.\(exec\|spawn\)\|fs\.\(readFile\|writeFile\)Sync\?\|net\.connect\|http\.request'
|
|
43
|
+
--include='*.ts' --include='*.js'`
|
|
44
|
+
- Rust: `grep -rn 'std::process::Command\|std::fs::File::open\|TcpStream::connect'
|
|
45
|
+
--include='*.rs'`
|
|
46
|
+
- Fallback: search the language's canonical process-spawn and low-level
|
|
47
|
+
file/socket primitives; a hit inside business logic rather than an
|
|
48
|
+
adapter module is the finding.
|
|
49
|
+
|
|
50
|
+
## Class 2 — Unrecovered async failure kills the host process
|
|
51
|
+
|
|
52
|
+
**Consumers:** philosophy, hotpath (hot-path facet: unrecovered-async-crash)
|
|
53
|
+
**thrum source:** new I/O work added inside a daemon boot stage or dispatch
|
|
54
|
+
handler with no recovery if that work panics/throws.
|
|
55
|
+
|
|
56
|
+
**Definition:** Work is moved onto a background thread/goroutine/async task
|
|
57
|
+
for latency reasons, but a panic, unhandled rejection, or unrecovered
|
|
58
|
+
exception on that path is not caught — it kills (or silently wedges) the
|
|
59
|
+
whole host process instead of failing just that one unit of work.
|
|
60
|
+
|
|
61
|
+
**Why it's expensive:** The failure is invisible until the specific input
|
|
62
|
+
that trips it arrives in production; the symptom (daemon down, service
|
|
63
|
+
restarted) points nowhere near the async call site that caused it.
|
|
64
|
+
|
|
65
|
+
**Detection heuristic:** every `go func`/async-task/background-thread spawn
|
|
66
|
+
on a request or boot path needs a recover/catch immediately inside it. A
|
|
67
|
+
spawn with no enclosing recover/catch in the same function is a finding.
|
|
68
|
+
|
|
69
|
+
**Per-language probes:**
|
|
70
|
+
|
|
71
|
+
- Go: `grep -rn 'go func' --include='*.go' -A3 | grep -B3 -L 'recover()'`
|
|
72
|
+
(spawns not immediately followed by a deferred recover)
|
|
73
|
+
- Python: `grep -rn 'asyncio\.create_task\|Thread(target=' --include='*.py'`
|
|
74
|
+
then check the target/coroutine body for a bare `try/except`
|
|
75
|
+
- JS/TS: `grep -rn 'setTimeout\|setImmediate\|\.then(\|async function'
|
|
76
|
+
--include='*.ts' --include='*.js'` then check for an unhandled-rejection
|
|
77
|
+
or `.catch()` immediately at the call site
|
|
78
|
+
- Rust: `grep -rn 'tokio::spawn\|std::thread::spawn' --include='*.rs'` then
|
|
79
|
+
check for `catch_unwind` or a supervised-restart wrapper
|
|
80
|
+
- Fallback: find the language's background-task/thread-spawn primitive; a
|
|
81
|
+
spawn with no local recovery wrapper is the finding.
|
|
82
|
+
|
|
83
|
+
## Class 3 — Destructive reconcile on fresh install (empty canonical is a wipe)
|
|
84
|
+
|
|
85
|
+
**Consumers:** philosophy only
|
|
86
|
+
|
|
87
|
+
**Definition:** A reconcile/sync/cleanup routine treats an EMPTY canonical
|
|
88
|
+
source (first boot, no state yet) the same as a canonical source that says
|
|
89
|
+
"delete everything" — so the very first run of the tool wipes whatever
|
|
90
|
+
pre-existing data it finds, because "nothing here yet" and "delete
|
|
91
|
+
everything" are indistinguishable to the code.
|
|
92
|
+
|
|
93
|
+
**Why it's expensive:** The bug only fires once per install, on the very
|
|
94
|
+
first run, against whatever real data happens to already be present — by
|
|
95
|
+
the time anyone notices, the data is gone and the repro is hard to
|
|
96
|
+
reconstruct.
|
|
97
|
+
|
|
98
|
+
**Detection heuristic:** find any routine that deletes/prunes/reconciles a
|
|
99
|
+
directory or table against a canonical list, and check whether an empty
|
|
100
|
+
canonical list is special-cased (hold/no-op) or falls through to "delete
|
|
101
|
+
everything not in the list."
|
|
102
|
+
|
|
103
|
+
**Per-language probes:**
|
|
104
|
+
|
|
105
|
+
- Go: `grep -rn 'os\.RemoveAll\|DROP TABLE\|DELETE FROM.*WHERE.*NOT IN'
|
|
106
|
+
--include='*.go'`
|
|
107
|
+
- Python: `grep -rn 'shutil\.rmtree\|os\.remove\|DELETE FROM.*NOT IN'
|
|
108
|
+
--include='*.py'`
|
|
109
|
+
- JS/TS: `grep -rn 'fs\.rm\(Sync\)\?\|rimraf\|DELETE FROM.*NOT IN'
|
|
110
|
+
--include='*.ts' --include='*.js'`
|
|
111
|
+
- Fallback: find the language's recursive-delete primitive and any
|
|
112
|
+
"reconcile against canonical list" SQL/logic; check for an explicit
|
|
113
|
+
empty-canonical guard.
|
|
114
|
+
|
|
115
|
+
## Class 4 — Guard-adequacy: presence verified, capability not
|
|
116
|
+
|
|
117
|
+
**Consumers:** philosophy only
|
|
118
|
+
|
|
119
|
+
**Definition:** A guard checks that something EXISTS (a file, a flag, a
|
|
120
|
+
config key, a permission record) but never checks that it actually WORKS or
|
|
121
|
+
grants the capability the caller assumes it grants. The guard passes on a
|
|
122
|
+
present-but-broken/present-but-insufficient state.
|
|
123
|
+
|
|
124
|
+
**Why it's expensive:** Code downstream of the guard trusts it fully, so a
|
|
125
|
+
present-but-inadequate state produces a confident wrong action instead of a
|
|
126
|
+
loud failure — the guard is worse than no guard because it suppresses the
|
|
127
|
+
obvious symptom.
|
|
128
|
+
|
|
129
|
+
**Detection heuristic:** find `if <thing> exists/present/!= nil` checks
|
|
130
|
+
immediately gating a privileged or destructive action; check whether the
|
|
131
|
+
check verifies capability (a successful no-op call, a real permission
|
|
132
|
+
check) or only presence.
|
|
133
|
+
|
|
134
|
+
**Per-language probes:**
|
|
135
|
+
|
|
136
|
+
- Go: `grep -rn 'if.*!= nil {' --include='*.go' -B2` near privileged
|
|
137
|
+
actions, cross-checked against whether the referenced value is ever
|
|
138
|
+
exercised (called) before use
|
|
139
|
+
- Python: `grep -rn 'if os\.path\.exists\|if.*is not None' --include='*.py'`
|
|
140
|
+
near privileged actions
|
|
141
|
+
- JS/TS: `grep -rn 'if\s*(\w+)\s*{' --include='*.ts' --include='*.js'` near
|
|
142
|
+
privileged actions
|
|
143
|
+
- Fallback: find presence-only guards (existence/non-null checks)
|
|
144
|
+
immediately preceding a privileged or destructive call, with no
|
|
145
|
+
accompanying capability check.
|
|
146
|
+
|
|
147
|
+
## Class 5 — Synchronous palliative vs. bounded async queue
|
|
148
|
+
|
|
149
|
+
**Consumers:** philosophy, hotpath (hot-path facets: shared-lock-across-I/O, unbounded-wait)
|
|
150
|
+
**thrum source:** the Hipp/SQLite lesson: remove a slow component rather than
|
|
151
|
+
patching around it with more synchronous work.
|
|
152
|
+
|
|
153
|
+
**Definition:** A shared serialization point (a global lock, a single
|
|
154
|
+
writer connection, a single dispatch queue) is under contention, and the
|
|
155
|
+
fix adds MORE synchronous work inside that same critical section (a retry
|
|
156
|
+
loop, a longer timeout, a synchronous fan-out call) instead of moving the
|
|
157
|
+
work off the serialization point onto a bounded async queue.
|
|
158
|
+
|
|
159
|
+
**Why it's expensive:** Each palliative fix narrows the failure window
|
|
160
|
+
without removing the root cause, so contention resurfaces under slightly
|
|
161
|
+
higher load, and the critical section keeps growing — eventually turning a
|
|
162
|
+
brief lock hold into an unbounded wait that stalls every caller behind it.
|
|
163
|
+
|
|
164
|
+
**Detection heuristic:** find a lock/mutex/single-connection acquisition
|
|
165
|
+
immediately followed by I/O (network call, subprocess, blocking file op)
|
|
166
|
+
inside the same critical section, or a synchronous call on a
|
|
167
|
+
request/dispatch path with no timeout/circuit breaker.
|
|
168
|
+
|
|
169
|
+
**Per-language probes:**
|
|
170
|
+
|
|
171
|
+
- Go: `grep -rn '\.Lock()\|\.RLock()' --include='*.go' -A10` then check
|
|
172
|
+
whether a network/exec/blocking call appears before the matching
|
|
173
|
+
`Unlock()`; separately `grep -rn 'context\.WithTimeout\|context\.WithDeadline'
|
|
174
|
+
--include='*.go'` to check whether peer/network calls on a dispatch path
|
|
175
|
+
have one
|
|
176
|
+
- Python: `grep -rn 'with.*[Ll]ock' --include='*.py' -A10` for I/O inside
|
|
177
|
+
the block; `grep -rn 'requests\.\(get\|post\)\|socket\.' --include='*.py'`
|
|
178
|
+
for missing `timeout=`
|
|
179
|
+
- JS/TS: `grep -rn 'await mutex\.\|\.acquire()' --include='*.ts'
|
|
180
|
+
--include='*.js' -A10` for I/O inside the held span
|
|
181
|
+
- Fallback: find the language's lock-acquisition primitive; check whether
|
|
182
|
+
I/O happens while held, and whether outbound calls on a hot path carry an
|
|
183
|
+
explicit timeout.
|
|
184
|
+
|
|
185
|
+
## Class 6 — Controlled-zero: a green check that cannot come back negative
|
|
186
|
+
|
|
187
|
+
**Consumers:** philosophy only (universal review discipline, not a hot-path lens)
|
|
188
|
+
**thrum source:** universal review discipline, generalized from thrum's own
|
|
189
|
+
merge-gate meta-checks.
|
|
190
|
+
|
|
191
|
+
**Definition:** A test, health check, or CI gate that reports "clean" or
|
|
192
|
+
"zero findings" whether or not it actually ran — an empty result set is
|
|
193
|
+
indistinguishable from "nothing wrong" and "the check silently didn't
|
|
194
|
+
execute" (wrong filter, empty fixture, mocked-away dependency, a
|
|
195
|
+
`--dry-run` flag left on).
|
|
196
|
+
|
|
197
|
+
**Why it's expensive:** Confidence in the check compounds — a green check
|
|
198
|
+
that cannot come back negative is worse than no check, because it is relied
|
|
199
|
+
upon precisely where a real problem would otherwise have been caught.
|
|
200
|
+
|
|
201
|
+
**Detection heuristic:** for any assertion-free or count-based check (a
|
|
202
|
+
test asserting only "no error," a CI step asserting only exit code 0), ask
|
|
203
|
+
whether there is a companion negative control proving the check COULD have
|
|
204
|
+
failed (a deliberately broken fixture, an invented-symbol probe).
|
|
205
|
+
|
|
206
|
+
**Per-language probes:**
|
|
207
|
+
|
|
208
|
+
- Go: `grep -rln 'require\.NoError(t, err)$\|assert\.NoError(t, err)$'
|
|
209
|
+
--include='*_test.go'` (assertion-free tests: NoError with no
|
|
210
|
+
accompanying state/equality assertion)
|
|
211
|
+
- Python: `grep -rn 'assert.*is None\|# no assert' --include='test_*.py'`
|
|
212
|
+
- JS/TS: `grep -rn 'expect(.*\.not\.toThrow()' --include='*.test.ts'`
|
|
213
|
+
- Fallback: find tests/checks whose only assertion is "no error raised,"
|
|
214
|
+
with no accompanying positive assertion on state, and no adjacent
|
|
215
|
+
negative-control test.
|
|
216
|
+
|
|
217
|
+
## Class 7 — Crash/power-loss recovery and migration against real old data
|
|
218
|
+
|
|
219
|
+
**Consumers:** philosophy only
|
|
220
|
+
**thrum source:** the SQLite core disciplines (Hipp/SQLite talk).
|
|
221
|
+
|
|
222
|
+
**Definition:** A durable store's crash-recovery path (WAL replay, journal
|
|
223
|
+
recovery, migration-on-boot) is tested only against synthetic in-memory
|
|
224
|
+
fixtures created by the current code version — never against a real
|
|
225
|
+
on-disk snapshot from an OLDER schema version, and never by actually
|
|
226
|
+
killing the process mid-write and restarting it.
|
|
227
|
+
|
|
228
|
+
**Why it's expensive:** The disciplines that matter (does recovery
|
|
229
|
+
converge on a truly random torn write? does a migration handle a customer's
|
|
230
|
+
real months-old on-disk state, not a freshly-created one?) are exactly the
|
|
231
|
+
ones a synthetic in-code fixture cannot exercise — bugs here surface only
|
|
232
|
+
against a real customer's real old data, in production.
|
|
233
|
+
|
|
234
|
+
**Detection heuristic:** find the migration/recovery entrypoint (schema
|
|
235
|
+
version check, WAL/journal replay on boot) and check whether any test kills
|
|
236
|
+
the process mid-write (not just simulates a corrupt file) and whether any
|
|
237
|
+
test runs a migration against a captured real on-disk snapshot rather than
|
|
238
|
+
a freshly-created fixture.
|
|
239
|
+
|
|
240
|
+
**Per-language probes:**
|
|
241
|
+
|
|
242
|
+
- Go: `grep -rn 'CurrentVersion\|migrat\|wal_checkpoint\|journal_mode'
|
|
243
|
+
--include='*.go'`, then `grep -rln 'os\.Kill\|SIGKILL\|process\.Kill'
|
|
244
|
+
--include='*_test.go'` for mid-write kill tests
|
|
245
|
+
- Python: `grep -rn 'alembic\|migration' --include='*.py'`, then check test
|
|
246
|
+
fixtures for a captured real DB snapshot vs. a freshly-created one
|
|
247
|
+
- Node/TS: `grep -rn 'migrate\|knex\.' --include='*.ts'`, same fixture check
|
|
248
|
+
- Fallback: find the migration/recovery entrypoint; check for a
|
|
249
|
+
mid-write-kill test and a real-old-snapshot migration test, not just
|
|
250
|
+
synthetic fixtures.
|
|
251
|
+
|
|
252
|
+
## Class 8 — Silent fail-open
|
|
253
|
+
|
|
254
|
+
**Consumers:** hotpath
|
|
255
|
+
**thrum source:** hotpath lens `silent_fail_open` (Step 5,
|
|
256
|
+
`project-hotpath-gate/SKILL.md`) — fallback/default-return patterns that
|
|
257
|
+
mask a missing or invalid dependency instead of failing loudly.
|
|
258
|
+
|
|
259
|
+
**Definition:** A code path that should fail when a required dependency,
|
|
260
|
+
credential, or lookup is missing instead falls back to a default value (a
|
|
261
|
+
default credential, a default user, an empty-but-valid-looking result) with
|
|
262
|
+
no log line or error surfaced to the caller.
|
|
263
|
+
|
|
264
|
+
**Why it's expensive:** The failure never announces itself — the system
|
|
265
|
+
keeps running on a silently-substituted default, and the divergence between
|
|
266
|
+
"the real thing" and "the fallback" only becomes visible once its
|
|
267
|
+
downstream effects (wrong data, wrong permissions) are noticed far from the
|
|
268
|
+
fallback site.
|
|
269
|
+
|
|
270
|
+
**Detection heuristic:** find `fallback`/`default`-shaped branches
|
|
271
|
+
immediately following a lookup, existence check, or dependency-load
|
|
272
|
+
attempt; a branch that returns a default value with no accompanying log/
|
|
273
|
+
error/metric emission is a finding.
|
|
274
|
+
|
|
275
|
+
**Per-language probes:**
|
|
276
|
+
|
|
277
|
+
- Go: `grep -rn 'fallback\|FallBack\|if !exists\|default.*return'
|
|
278
|
+
--include='*.go'`
|
|
279
|
+
- Python: `grep -rn 'except:\s*pass\|\.get(\w+,\s*default_' --include='*.py'`
|
|
280
|
+
(bare `except: pass` swallowing an error, or a `.get(k, default_creds)`
|
|
281
|
+
shaped silent substitution)
|
|
282
|
+
- JS/TS: `grep -rn 'catch\s*{\s*}\|??\s*default' --include='*.ts'
|
|
283
|
+
--include='*.js'` (an empty catch block, or a `??` fallback onto a
|
|
284
|
+
default-shaped value with no logging)
|
|
285
|
+
- Fallback: find lookup/dependency-load call sites; check whether the
|
|
286
|
+
failure branch logs/surfaces the failure or silently substitutes a
|
|
287
|
+
default.
|
|
288
|
+
|
|
289
|
+
## Class 9 — Unconditional write on a dual-source path
|
|
290
|
+
|
|
291
|
+
**Consumers:** hotpath
|
|
292
|
+
**thrum source:** hotpath lens for dual-source write paths (Step 6,
|
|
293
|
+
`project-hotpath-gate/SKILL.md`) — unconditional writes with no
|
|
294
|
+
last-writer-wins or version guard.
|
|
295
|
+
|
|
296
|
+
**Definition:** A shared record is writable from two or more independent
|
|
297
|
+
sources (an API path and a webhook/sync path, a UI edit and a background
|
|
298
|
+
reconciler), and at least one of those write paths performs an
|
|
299
|
+
unconditional upsert/replace with no last-writer-wins timestamp check or
|
|
300
|
+
version guard — so whichever source writes last silently wins, even when
|
|
301
|
+
it holds stale data.
|
|
302
|
+
|
|
303
|
+
**Why it's expensive:** The two write paths race under real concurrent
|
|
304
|
+
load, and the bug only manifests as an occasional stale overwrite —
|
|
305
|
+
intermittent, hard to reproduce, and easy to misdiagnose as a caching issue
|
|
306
|
+
rather than a missing guard.
|
|
307
|
+
|
|
308
|
+
**Detection heuristic:** find upsert/replace statements on a path reachable
|
|
309
|
+
from more than one caller/trigger, and check whether a timestamp or version
|
|
310
|
+
comparison gates the write (vs. an unconditional `INSERT OR REPLACE`-shaped
|
|
311
|
+
statement).
|
|
312
|
+
|
|
313
|
+
**Per-language probes:**
|
|
314
|
+
|
|
315
|
+
- Go: `grep -rn 'INSERT OR REPLACE\|INSERT OR IGNORE' --include='*.go'` for
|
|
316
|
+
the unconditional write; cross-check against `grep -rn
|
|
317
|
+
'lww\.\|GuardSQL\|GuardTarget' --include='*.go'` for an adjacent guard —
|
|
318
|
+
a write-site hit with no guard hit nearby is the finding.
|
|
319
|
+
- Python: `grep -rn 'ON CONFLICT DO UPDATE\|session\.merge(' --include='*.py'`
|
|
320
|
+
with no accompanying version/timestamp comparison in the same
|
|
321
|
+
transaction.
|
|
322
|
+
- JS/TS: `grep -rn 'upsert(' --include='*.ts' --include='*.js'` on a route
|
|
323
|
+
reachable from both an API handler and a webhook handler, with no
|
|
324
|
+
`updatedAt`/version guard in the upsert's `where`/conflict clause.
|
|
325
|
+
- Fallback: find upsert/replace call sites reachable from more than one
|
|
326
|
+
caller; check for a timestamp or version guard gating the write.
|
|
327
|
+
|
|
328
|
+
## Class 10 — Shared-resource poisoning
|
|
329
|
+
|
|
330
|
+
**Consumers:** hotpath
|
|
331
|
+
**thrum source:** hotpath lens `shared_resource_poisoning` (Step 7,
|
|
332
|
+
`project-hotpath-gate/SKILL.md`) — a read-only worker or side process
|
|
333
|
+
performing writes/maintenance against a resource other callers assume is
|
|
334
|
+
read-only or externally managed.
|
|
335
|
+
|
|
336
|
+
**Definition:** A process or code path documented or assumed to be
|
|
337
|
+
read-only (a read replica, a reporting worker, a "just reads" script)
|
|
338
|
+
actually performs writes, schema changes, or maintenance operations (a
|
|
339
|
+
checkpoint, a VACUUM, a lock-escalating write) against the shared resource
|
|
340
|
+
— poisoning it for every other consumer that assumed read-only access was
|
|
341
|
+
safe to run concurrently.
|
|
342
|
+
|
|
343
|
+
**Why it's expensive:** Every other caller's concurrency assumptions were
|
|
344
|
+
built on that resource being safely shareable for reads; a write or
|
|
345
|
+
maintenance op from the "read-only" side blows up those assumptions
|
|
346
|
+
without warning, producing lock contention or corruption that shows up in
|
|
347
|
+
an unrelated caller.
|
|
348
|
+
|
|
349
|
+
**Detection heuristic:** find DB/file-open calls in code paths labeled or
|
|
350
|
+
assumed read-only, and check whether any write or maintenance statement
|
|
351
|
+
(checkpoint, VACUUM, PRAGMA, write query) appears in the same process.
|
|
352
|
+
|
|
353
|
+
**Per-language probes:**
|
|
354
|
+
|
|
355
|
+
- Go: `grep -rn 'OpenDB\|OpenReadDB\|sql\.Open\|sql\.OpenDB'
|
|
356
|
+
--include='*.go'` for the open call, cross-checked against `grep -rn
|
|
357
|
+
'wal_checkpoint\|PRAGMA\|checkpoint\|Checkpoint' --include='*.go'` in the
|
|
358
|
+
same file/package for a write/maintenance op reachable from a
|
|
359
|
+
read-labeled path.
|
|
360
|
+
- Python: `grep -rn 'sqlite3\.connect\|open(.*[\'"]r[\'"]' --include='*.py'`
|
|
361
|
+
in a worker/script documented as read-only, cross-checked for `.execute(
|
|
362
|
+
' *(INSERT|UPDATE|DELETE|VACUUM)` in the same module.
|
|
363
|
+
- JS/TS: `grep -rn 'fs\.open\|db\.prepare' --include='*.ts' --include='*.js'`
|
|
364
|
+
in a script/worker documented as read-only, cross-checked for a write
|
|
365
|
+
call (`fs.write`, an `INSERT`/`UPDATE`/`DELETE`/`VACUUM` statement) in the
|
|
366
|
+
same file.
|
|
367
|
+
- Fallback: find the resource-open call in a read-only-labeled process;
|
|
368
|
+
check whether any write or maintenance statement is reachable from the
|
|
369
|
+
same process.
|
|
370
|
+
|
|
371
|
+
## Class 11 — Pattern divergence from the repo's own reference implementation
|
|
372
|
+
|
|
373
|
+
**Consumers:** hotpath
|
|
374
|
+
**thrum source:** hotpath lens for reference-pattern detection (Step 8,
|
|
375
|
+
`project-hotpath-gate/SKILL.md`) — the repo already has an established,
|
|
376
|
+
correct way to do something; some call sites diverge from it.
|
|
377
|
+
|
|
378
|
+
**Definition:** The repo has an established/reference implementation of a
|
|
379
|
+
shared pattern (e.g. thrum's `net.Listen`-followed-by-`Serve()` vs. a
|
|
380
|
+
listener never served, `OpenReadDB` vs. raw `sql.Open`, a typed
|
|
381
|
+
`safehandler.SafeHandler` vs. legacy `json.RawMessage` handling) — and one
|
|
382
|
+
or more call sites use an older/legacy/ad hoc variant of the same operation
|
|
383
|
+
instead of the current pattern, usually because the legacy variant predates
|
|
384
|
+
the reference implementation and was never migrated.
|
|
385
|
+
|
|
386
|
+
**Why it's expensive:** The legacy variant misses whatever correctness or
|
|
387
|
+
safety property motivated the reference implementation in the first place
|
|
388
|
+
(a missed `Serve()` call, a missing typed-validation layer, a bypassed
|
|
389
|
+
guard) — and because it still compiles and runs, nothing forces the
|
|
390
|
+
migration; the divergence persists indefinitely as a live footgun sitting
|
|
391
|
+
next to its own fix.
|
|
392
|
+
|
|
393
|
+
**Detection heuristic:** identify the established/reference implementation
|
|
394
|
+
of a shared pattern in the repo (the version most call sites use, or the
|
|
395
|
+
one with a tripwire test enforcing it), then find call sites performing the
|
|
396
|
+
same operation via a different, non-canonical route. Go: illustrative
|
|
397
|
+
categories from `project-hotpath-gate/SKILL.md` Step 8 — listener-serve
|
|
398
|
+
pairing, read-only-DB access, LWW guard usage, canonical liveness
|
|
399
|
+
predicate, typed vs. raw handler dispatch — are examples of the pattern
|
|
400
|
+
class, not a single literal grep; identify the repo's own reference
|
|
401
|
+
pattern for each category before searching for divergent call sites.
|
|
402
|
+
|
|
403
|
+
**Per-language probes:**
|
|
404
|
+
|
|
405
|
+
- Go: no single grep — per `project-hotpath-gate/SKILL.md` Step 8, identify
|
|
406
|
+
the repo's reference pattern per category (e.g. `grep -rn 'net\.Listen'
|
|
407
|
+
--include='*.go'` cross-checked for a following `Serve()`/`Accept()`
|
|
408
|
+
call; `grep -rn 'OpenReadDB\|sql\.Open' --include='*.go'` to find both
|
|
409
|
+
the canonical and legacy DB-open routes) and diff call sites against it.
|
|
410
|
+
- Python: repo idiom probe — find the repo's own established retry
|
|
411
|
+
decorator (e.g. `grep -rn '@retry' --include='*.py'`), then `grep -rn
|
|
412
|
+
'while True:.*try:\|for _ in range(' --include='*.py'` for raw/ad hoc
|
|
413
|
+
retry loops that should have used the decorator instead.
|
|
414
|
+
- JS/TS: apiClient vs. raw fetch — find the repo's established API-client
|
|
415
|
+
wrapper (e.g. `grep -rln 'class ApiClient\|export.*apiClient'
|
|
416
|
+
--include='*.ts'`), then `grep -rn 'fetch(\|axios\.' --include='*.ts'
|
|
417
|
+
--include='*.js'` for call sites bypassing it with a raw `fetch`/`axios`
|
|
418
|
+
call.
|
|
419
|
+
- Fallback: identify the repo's own reference implementation for a shared
|
|
420
|
+
operation (the version most call sites use, or the one with a tripwire
|
|
421
|
+
test); grep for call sites performing the same operation via a different
|
|
422
|
+
route.
|
|
423
|
+
|
|
424
|
+
## Class 12 — Coincidence-detector tests on the hot-path test suite
|
|
425
|
+
|
|
426
|
+
**Consumers:** hotpath
|
|
427
|
+
**thrum source:** hotpath lens `assertion_free_patterns` (Step 9,
|
|
428
|
+
`project-hotpath-gate/SKILL.md`) — same underlying shape as Class 6
|
|
429
|
+
("Controlled-zero") in this library, applied specifically to the hot-path
|
|
430
|
+
test suite rather than as a general review discipline.
|
|
431
|
+
|
|
432
|
+
**Definition:** A test on the hot-path test suite asserts only "no error
|
|
433
|
+
raised" (`require.NoError`/`assert.NoError` with no accompanying
|
|
434
|
+
state/equality assertion, an `assert-free` test body, a sole `.not.toThrow()`
|
|
435
|
+
expectation) with no assertion on the actual resulting state — see Class 6
|
|
436
|
+
for the general shape; this entry targets hot-path-adjacent tests
|
|
437
|
+
specifically, since a coincidence detector guarding a request-handling or
|
|
438
|
+
concurrency-sensitive path is more expensive to leave undetected than one
|
|
439
|
+
guarding an unrelated code path.
|
|
440
|
+
|
|
441
|
+
**Why it's expensive:** A hot-path regression (a race, a silent fallback, a
|
|
442
|
+
dropped write) passes the suite because the test never checked the state
|
|
443
|
+
the regression would have corrupted — the team's confidence that the hot
|
|
444
|
+
path is covered is unearned exactly where the cost of being wrong is
|
|
445
|
+
highest.
|
|
446
|
+
|
|
447
|
+
**Detection heuristic:** for each test file touching a hot-path
|
|
448
|
+
handler/lens area, check whether its assertions include a state/equality
|
|
449
|
+
check or only a bare no-error check.
|
|
450
|
+
|
|
451
|
+
**Per-language probes:**
|
|
452
|
+
|
|
453
|
+
- Go: `grep -rn 'require\.NoError\|assert\.NoError' --include='*_test.go'`
|
|
454
|
+
scoped to hot-path test files, then check whether the same test function
|
|
455
|
+
has any accompanying state/equality assertion.
|
|
456
|
+
- Python: `grep -rln 'assert-free' 'test_*.py'` — a Python test file with
|
|
457
|
+
no `assert` beyond error-absence checks (e.g. no `assertEqual`/`assert
|
|
458
|
+
result ==`), scoped to hot-path-adjacent test modules.
|
|
459
|
+
- JS/TS: `grep -rn 'expect(.*\.not\.toThrow()' --include='*.test.ts'` as
|
|
460
|
+
the sole assertion in a hot-path-adjacent test.
|
|
461
|
+
- Fallback: find tests covering hot-path handlers/lenses whose only
|
|
462
|
+
assertion is "no error raised," with no accompanying state assertion.
|
|
463
|
+
|
|
464
|
+
## Class 13 — Incomplete class fix: sibling writers left unfixed
|
|
465
|
+
|
|
466
|
+
**Consumers:** hotpath
|
|
467
|
+
**thrum source:** hotpath lens for sibling-resource enumeration (Step 10,
|
|
468
|
+
`project-hotpath-gate/SKILL.md`) — a fix applied to one writer/handler of a
|
|
469
|
+
shared resource while sibling writers of the same resource type were never
|
|
470
|
+
enumerated or checked.
|
|
471
|
+
|
|
472
|
+
**Definition:** A bug fix (a guard added, a race closed, a validation
|
|
473
|
+
tightened) is applied to one call site that writes a shared resource type
|
|
474
|
+
(a projection writer, a vault/identity file, a network listener, a DB open
|
|
475
|
+
call) — but the codebase has other call sites writing the same resource
|
|
476
|
+
type that were never enumerated during the fix, and so never received the
|
|
477
|
+
equivalent fix.
|
|
478
|
+
|
|
479
|
+
**Why it's expensive:** The class of bug is not actually closed — only the
|
|
480
|
+
one instance that happened to be noticed is. The sibling call sites carry
|
|
481
|
+
the identical defect and will surface it later, independently, looking
|
|
482
|
+
like a "new" bug rather than the same one recurring, wasting a second
|
|
483
|
+
investigation on a problem already solved once.
|
|
484
|
+
|
|
485
|
+
**Detection heuristic:** when reviewing a fix to a shared-resource writer,
|
|
486
|
+
enumerate every other call site of the same resource type (via the
|
|
487
|
+
resource's canonical open/write primitive) and check whether each one
|
|
488
|
+
received the equivalent guard/fix.
|
|
489
|
+
|
|
490
|
+
**Per-language probes:**
|
|
491
|
+
|
|
492
|
+
- Go: `grep -rn 'func apply.*Tx' --include='*.go'` (projection writers),
|
|
493
|
+
`grep -rn 'vault\.\|Vault\|loadIdentity' --include='*.go'`
|
|
494
|
+
(vault/identity writers), `grep -rn 'net\.Listen\|net\.FileListener'
|
|
495
|
+
--include='*.go'` (listeners), `grep -rn
|
|
496
|
+
'schema\.OpenDB\|schema\.OpenReadDB\|sql\.Open' --include='*.go'` (DB
|
|
497
|
+
opens) — enumerate all hits per resource type and diff against which
|
|
498
|
+
ones received the fix.
|
|
499
|
+
- Python: enumerate all writers across `*.py` — `grep -rn
|
|
500
|
+
'\.execute(\|\.commit(\|session\.add(' --include='*.py'` scoped to the
|
|
501
|
+
same resource/table the fix targeted, then check each hit for the
|
|
502
|
+
equivalent fix.
|
|
503
|
+
- JS/TS: enumerate all save/write across `*.ts` — `grep -rn
|
|
504
|
+
'\.save(\|\.update(\|\.write(' --include='*.ts' --include='*.js'` scoped
|
|
505
|
+
to the same resource the fix targeted, then check each hit for the
|
|
506
|
+
equivalent fix.
|
|
507
|
+
- Fallback: find the resource's canonical open/write primitive; enumerate
|
|
508
|
+
every call site; check each one for the fix that was applied to the
|
|
509
|
+
originally-reported site.
|
|
510
|
+
|
|
511
|
+
## Class 14 — Dead-row pre-filter: a weak liveness proxy feeding per-row cost
|
|
512
|
+
|
|
513
|
+
**Consumers:** hotpath
|
|
514
|
+
**thrum source:** hotpath lens `dead_row_prefilter` (Step 10a,
|
|
515
|
+
`project-hotpath-gate/SKILL.md`) — a candidate query using a weak liveness
|
|
516
|
+
proxy instead of the canonical liveness predicate, feeding dead rows into a
|
|
517
|
+
per-row-expensive operation.
|
|
518
|
+
|
|
519
|
+
**Definition:** A query selecting candidate rows for a per-row-expensive
|
|
520
|
+
operation (an N+1 lookup, a per-row filesystem/subprocess check) filters on
|
|
521
|
+
a weak proxy for "is this row still live" (a nullable timestamp column, a
|
|
522
|
+
loosely-named status flag) instead of the repo's canonical liveness
|
|
523
|
+
predicate — so rows that are actually dead by the canonical definition
|
|
524
|
+
still pass the pre-filter and pay the expensive per-row cost anyway.
|
|
525
|
+
|
|
526
|
+
**Why it's expensive:** The per-row cost this pre-filter exists to avoid
|
|
527
|
+
still gets paid, just for a population that should have been excluded —
|
|
528
|
+
the pre-filter creates a false sense that the expensive path is already
|
|
529
|
+
bounded, when it is only bounded by an approximation that under-filters.
|
|
530
|
+
|
|
531
|
+
**Detection heuristic:** find queries selecting candidates for a per-row
|
|
532
|
+
loop and check whether the WHERE/filter clause uses the canonical liveness
|
|
533
|
+
predicate (a function or well-defined phase set) or a weaker ad hoc proxy
|
|
534
|
+
(nullable-timestamp check, loosely-named boolean).
|
|
535
|
+
|
|
536
|
+
**Per-language probes:**
|
|
537
|
+
|
|
538
|
+
- Go: `grep -rn 'ended_at IS NULL\|session.*not.*closed\|phase IS NULL'
|
|
539
|
+
--include='*.go'`
|
|
540
|
+
- Python: N+1 ORM loop without `select_related`/`prefetch_related` — `grep
|
|
541
|
+
-rn 'for \w+ in \w+\.objects\.\(all\|filter\)(' --include='*.py'` with no
|
|
542
|
+
accompanying `select_related(`/`prefetch_related(` on the same
|
|
543
|
+
queryset.
|
|
544
|
+
- JS/TS: N+1 for-loop issuing per-item queries — `grep -rn 'for\s*(.*of.*)\s*{'
|
|
545
|
+
--include='*.ts' --include='*.js' -A5` checked for an `await db.query(`
|
|
546
|
+
or equivalent per-row call inside the loop body instead of a single
|
|
547
|
+
batched query.
|
|
548
|
+
- Fallback: find the candidate-selection query feeding a per-row-expensive
|
|
549
|
+
loop; check whether its filter uses the repo's canonical liveness
|
|
550
|
+
predicate or a weaker ad hoc proxy.
|
|
551
|
+
|
|
552
|
+
## Class 15 — Frozen identity key: a shared primitive whose output is persisted
|
|
553
|
+
|
|
554
|
+
**Consumers:** hotpath
|
|
555
|
+
**thrum source:** hotpath lens `frozen_identity_key` (Step 10c,
|
|
556
|
+
`project-hotpath-gate/SKILL.md`) — a shared normalizer/hash primitive whose
|
|
557
|
+
output is persisted or compared as a licensing/identity key, so changing
|
|
558
|
+
the primitive silently breaks every previously-persisted key.
|
|
559
|
+
|
|
560
|
+
**Definition:** A shared normalization or hashing primitive (a URL
|
|
561
|
+
normalizer, a stable-ID generator, a `sha256.Sum`-shaped function) has its
|
|
562
|
+
output persisted to disk, embedded in a cache/DB key, or compared for
|
|
563
|
+
exact equality against a stored licensing/identity value downstream. The
|
|
564
|
+
finding is the PAIR — the primitive plus a persisted/licensing consumer of
|
|
565
|
+
its output — not the primitive alone; a normalizer with no persisted
|
|
566
|
+
consumer is not a candidate for this class.
|
|
567
|
+
|
|
568
|
+
**Why it's expensive:** Once a primitive's output is persisted anywhere,
|
|
569
|
+
the primitive is effectively frozen — any change to its normalization
|
|
570
|
+
logic (a bug fix, a new edge case handled) silently invalidates every
|
|
571
|
+
previously-persisted key, and the failure mode (a returning user's license
|
|
572
|
+
no longer matching, a stale identity mismatch) surfaces far from the
|
|
573
|
+
primitive that changed and long after the change shipped.
|
|
574
|
+
|
|
575
|
+
**Detection heuristic:** find normalizer/hash primitives whose result
|
|
576
|
+
feeds a persisted value (a saved file, a DB column, a cache key, a JWT
|
|
577
|
+
claim) or an exact-equality comparison against a stored value; check
|
|
578
|
+
whether the primitive's contract is documented as frozen/versioned, and
|
|
579
|
+
whether any test pins its output against a fixed input to catch accidental
|
|
580
|
+
drift.
|
|
581
|
+
|
|
582
|
+
**Per-language probes:**
|
|
583
|
+
|
|
584
|
+
- Go: `grep -rn 'GenerateRepoID\|NormalizeGitURL\|stableRepoAccount\|sha256\.Sum'
|
|
585
|
+
--include='*.go'` for the primitive, cross-checked against `grep -rn
|
|
586
|
+
'SaveIdentityFile\|identities/.*\.json\|license\.\|GrantsOSS'
|
|
587
|
+
--include='*.go'` for a persisted/licensing consumer downstream.
|
|
588
|
+
- Python: normalizer → disk/cache/DB key — `grep -rn 'def normalize_\|
|
|
589
|
+
hashlib\.sha256' --include='*.py'` for the primitive, cross-checked
|
|
590
|
+
against `grep -rn 'pickle\.dump\|redis\.set\|\.save(' --include='*.py'`
|
|
591
|
+
for a persisted consumer of its output.
|
|
592
|
+
- JS/TS: hash → localStorage/DB key/JWT — `grep -rn
|
|
593
|
+
'crypto\.createHash\|normalize' --include='*.ts' --include='*.js'` for
|
|
594
|
+
the primitive, cross-checked against `grep -rn
|
|
595
|
+
'localStorage\.setItem\|jwt\.sign\|\.save(' --include='*.ts'
|
|
596
|
+
--include='*.js'` for a persisted consumer.
|
|
597
|
+
- Fallback: find normalizer/hash primitives; check whether their output
|
|
598
|
+
feeds a persisted value or licensing comparison downstream, and whether
|
|
599
|
+
a pinned-output test guards against accidental drift.
|
|
600
|
+
|
|
601
|
+
## Class 16 — Unauthenticated hot-path route
|
|
602
|
+
|
|
603
|
+
**Consumers:** hotpath
|
|
604
|
+
|
|
605
|
+
**Definition:** A request-handling route on a hot path — one reachable by an external, unauthenticated caller off the box (a customer-facing HTTP/RPC endpoint) — has no authentication or authorization check in its call chain before it touches business logic or data.
|
|
606
|
+
|
|
607
|
+
**Why it's expensive:** An externally-reachable handler with no auth check is a direct path to data or actions the caller was never granted. This is scoped narrowly on purpose: it applies only to a route that is actually reachable by an unauthenticated caller off the box — an internal-only route (behind internal-network middleware, admin-only binding, loopback-only listener), or a deliberately open endpoint (health check, static asset, login/signup route itself), is not a finding. A local/internal-trust surface is not the same threat as an externally-reachable one. This distinction is also why thrum's own hot-path handlers get **no probe for this class in Go** (see below) — thrum's daemon RPC surface sits behind an mTLS + pairing perimeter that already authenticates every non-loopback caller, so "route lacking auth" is not a coherent finding against thrum's own hot path. Do not import that exemption into a customer repo: a typical Flask/Express/Go web service has no equivalent perimeter, and a route handler in its public API is presumptively externally reachable unless there is clear evidence of an internal-only guard (internal-network middleware, admin-only bind address, an explicit auth-exempt allowlist).
|
|
608
|
+
|
|
609
|
+
**Detection heuristic:** find route/handler registrations on the hot path (per the target repo's routing framework) and check whether an auth decorator/middleware/dependency guard appears in the handler's own chain (not just declared somewhere in the app and never wired to this route). A route with no such guard, and no adjacent evidence that it is internal-only or intentionally public, is a finding.
|
|
610
|
+
|
|
611
|
+
**Per-language probes:**
|
|
612
|
+
|
|
613
|
+
- Go: none — thrum's own hot-path handlers sit behind an mTLS perimeter that already authenticates every non-loopback caller; this concept targets repos with no equivalent perimeter. Emit no Go probe.
|
|
614
|
+
- Python: `grep -rn '@app\.\(route\|get\|post\|put\|delete\|patch\)\|@router\.\(get\|post\|put\|delete\|patch\)' --include='*.py'`, then for each match check the surrounding function/decorator stack for `@requires_auth`, `@login_required`, or a `Depends(verify_token)`/`Depends(get_current_user)`-shaped FastAPI dependency; a route with none of these, and no `# public`/`# internal-only` marker, is a finding.
|
|
615
|
+
- JS/TS: `grep -rn "app\.\(get\|post\|put\|delete\|patch\)(\|router\.\(get\|post\|put\|delete\|patch\)(" --include='*.ts' --include='*.js'`, then check whether an auth middleware (e.g. `requireAuth`, `passport.authenticate`, `verifyToken`) appears in the same handler's middleware chain before the handler body.
|
|
616
|
+
- Rust: no probe defined for this concept yet.
|
|
617
|
+
- Fallback: find the target language's route/handler registration primitive; check whether an auth guard (middleware, decorator, dependency-injection check) sits in that specific handler's chain, and treat internal-only-bound or explicitly-public routes as out of scope rather than findings.
|
|
618
|
+
|
|
619
|
+
## Class 17 — Secret or token value flows into a log
|
|
620
|
+
|
|
621
|
+
**Consumers:** hotpath
|
|
622
|
+
|
|
623
|
+
**Definition:** A hot-path handler logs or prints a request object, auth header, bearer token, API key, or other secret-shaped value — directly or via a wholesale dump of headers/request state — rather than a redacted or field-scoped log line.
|
|
624
|
+
|
|
625
|
+
**Why it's expensive:** Logs are typically retained far longer than the request, read by a wider audience (log aggregators, on-call engineers, third-party log-shipping services) than the original caller intended, and rarely treated with the same access control as the primary datastore. A token or secret that lands in a log is effectively broadcast to every downstream consumer of that log stream, and the leak is invisible until someone greps the logs.
|
|
626
|
+
|
|
627
|
+
**Detection heuristic:** find log/print calls on the hot path and check whether their arguments include a whole request/headers object, or a variable named/typed like a token, secret, password, or API key, without an intervening redaction step.
|
|
628
|
+
|
|
629
|
+
**Per-language probes:**
|
|
630
|
+
|
|
631
|
+
- Go: none — whether thrum's own daemon logging should get a probe for this pattern is left for a future pass. Emit no Go probe.
|
|
632
|
+
- Python: `grep -rn 'log\(ger\)\?\.\(info\|debug\|warning\|error\)(.*\(token\|request\.headers\|authorization\|api_key\|secret\)\|print(.*\(token\|request\.headers\|authorization\)' --include='*.py' -i`
|
|
633
|
+
- JS/TS: `grep -rn 'console\.\(log\|error\|warn\|info\)(.*\(req\.headers\|token\|authorization\|apiKey\|secret\)' --include='*.ts' --include='*.js' -i`
|
|
634
|
+
- Rust: no probe defined for this concept yet.
|
|
635
|
+
- Fallback: find the language's logging/print primitives on the hot path; check whether any call site passes a whole headers/request object or a token/secret/password/api-key-named variable without a redaction step first.
|
|
636
|
+
|
|
637
|
+
## Class 18 — Hot-path handler or proxy loop with no concurrency cap
|
|
638
|
+
|
|
639
|
+
**Consumers:** hotpath
|
|
640
|
+
|
|
641
|
+
**Definition:** A hot-path handler or proxy/fan-out loop issues unbounded concurrent work (unbounded `Promise.all`, an unguarded per-request loop dialing out to N downstream calls, a proxy relay with no semaphore/limiter) with no cap on in-flight concurrency and no rate limiter protecting the entry point.
|
|
642
|
+
|
|
643
|
+
**Why it's expensive:** Without a concurrency cap, load on this path scales directly with caller-controlled fan-out (request volume, or the size of a caller-supplied list/batch) rather than with any server-chosen bound. A burst of legitimate traffic — or a single caller sending a large batch — can exhaust downstream connections, memory, or file descriptors, causing backpressure collapse for every other caller on the same process. This is a request-shape and resource-exhaustion concern, distinct from an out-of-scope on-path network adversary (ARP poisoning, packet drops): sizing a concurrency cap or rate limiter against ordinary caller-controlled load is in scope even where sizing against a network-level adversary is not.
|
|
644
|
+
|
|
645
|
+
**Detection heuristic:** find the hot-path handler or proxy/relay loop and check for (a) a rate limiter or throttle guarding the entry point, and (b) a concurrency cap (semaphore, worker pool, bounded queue) around any per-request fan-out to downstream calls. A loop or `Promise.all`-shaped fan-out with neither is a finding; a rate-limiter decorator/middleware that was present and has since been removed (a diff-visible regression) is also a finding.
|
|
646
|
+
|
|
647
|
+
**Per-language probes:**
|
|
648
|
+
|
|
649
|
+
- Go: none. Emit no Go probe.
|
|
650
|
+
- Python: `grep -rn 'for .* in .*:\s*$' --include='*.py' -A5` in proxy/relay-shaped modules, checked for an absent `Semaphore`/`BoundedSemaphore` around the loop body; separately `grep -rn '@limiter\.\(limit\|exempt\)\|@ratelimit' --include='*.py'` cross-checked via `git log -p` for a removed `@limiter` decorator on a hot-path route.
|
|
651
|
+
- JS/TS: `grep -rn 'Promise\.all(' --include='*.ts' --include='*.js'`, checked for whether the mapped array length is caller-controlled (request body/query param) and no chunking/`p-limit`/semaphore wraps it; separately `grep -rn 'rateLimit\|express-rate-limit\|limiter' --include='*.ts' --include='*.js'` to confirm a limiter exists on the entry point.
|
|
652
|
+
- Rust: no probe defined for this concept yet.
|
|
653
|
+
- Fallback: find the hot-path handler or proxy/fan-out loop; check for a rate limiter at the entry point and a concurrency cap (semaphore, bounded worker pool) around any per-request fan-out, and check version history for a removed limiter on that route.
|