fastify 6.0.0-alpha.0 → 6.0.0-alpha.2

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.
Files changed (46) hide show
  1. package/.pi/self-learning-memory/README.md +8 -0
  2. package/.pi/self-learning-memory/core/CORE.md +33 -0
  3. package/.pi/self-learning-memory/core/index.json +735 -0
  4. package/.pi/self-learning-memory/daily/2026-07-06.md +22 -0
  5. package/.pi/self-learning-memory/daily/2026-07-29.md +50 -0
  6. package/.pi/self-learning-memory/daily/2026-08-04.md +41 -0
  7. package/.pi/self-learning-memory/daily/2026-08-18.md +40 -0
  8. package/.pi/self-learning-memory/long-term-memory.md +89 -0
  9. package/AGENTS.md +290 -0
  10. package/docs/Reference/ContentTypeParser.md +0 -4
  11. package/docs/Reference/Reply.md +14 -3
  12. package/docs/Reference/Request.md +0 -4
  13. package/docs/Reference/Server.md +8 -3
  14. package/fastify.d.ts +1 -1
  15. package/fastify.js +2 -2
  16. package/lib/content-type.js +4 -1
  17. package/lib/reply.js +11 -1
  18. package/lib/request.js +3 -2
  19. package/lib/route.js +2 -1
  20. package/lib/validation.js +10 -1
  21. package/package.json +2 -2
  22. package/test/close.test.js +34 -0
  23. package/test/content-parser.test.js +16 -18
  24. package/test/content-type.test.js +4 -0
  25. package/test/fix-root-primitive-coercion.test.js +35 -0
  26. package/test/helper.js +73 -1
  27. package/test/hooks-async.test.js +1 -2
  28. package/test/hooks.test.js +1 -2
  29. package/test/internals/reply.test.js +76 -1
  30. package/test/listen.1.test.js +17 -52
  31. package/test/listen.5.test.js +6 -9
  32. package/test/logger/instantiation.test.js +1 -1
  33. package/test/logger/logging.test.js +1 -1
  34. package/test/logger/request.test.js +1 -1
  35. package/test/logger/response.test.js +1 -1
  36. package/test/reply-media-type.test.js +131 -0
  37. package/test/route-prefix.test.js +33 -1
  38. package/test/route.6.test.js +4 -11
  39. package/test/schema-feature.test.js +36 -65
  40. package/test/schema-serialization.test.js +1 -1
  41. package/test/schema-special-usage.test.js +1 -1
  42. package/test/schema-validation.test.js +1 -1
  43. package/test/stream.2.test.js +1 -1
  44. package/test/trust-proxy.test.js +27 -21
  45. package/test/types/fastify.tst.ts +2 -0
  46. package/test/toolkit.js +0 -63
@@ -0,0 +1,735 @@
1
+ {
2
+ "version": 1,
3
+ "updatedAt": "2026-08-18T19:46:43.092Z",
4
+ "items": [
5
+ {
6
+ "key": "pass workflow input through `env.npm_package` and read it from `process.env` inside the script.",
7
+ "text": "Pass workflow input through `env.NPM_PACKAGE` and read it from `process.env` inside the script.",
8
+ "kind": "learning",
9
+ "hits": 1,
10
+ "score": 1,
11
+ "firstSeen": "2026-07-06T15:46:17.613Z",
12
+ "lastSeen": "2026-07-06T15:46:17.613Z"
13
+ },
14
+ {
15
+ "key": "validate the package name with an npm package-name regex before using it.",
16
+ "text": "Validate the package name with an npm package-name regex before using it.",
17
+ "kind": "learning",
18
+ "hits": 1,
19
+ "score": 1,
20
+ "firstSeen": "2026-07-06T15:46:17.613Z",
21
+ "lastSeen": "2026-07-06T15:46:17.613Z"
22
+ },
23
+ {
24
+ "key": "use `encodeuricomponent(packagename)` in the npm registry url.",
25
+ "text": "Use `encodeURIComponent(packageName)` in the npm registry URL.",
26
+ "kind": "learning",
27
+ "hits": 1,
28
+ "score": 1,
29
+ "firstSeen": "2026-07-06T15:46:17.613Z",
30
+ "lastSeen": "2026-07-06T15:46:17.613Z"
31
+ },
32
+ {
33
+ "key": "check `response.ok` and throw explicit errors for failed registry fetches or unsupported repository urls.",
34
+ "text": "Check `response.ok` and throw explicit errors for failed registry fetches or unsupported repository URLs.",
35
+ "kind": "learning",
36
+ "hits": 1,
37
+ "score": 1,
38
+ "firstSeen": "2026-07-06T15:46:17.613Z",
39
+ "lastSeen": "2026-07-06T15:46:17.613Z"
40
+ },
41
+ {
42
+ "key": "avoid: interpolated `${{ inputs.package }}` directly into `actions/github-script` javascript, allowing malformed input to break or inject code.",
43
+ "text": "Avoid: Interpolated `${{ inputs.package }}` directly into `actions/github-script` JavaScript, allowing malformed input to break or inject code.",
44
+ "kind": "antiPattern",
45
+ "hits": 1,
46
+ "score": 1,
47
+ "firstSeen": "2026-07-06T15:46:17.613Z",
48
+ "lastSeen": "2026-07-06T15:46:17.613Z"
49
+ },
50
+ {
51
+ "key": "avoid: fetched npm metadata with the raw package name instead of url-encoding it.",
52
+ "text": "Avoid: Fetched npm metadata with the raw package name instead of URL-encoding it.",
53
+ "kind": "antiPattern",
54
+ "hits": 1,
55
+ "score": 1,
56
+ "firstSeen": "2026-07-06T15:46:17.613Z",
57
+ "lastSeen": "2026-07-06T15:46:17.613Z"
58
+ },
59
+ {
60
+ "key": "avoid: assumed `data.repository.url` always existed and was a github `.git` url.",
61
+ "text": "Avoid: Assumed `data.repository.url` always existed and was a GitHub `.git` URL.",
62
+ "kind": "antiPattern",
63
+ "hits": 1,
64
+ "score": 1,
65
+ "firstSeen": "2026-07-06T15:46:17.613Z",
66
+ "lastSeen": "2026-07-06T15:46:17.613Z"
67
+ },
68
+ {
69
+ "key": "avoid: used a brittle repository url regex that missed scoped/modern github url formats and failed without a clear error.",
70
+ "text": "Avoid: Used a brittle repository URL regex that missed scoped/modern GitHub URL formats and failed without a clear error.",
71
+ "kind": "antiPattern",
72
+ "hits": 1,
73
+ "score": 1,
74
+ "firstSeen": "2026-07-06T15:46:17.613Z",
75
+ "lastSeen": "2026-07-06T15:46:17.613Z"
76
+ },
77
+ {
78
+ "key": "renamed pr #6833 to `ci: harden citgm package workflow input handling` using `gh pr edit`, then watched checks until `pull-request-title-check` passed.",
79
+ "text": "Renamed PR #6833 to `ci: harden CITGM package workflow input handling` using `gh pr edit`, then watched checks until `pull-request-title-check` passed.",
80
+ "kind": "learning",
81
+ "hits": 1,
82
+ "score": 1,
83
+ "firstSeen": "2026-07-06T16:04:51.833Z",
84
+ "lastSeen": "2026-07-06T16:04:51.833Z"
85
+ },
86
+ {
87
+ "key": "avoid: pr title did not satisfy the repository's conventional title check before opening/updating the pr.",
88
+ "text": "Avoid: PR title did not satisfy the repository's conventional title check before opening/updating the PR.",
89
+ "kind": "antiPattern",
90
+ "hits": 1,
91
+ "score": 1,
92
+ "firstSeen": "2026-07-06T16:04:51.833Z",
93
+ "lastSeen": "2026-07-06T16:04:51.833Z"
94
+ },
95
+ {
96
+ "key": "honor soft tool-budget and interruption signals immediately; stop browsing and provide a bounded reflection.",
97
+ "text": "Honor soft tool-budget and interruption signals immediately; stop browsing and provide a bounded reflection.",
98
+ "kind": "learning",
99
+ "hits": 1,
100
+ "score": 1,
101
+ "firstSeen": "2026-07-29T19:37:37.132Z",
102
+ "lastSeen": "2026-07-29T19:37:37.132Z"
103
+ },
104
+ {
105
+ "key": "run only the single highest-value targeted test before the budget limit, then report if execution is blocked.",
106
+ "text": "Run only the single highest-value targeted test before the budget limit, then report if execution is blocked.",
107
+ "kind": "learning",
108
+ "hits": 1,
109
+ "score": 1,
110
+ "firstSeen": "2026-07-29T19:37:37.132Z",
111
+ "lastSeen": "2026-07-29T19:37:37.132Z"
112
+ },
113
+ {
114
+ "key": "reproduce root primitive coercion with the repository’s actual validatorcompiler path rather than relying on ajv internals alone.",
115
+ "text": "Reproduce root primitive coercion with the repository’s actual validatorCompiler path rather than relying on Ajv internals alone.",
116
+ "kind": "learning",
117
+ "hits": 1,
118
+ "score": 1,
119
+ "firstSeen": "2026-07-29T19:37:37.132Z",
120
+ "lastSeen": "2026-07-29T19:37:37.132Z"
121
+ },
122
+ {
123
+ "key": "add focused tests covering custom validator compilation, schemaenv availability, and root primitive coercion.",
124
+ "text": "Add focused tests covering custom validator compilation, schemaEnv availability, and root primitive coercion.",
125
+ "kind": "learning",
126
+ "hits": 1,
127
+ "score": 1,
128
+ "firstSeen": "2026-07-29T19:37:37.132Z",
129
+ "lastSeen": "2026-07-29T19:37:37.132Z"
130
+ },
131
+ {
132
+ "key": "avoid: continued initiating tool work after the tool-budget warning instead of stopping to assess the existing evidence.",
133
+ "text": "Avoid: Continued initiating tool work after the tool-budget warning instead of stopping to assess the existing evidence.",
134
+ "kind": "antiPattern",
135
+ "hits": 1,
136
+ "score": 1,
137
+ "firstSeen": "2026-07-29T19:37:37.132Z",
138
+ "lastSeen": "2026-07-29T19:37:37.132Z"
139
+ },
140
+ {
141
+ "key": "avoid: started a test command that was aborted, leaving the primitive-coercion fix unverified.",
142
+ "text": "Avoid: Started a test command that was aborted, leaving the primitive-coercion fix unverified.",
143
+ "kind": "antiPattern",
144
+ "hits": 1,
145
+ "score": 1,
146
+ "firstSeen": "2026-07-29T19:37:37.132Z",
147
+ "lastSeen": "2026-07-29T19:37:37.132Z"
148
+ },
149
+ {
150
+ "key": "avoid: investigated ajv internals and custom compiler compatibility without first isolating the exact failing repository behavior.",
151
+ "text": "Avoid: Investigated Ajv internals and custom compiler compatibility without first isolating the exact failing repository behavior.",
152
+ "kind": "antiPattern",
153
+ "hits": 1,
154
+ "score": 1,
155
+ "firstSeen": "2026-07-29T19:37:37.132Z",
156
+ "lastSeen": "2026-07-29T19:37:37.132Z"
157
+ },
158
+ {
159
+ "key": "avoid: did not establish regression coverage for missing schemaenv handling and root primitive coercion before changing direction.",
160
+ "text": "Avoid: Did not establish regression coverage for missing schemaEnv handling and root primitive coercion before changing direction.",
161
+ "kind": "antiPattern",
162
+ "hits": 1,
163
+ "score": 1,
164
+ "firstSeen": "2026-07-29T19:37:37.132Z",
165
+ "lastSeen": "2026-07-29T19:37:37.132Z"
166
+ },
167
+ {
168
+ "key": "pass request and parameter metadata as ajv parentdata/parentdataproperty so root coercions update the original request value.",
169
+ "text": "Pass request and parameter metadata as Ajv parentData/parentDataProperty so root coercions update the original request value.",
170
+ "kind": "learning",
171
+ "hits": 1,
172
+ "score": 1,
173
+ "firstSeen": "2026-07-29T20:03:27.706Z",
174
+ "lastSeen": "2026-07-29T20:03:27.706Z"
175
+ },
176
+ {
177
+ "key": "add a regression test asserting quoted numeric json becomes a number in the handler while out-of-range values remain rejected.",
178
+ "text": "Add a regression test asserting quoted numeric JSON becomes a number in the handler while out-of-range values remain rejected.",
179
+ "kind": "learning",
180
+ "hits": 1,
181
+ "score": 1,
182
+ "firstSeen": "2026-07-29T20:03:27.706Z",
183
+ "lastSeen": "2026-07-29T20:03:27.706Z"
184
+ },
185
+ {
186
+ "key": "verify targeted test execution separately from the full suite to avoid mistaking aggregate counts for focused coverage.",
187
+ "text": "Verify targeted test execution separately from the full suite to avoid mistaking aggregate counts for focused coverage.",
188
+ "kind": "learning",
189
+ "hits": 1,
190
+ "score": 1,
191
+ "firstSeen": "2026-07-29T20:03:27.706Z",
192
+ "lastSeen": "2026-07-29T20:03:27.706Z"
193
+ },
194
+ {
195
+ "key": "avoid: root primitive request values were validated without a parent reference, so ajv coercion changed only its local value and left request.body as a string.",
196
+ "text": "Avoid: Root primitive request values were validated without a parent reference, so Ajv coercion changed only its local value and left request.body as a string.",
197
+ "kind": "antiPattern",
198
+ "hits": 1,
199
+ "score": 1,
200
+ "firstSeen": "2026-07-29T20:03:27.706Z",
201
+ "lastSeen": "2026-07-29T20:03:27.706Z"
202
+ },
203
+ {
204
+ "key": "avoid: test command output was misread: borp ran the broader suite despite a specific file argument, making targeted coverage unclear.",
205
+ "text": "Avoid: Test command output was misread: Borp ran the broader suite despite a specific file argument, making targeted coverage unclear.",
206
+ "kind": "antiPattern",
207
+ "hits": 1,
208
+ "score": 1,
209
+ "firstSeen": "2026-07-29T20:03:27.706Z",
210
+ "lastSeen": "2026-07-29T20:03:27.706Z"
211
+ },
212
+ {
213
+ "key": "pass parentdata and parentdataproperty to ajv validators so root primitive coercions update the request object.",
214
+ "text": "Pass parentData and parentDataProperty to Ajv validators so root primitive coercions update the request object.",
215
+ "kind": "learning",
216
+ "hits": 1,
217
+ "score": 1,
218
+ "firstSeen": "2026-07-29T20:06:55.452Z",
219
+ "lastSeen": "2026-07-29T20:06:55.452Z"
220
+ },
221
+ {
222
+ "key": "retain the direct-call path for validators without schemaenv to avoid changing non-ajv validator behavior.",
223
+ "text": "Retain the direct-call path for validators without schemaEnv to avoid changing non-Ajv validator behavior.",
224
+ "kind": "learning",
225
+ "hits": 1,
226
+ "score": 1,
227
+ "firstSeen": "2026-07-29T20:06:55.452Z",
228
+ "lastSeen": "2026-07-29T20:06:55.452Z"
229
+ },
230
+ {
231
+ "key": "add a regression test asserting quoted numeric json reaches the handler as number 10.",
232
+ "text": "Add a regression test asserting quoted numeric JSON reaches the handler as number 10.",
233
+ "kind": "learning",
234
+ "hits": 1,
235
+ "score": 1,
236
+ "firstSeen": "2026-07-29T20:06:55.452Z",
237
+ "lastSeen": "2026-07-29T20:06:55.452Z"
238
+ },
239
+ {
240
+ "key": "determine pr from the deployed route’s authentication requirements; use pr:n for unauthenticated endpoints.",
241
+ "text": "Determine PR from the deployed route’s authentication requirements; use PR:N for unauthenticated endpoints.",
242
+ "kind": "learning",
243
+ "hits": 1,
244
+ "score": 1,
245
+ "firstSeen": "2026-07-29T20:06:55.452Z",
246
+ "lastSeen": "2026-07-29T20:06:55.452Z"
247
+ },
248
+ {
249
+ "key": "base c/i impact on the specific handler and data affected rather than assigning a universal fastify score.",
250
+ "text": "Base C/I impact on the specific handler and data affected rather than assigning a universal Fastify score.",
251
+ "kind": "learning",
252
+ "hits": 1,
253
+ "score": 1,
254
+ "firstSeen": "2026-07-29T20:06:55.452Z",
255
+ "lastSeen": "2026-07-29T20:06:55.452Z"
256
+ },
257
+ {
258
+ "key": "avoid: ajv was called with a primitive root value without parentdata context, so coerced values were not written back to request.body.",
259
+ "text": "Avoid: Ajv was called with a primitive root value without parentData context, so coerced values were not written back to request.body.",
260
+ "kind": "antiPattern",
261
+ "hits": 1,
262
+ "score": 1,
263
+ "firstSeen": "2026-07-29T20:06:55.452Z",
264
+ "lastSeen": "2026-07-29T20:06:55.452Z"
265
+ },
266
+ {
267
+ "key": "avoid: the regression scenario was initially vulnerable because the handler received string \"10\" after validation accepted it.",
268
+ "text": "Avoid: The regression scenario was initially vulnerable because the handler received string \"10\" after validation accepted it.",
269
+ "kind": "antiPattern",
270
+ "hits": 1,
271
+ "score": 1,
272
+ "firstSeen": "2026-07-29T20:06:55.452Z",
273
+ "lastSeen": "2026-07-29T20:06:55.452Z"
274
+ },
275
+ {
276
+ "key": "avoid: the cvss rating assumed pr:l without repository evidence that authentication is required; many affected routes may be unauthenticated.",
277
+ "text": "Avoid: The CVSS rating assumed PR:L without repository evidence that authentication is required; many affected routes may be unauthenticated.",
278
+ "kind": "antiPattern",
279
+ "hits": 1,
280
+ "score": 1,
281
+ "firstSeen": "2026-07-29T20:06:55.452Z",
282
+ "lastSeen": "2026-07-29T20:06:55.452Z"
283
+ },
284
+ {
285
+ "key": "avoid: the impact rating was stated as application-dependent without defining the affected endpoint’s actual confidentiality and integrity consequences.",
286
+ "text": "Avoid: The impact rating was stated as application-dependent without defining the affected endpoint’s actual confidentiality and integrity consequences.",
287
+ "kind": "antiPattern",
288
+ "hits": 1,
289
+ "score": 1,
290
+ "firstSeen": "2026-07-29T20:06:55.452Z",
291
+ "lastSeen": "2026-07-29T20:06:55.452Z"
292
+ },
293
+ {
294
+ "key": "updated `lib/validation.js` to retain and return coerced root values.",
295
+ "text": "Updated `lib/validation.js` to retain and return coerced root values.",
296
+ "kind": "learning",
297
+ "hits": 1,
298
+ "score": 1,
299
+ "firstSeen": "2026-07-29T20:09:41.212Z",
300
+ "lastSeen": "2026-07-29T20:09:41.212Z"
301
+ },
302
+ {
303
+ "key": "added `test/fix-root-primitive-coercion.test.js` to prevent recurrence.",
304
+ "text": "Added `test/fix-root-primitive-coercion.test.js` to prevent recurrence.",
305
+ "kind": "learning",
306
+ "hits": 1,
307
+ "score": 1,
308
+ "firstSeen": "2026-07-29T20:09:41.212Z",
309
+ "lastSeen": "2026-07-29T20:09:41.212Z"
310
+ },
311
+ {
312
+ "key": "avoid: validation coerced a root primitive but failed to preserve the coerced value for the caller.",
313
+ "text": "Avoid: Validation coerced a root primitive but failed to preserve the coerced value for the caller.",
314
+ "kind": "antiPattern",
315
+ "hits": 1,
316
+ "score": 1,
317
+ "firstSeen": "2026-07-29T20:09:41.212Z",
318
+ "lastSeen": "2026-07-29T20:09:41.212Z"
319
+ },
320
+ {
321
+ "key": "avoid: the regression scenario was not covered by existing tests.",
322
+ "text": "Avoid: The regression scenario was not covered by existing tests.",
323
+ "kind": "antiPattern",
324
+ "hits": 1,
325
+ "score": 1,
326
+ "firstSeen": "2026-07-29T20:09:41.212Z",
327
+ "lastSeen": "2026-07-29T20:09:41.212Z"
328
+ },
329
+ {
330
+ "key": "always log/compare the exact pathname string passed into send(), including after encodeuri",
331
+ "text": "Always log/compare the exact pathname string passed into send(), including after encodeURI",
332
+ "kind": "learning",
333
+ "hits": 1,
334
+ "score": 1,
335
+ "firstSeen": "2026-08-04T10:59:32.673Z",
336
+ "lastSeen": "2026-08-04T10:59:32.673Z"
337
+ },
338
+ {
339
+ "key": "when statuses differ for \"same\" path, diff charcodes for % vs %25 double-encoding first",
340
+ "text": "When statuses differ for \"same\" path, diff charCodes for % vs %25 double-encoding first",
341
+ "kind": "learning",
342
+ "hits": 1,
343
+ "score": 1,
344
+ "firstSeen": "2026-08-04T10:59:32.673Z",
345
+ "lastSeen": "2026-08-04T10:59:32.673Z"
346
+ },
347
+ {
348
+ "key": "instrument static handler vs router to separate callnotfound from unmatched routes",
349
+ "text": "Instrument static handler vs router to separate callNotFound from unmatched routes",
350
+ "kind": "learning",
351
+ "hits": 1,
352
+ "score": 1,
353
+ "firstSeen": "2026-08-04T10:59:32.673Z",
354
+ "lastSeen": "2026-08-04T10:59:32.673Z"
355
+ },
356
+ {
357
+ "key": "trace full pipeline: raw.url → getpathnameforsend → dotdot guards → encodeuri → @fastify/send",
358
+ "text": "Trace full pipeline: raw.url → getPathnameForSend → dotDot guards → encodeURI → @fastify/send",
359
+ "kind": "learning",
360
+ "hits": 1,
361
+ "score": 1,
362
+ "firstSeen": "2026-08-04T10:59:32.673Z",
363
+ "lastSeen": "2026-08-04T10:59:32.673Z"
364
+ },
365
+ {
366
+ "key": "reproduce with send(root, p) using both raw and encodeuri(p) before changing triage conclusions",
367
+ "text": "Reproduce with send(root, p) using both raw and encodeURI(p) before changing triage conclusions",
368
+ "kind": "learning",
369
+ "hits": 1,
370
+ "score": 1,
371
+ "firstSeen": "2026-08-04T10:59:32.673Z",
372
+ "lastSeen": "2026-08-04T10:59:32.673Z"
373
+ },
374
+ {
375
+ "key": "avoid: assumed /static/..%2f… 404 meant getpathnameforsend/route miss instead of later send pipeline behavior",
376
+ "text": "Avoid: Assumed /static/..%2f… 404 meant getPathnameForSend/route miss instead of later send pipeline behavior",
377
+ "kind": "antiPattern",
378
+ "hits": 1,
379
+ "score": 1,
380
+ "firstSeen": "2026-08-04T10:59:32.673Z",
381
+ "lastSeen": "2026-08-04T10:59:32.673Z"
382
+ },
383
+ {
384
+ "key": "avoid: did not check that encodeuri() re-encodes residual % as %25 before blaming path guards",
385
+ "text": "Avoid: Did not check that encodeURI() re-encodes residual % as %25 before blaming path guards",
386
+ "kind": "antiPattern",
387
+ "hits": 1,
388
+ "score": 1,
389
+ "firstSeen": "2026-08-04T10:59:32.673Z",
390
+ "lastSeen": "2026-08-04T10:59:32.673Z"
391
+ },
392
+ {
393
+ "key": "avoid: conflated reply.callnotfound() json (\"route get:… not found\") with an actual find-my-way miss",
394
+ "text": "Avoid: Conflated reply.callNotFound() JSON (\"Route GET:… not found\") with an actual find-my-way miss",
395
+ "kind": "antiPattern",
396
+ "hits": 1,
397
+ "score": 1,
398
+ "firstSeen": "2026-08-04T10:59:32.673Z",
399
+ "lastSeen": "2026-08-04T10:59:32.673Z"
400
+ },
401
+ {
402
+ "key": "avoid: debugged isolated getpathnameforsend reimplementation before tracing pumpsendtoreply → encodeuri(pathnameforsend) → @fastify/send",
403
+ "text": "Avoid: Debugged isolated getPathnameForSend reimplementation before tracing pumpSendToReply → encodeURI(pathnameForSend) → @fastify/send",
404
+ "kind": "antiPattern",
405
+ "hits": 1,
406
+ "score": 1,
407
+ "firstSeen": "2026-08-04T10:59:32.673Z",
408
+ "lastSeen": "2026-08-04T10:59:32.673Z"
409
+ },
410
+ {
411
+ "key": "avoid: compared send() on decoded ../ paths (403) to static outcomes without using the exact post-encodeuri string static passes",
412
+ "text": "Avoid: Compared send() on decoded ../ paths (403) to static outcomes without using the exact post-encodeURI string static passes",
413
+ "kind": "antiPattern",
414
+ "hits": 1,
415
+ "score": 1,
416
+ "firstSeen": "2026-08-04T10:59:32.673Z",
417
+ "lastSeen": "2026-08-04T10:59:32.673Z"
418
+ },
419
+ {
420
+ "key": "empirically print encodeuri output/charcodes: '%' → '%25', so static sees '/..%252ftop-secret.txt' (safe 404) not a '..' segment.",
421
+ "text": "Empirically print encodeURI output/charCodes: '%' → '%25', so static sees '/..%252ftop-secret.txt' (safe 404) not a '..' segment.",
422
+ "kind": "learning",
423
+ "hits": 1,
424
+ "score": 1,
425
+ "firstSeen": "2026-08-04T11:12:27.539Z",
426
+ "lastSeen": "2026-08-04T11:12:27.539Z"
427
+ },
428
+ {
429
+ "key": "reproduce with @fastify/send directly for raw percent, encodeuri(percent), and decoded '../' to map 403 vs 404 to normalize/root logic.",
430
+ "text": "Reproduce with @fastify/send directly for raw percent, encodeURI(percent), and decoded '../' to map 403 vs 404 to normalize/root logic.",
431
+ "kind": "learning",
432
+ "hits": 1,
433
+ "score": 1,
434
+ "firstSeen": "2026-08-04T11:12:27.539Z",
435
+ "lastSeen": "2026-08-04T11:12:27.539Z"
436
+ },
437
+ {
438
+ "key": "keep reporter response framed as app-level path.join(param) misuse; cite static/sendfile containment and express parity, not a core cve.",
439
+ "text": "Keep reporter response framed as app-level path.join(param) misuse; cite static/sendFile containment and Express parity, not a core CVE.",
440
+ "kind": "learning",
441
+ "hits": 1,
442
+ "score": 1,
443
+ "firstSeen": "2026-08-04T11:12:27.539Z",
444
+ "lastSeen": "2026-08-04T11:12:27.539Z"
445
+ },
446
+ {
447
+ "key": "avoid: assumed encodeuri leaves '%' intact on paths like '/..%2ftop-secret.txt', so static 404 vs raw '..' 403 looked inconsistent.",
448
+ "text": "Avoid: Assumed encodeURI leaves '%' intact on paths like '/..%2ftop-secret.txt', so static 404 vs raw '..' 403 looked inconsistent.",
449
+ "kind": "antiPattern",
450
+ "hits": 1,
451
+ "score": 1,
452
+ "firstSeen": "2026-08-04T11:12:27.539Z",
453
+ "lastSeen": "2026-08-04T11:12:27.539Z"
454
+ },
455
+ {
456
+ "key": "avoid: treated encoded traversal and decoded param sinks as the same layer before separating find-my-way dual-decode from @fastify/send root checks.",
457
+ "text": "Avoid: Treated encoded traversal and decoded param sinks as the same layer before separating find-my-way dual-decode from @fastify/send root checks.",
458
+ "kind": "antiPattern",
459
+ "hits": 1,
460
+ "score": 1,
461
+ "firstSeen": "2026-08-04T11:12:27.539Z",
462
+ "lastSeen": "2026-08-04T11:12:27.539Z"
463
+ },
464
+ {
465
+ "key": "avoid: relied on log/message shape alone (callnotfound 'route ... not found') to infer static failure mode without probing @fastify/send inputs.",
466
+ "text": "Avoid: Relied on log/message shape alone (callNotFound 'Route ... not found') to infer static failure mode without probing @fastify/send inputs.",
467
+ "kind": "antiPattern",
468
+ "hits": 1,
469
+ "score": 1,
470
+ "firstSeen": "2026-08-04T11:12:27.539Z",
471
+ "lastSeen": "2026-08-04T11:12:27.539Z"
472
+ },
473
+ {
474
+ "key": "re-run markdownlint scoped strictly to docs/reference/request.md and docs/reference/routes.md.",
475
+ "text": "Re-run markdownlint scoped strictly to docs/Reference/Request.md and docs/Reference/Routes.md.",
476
+ "kind": "learning",
477
+ "hits": 1,
478
+ "score": 1,
479
+ "firstSeen": "2026-08-04T12:31:54.341Z",
480
+ "lastSeen": "2026-08-04T12:31:54.341Z"
481
+ },
482
+ {
483
+ "key": "stage only those two paths (git add docs/reference/request.md docs/reference/routes.md); never git add .",
484
+ "text": "Stage only those two paths (git add docs/Reference/Request.md docs/Reference/Routes.md); never git add .",
485
+ "kind": "learning",
486
+ "hits": 1,
487
+ "score": 1,
488
+ "firstSeen": "2026-08-04T12:31:54.341Z",
489
+ "lastSeen": "2026-08-04T12:31:54.341Z"
490
+ },
491
+ {
492
+ "key": "manually awk length>80 on changed files to confirm new lines stay ≤80 before commit.",
493
+ "text": "Manually awk length>80 on changed files to confirm new lines stay ≤80 before commit.",
494
+ "kind": "learning",
495
+ "hits": 1,
496
+ "score": 1,
497
+ "firstSeen": "2026-08-04T12:31:54.341Z",
498
+ "lastSeen": "2026-08-04T12:31:54.341Z"
499
+ },
500
+ {
501
+ "key": "leave response.md untracked and verify git status/diff --stat before commit and pr.",
502
+ "text": "Leave response.md untracked and verify git status/diff --stat before commit and PR.",
503
+ "kind": "learning",
504
+ "hits": 1,
505
+ "score": 1,
506
+ "firstSeen": "2026-08-04T12:31:54.341Z",
507
+ "lastSeen": "2026-08-04T12:31:54.341Z"
508
+ },
509
+ {
510
+ "key": "avoid: markdown lint invoked in a way that flooded output with unrelated files (agents.md, .pi memory, response.md) instead of only the two edited docs.",
511
+ "text": "Avoid: Markdown lint invoked in a way that flooded output with unrelated files (AGENTS.md, .pi memory, response.md) instead of only the two edited docs.",
512
+ "kind": "antiPattern",
513
+ "hits": 1,
514
+ "score": 1,
515
+ "firstSeen": "2026-08-04T12:31:54.341Z",
516
+ "lastSeen": "2026-08-04T12:31:54.341Z"
517
+ },
518
+ {
519
+ "key": "avoid: untracked response.md sat in the working tree and could have been staged if git add was too broad.",
520
+ "text": "Avoid: Untracked response.md sat in the working tree and could have been staged if git add was too broad.",
521
+ "kind": "antiPattern",
522
+ "hits": 1,
523
+ "score": 1,
524
+ "firstSeen": "2026-08-04T12:31:54.341Z",
525
+ "lastSeen": "2026-08-04T12:31:54.341Z"
526
+ },
527
+ {
528
+ "key": "avoid: relied on noisy repo-wide lint signal before confirming md013 on the actual changed regions.",
529
+ "text": "Avoid: Relied on noisy repo-wide lint signal before confirming MD013 on the actual changed regions.",
530
+ "kind": "antiPattern",
531
+ "hits": 1,
532
+ "score": 1,
533
+ "firstSeen": "2026-08-04T12:31:54.341Z",
534
+ "lastSeen": "2026-08-04T12:31:54.341Z"
535
+ },
536
+ {
537
+ "key": "before any security-fix push, explicitly ask whether targets are the private advisory fork, public `origin`, or both; treat a draft advisory as a warning, not implicit authorization policy.",
538
+ "text": "Before any security-fix push, explicitly ask whether targets are the private advisory fork, public `origin`, or both; treat a draft advisory as a warning, not implicit authorization policy.",
539
+ "kind": "learning",
540
+ "hits": 1,
541
+ "score": 1,
542
+ "firstSeen": "2026-08-18T19:37:20.498Z",
543
+ "lastSeen": "2026-08-18T19:37:20.498Z"
544
+ },
545
+ {
546
+ "key": "always state push destinations and exact resulting refs, e.g. `advisory-ghsa-3m5p/main` changed while `origin/main` remained at `6e95cb9f`.",
547
+ "text": "Always state push destinations and exact resulting refs, e.g. `advisory-ghsa-3m5p/main` changed while `origin/main` remained at `6e95cb9f`.",
548
+ "kind": "learning",
549
+ "hits": 1,
550
+ "score": 1,
551
+ "firstSeen": "2026-08-18T19:37:20.498Z",
552
+ "lastSeen": "2026-08-18T19:37:20.498Z"
553
+ },
554
+ {
555
+ "key": "run workspace-sensitive pr operations separately: attempt the comment, record an expected permission denial, then independently close only if authorized.",
556
+ "text": "Run workspace-sensitive PR operations separately: attempt the comment, record an expected permission denial, then independently close only if authorized.",
557
+ "kind": "learning",
558
+ "hits": 1,
559
+ "score": 1,
560
+ "firstSeen": "2026-08-18T19:37:20.498Z",
561
+ "lastSeen": "2026-08-18T19:37:20.498Z"
562
+ },
563
+ {
564
+ "key": "describe pr state from verified fields (`closed`, `mergedat: null`, head/base oids) rather than inferring that it is empty.",
565
+ "text": "Describe PR state from verified fields (`CLOSED`, `mergedAt: null`, head/base OIDs) rather than inferring that it is empty.",
566
+ "kind": "learning",
567
+ "hits": 1,
568
+ "score": 1,
569
+ "firstSeen": "2026-08-18T19:37:20.498Z",
570
+ "lastSeen": "2026-08-18T19:37:20.498Z"
571
+ },
572
+ {
573
+ "key": "keep local security branches until the user confirms the intended public/private publication state; delete them only after final authorization and ref verification.",
574
+ "text": "Keep local security branches until the user confirms the intended public/private publication state; delete them only after final authorization and ref verification.",
575
+ "kind": "learning",
576
+ "hits": 1,
577
+ "score": 1,
578
+ "firstSeen": "2026-08-18T19:37:20.498Z",
579
+ "lastSeen": "2026-08-18T19:37:20.498Z"
580
+ },
581
+ {
582
+ "key": "avoid: assumed the draft advisory meant `origin/main` and `origin/5.x` must remain untouched without first confirming whether the user expected public pushes.",
583
+ "text": "Avoid: Assumed the draft advisory meant `origin/main` and `origin/5.x` must remain untouched without first confirming whether the user expected public pushes.",
584
+ "kind": "antiPattern",
585
+ "hits": 1,
586
+ "score": 1,
587
+ "firstSeen": "2026-08-18T19:37:20.498Z",
588
+ "lastSeen": "2026-08-18T19:37:20.498Z"
589
+ },
590
+ {
591
+ "key": "avoid: reported that the private branches were complete without clearly distinguishing advisory-fork refs from public `origin` refs, prompting the user to stop and verify deployment status.",
592
+ "text": "Avoid: Reported that the private branches were complete without clearly distinguishing advisory-fork refs from public `origin` refs, prompting the user to stop and verify deployment status.",
593
+ "kind": "antiPattern",
594
+ "hits": 1,
595
+ "score": 1,
596
+ "firstSeen": "2026-08-18T19:37:20.498Z",
597
+ "lastSeen": "2026-08-18T19:37:20.498Z"
598
+ },
599
+ {
600
+ "key": "avoid: combined pr closing with an unsupported workspace-repository comment under `set -e`, causing the entire command to fail before the close could occur.",
601
+ "text": "Avoid: Combined PR closing with an unsupported workspace-repository comment under `set -e`, causing the entire command to fail before the close could occur.",
602
+ "kind": "antiPattern",
603
+ "hits": 1,
604
+ "score": 1,
605
+ "firstSeen": "2026-08-18T19:37:20.498Z",
606
+ "lastSeen": "2026-08-18T19:37:20.498Z"
607
+ },
608
+ {
609
+ "key": "avoid: called the pr “now-empty” even though github still reported it as mergeable with different head and base oids.",
610
+ "text": "Avoid: Called the PR “now-empty” even though GitHub still reported it as mergeable with different head and base OIDs.",
611
+ "kind": "antiPattern",
612
+ "hits": 1,
613
+ "score": 1,
614
+ "firstSeen": "2026-08-18T19:37:20.498Z",
615
+ "lastSeen": "2026-08-18T19:37:20.498Z"
616
+ },
617
+ {
618
+ "key": "avoid: deleted local security branches before resolving whether the user wanted the commits pushed to public branches, reducing convenient recovery points.",
619
+ "text": "Avoid: Deleted local security branches before resolving whether the user wanted the commits pushed to public branches, reducing convenient recovery points.",
620
+ "kind": "antiPattern",
621
+ "hits": 1,
622
+ "score": 1,
623
+ "firstSeen": "2026-08-18T19:37:20.498Z",
624
+ "lastSeen": "2026-08-18T19:37:20.498Z"
625
+ },
626
+ {
627
+ "key": "obtained explicit approval before public disclosure, then atomically fast-forwarded `origin/main` to `af079bd4` and `origin/5.x` to `8acfea7e`, verifying both refs with `git ls-remote`.",
628
+ "text": "Obtained explicit approval before public disclosure, then atomically fast-forwarded `origin/main` to `af079bd4` and `origin/5.x` to `8acfea7e`, verifying both refs with `git ls-remote`.",
629
+ "kind": "learning",
630
+ "hits": 1,
631
+ "score": 1,
632
+ "firstSeen": "2026-08-18T19:38:15.045Z",
633
+ "lastSeen": "2026-08-18T19:38:15.045Z"
634
+ },
635
+ {
636
+ "key": "fast-forwarded local `main` to `origin/main` and confirmed a clean synchronized status.",
637
+ "text": "Fast-forwarded local `main` to `origin/main` and confirmed a clean synchronized status.",
638
+ "kind": "learning",
639
+ "hits": 1,
640
+ "score": 1,
641
+ "firstSeen": "2026-08-18T19:38:15.045Z",
642
+ "lastSeen": "2026-08-18T19:38:15.045Z"
643
+ },
644
+ {
645
+ "key": "do not retry forbidden workspace graphql actions; use permitted repository operations or ask the user for an authorized alternative before attempting comments or metadata changes.",
646
+ "text": "Do not retry forbidden workspace GraphQL actions; use permitted repository operations or ask the user for an authorized alternative before attempting comments or metadata changes.",
647
+ "kind": "learning",
648
+ "hits": 1,
649
+ "score": 1,
650
+ "firstSeen": "2026-08-18T19:38:15.045Z",
651
+ "lastSeen": "2026-08-18T19:38:15.045Z"
652
+ },
653
+ {
654
+ "key": "avoid: initially pushed the security fix only to the private advisory fork, leaving `origin/main` and `origin/5.x` unchanged without first confirming the intended publication target.",
655
+ "text": "Avoid: Initially pushed the security fix only to the private advisory fork, leaving `origin/main` and `origin/5.x` unchanged without first confirming the intended publication target.",
656
+ "kind": "antiPattern",
657
+ "hits": 1,
658
+ "score": 1,
659
+ "firstSeen": "2026-08-18T19:38:15.045Z",
660
+ "lastSeen": "2026-08-18T19:38:15.045Z"
661
+ },
662
+ {
663
+ "key": "avoid: after pushing remote refs, local `main` remained one commit behind, creating temporary local/remote inconsistency.",
664
+ "text": "Avoid: After pushing remote refs, local `main` remained one commit behind, creating temporary local/remote inconsistency.",
665
+ "kind": "antiPattern",
666
+ "hits": 1,
667
+ "score": 1,
668
+ "firstSeen": "2026-08-18T19:38:15.045Z",
669
+ "lastSeen": "2026-08-18T19:38:15.045Z"
670
+ },
671
+ {
672
+ "key": "avoid: a graphql `addcomment` action was attempted despite workspace-repository restrictions; the denial indicates an intentional permission boundary that should not be bypassed.",
673
+ "text": "Avoid: A GraphQL `addComment` action was attempted despite workspace-repository restrictions; the denial indicates an intentional permission boundary that should not be bypassed.",
674
+ "kind": "antiPattern",
675
+ "hits": 1,
676
+ "score": 1,
677
+ "firstSeen": "2026-08-18T19:38:15.045Z",
678
+ "lastSeen": "2026-08-18T19:38:15.045Z"
679
+ },
680
+ {
681
+ "key": "closed pr #1 without merging after confirming its head sha was already the intended private `main` commit.",
682
+ "text": "Closed PR #1 without merging after confirming its head SHA was already the intended private `main` commit.",
683
+ "kind": "learning",
684
+ "hits": 1,
685
+ "score": 1,
686
+ "firstSeen": "2026-08-18T19:46:43.092Z",
687
+ "lastSeen": "2026-08-18T19:46:43.092Z"
688
+ },
689
+ {
690
+ "key": "fetched current `origin/main` and `origin/5.x`, verified each security commit’s parent exactly matched the corresponding remote tip, then used an atomic push.",
691
+ "text": "Fetched current `origin/main` and `origin/5.x`, verified each security commit’s parent exactly matched the corresponding remote tip, then used an atomic push.",
692
+ "kind": "learning",
693
+ "hits": 1,
694
+ "score": 1,
695
+ "firstSeen": "2026-08-18T19:46:43.092Z",
696
+ "lastSeen": "2026-08-18T19:46:43.092Z"
697
+ },
698
+ {
699
+ "key": "verified public and private `main`/`5.x` refs matched, fast-forwarded local `main`, deleted temporary security branches, and confirmed a clean synchronized checkout.",
700
+ "text": "Verified public and private `main`/`5.x` refs matched, fast-forwarded local `main`, deleted temporary security branches, and confirmed a clean synchronized checkout.",
701
+ "kind": "learning",
702
+ "hits": 1,
703
+ "score": 1,
704
+ "firstSeen": "2026-08-18T19:46:43.092Z",
705
+ "lastSeen": "2026-08-18T19:46:43.092Z"
706
+ },
707
+ {
708
+ "key": "avoid: private pr #1 remained open after its head commit had already been placed on private `main`; its mergeability was `unknown` and it was no longer the correct landing path.",
709
+ "text": "Avoid: Private PR #1 remained open after its head commit had already been placed on private `main`; its mergeability was `UNKNOWN` and it was no longer the correct landing path.",
710
+ "kind": "antiPattern",
711
+ "hits": 1,
712
+ "score": 1,
713
+ "firstSeen": "2026-08-18T19:46:43.092Z",
714
+ "lastSeen": "2026-08-18T19:46:43.092Z"
715
+ },
716
+ {
717
+ "key": "avoid: local `main` and temporary security branches were left out of sync after updating the remote branches.",
718
+ "text": "Avoid: Local `main` and temporary security branches were left out of sync after updating the remote branches.",
719
+ "kind": "antiPattern",
720
+ "hits": 1,
721
+ "score": 1,
722
+ "firstSeen": "2026-08-18T19:46:43.092Z",
723
+ "lastSeen": "2026-08-18T19:46:43.092Z"
724
+ },
725
+ {
726
+ "key": "avoid: direct public branch updates bypassed the repository’s pull-request-only rules, so ancestry and target refs required explicit validation to avoid unsafe rewrites.",
727
+ "text": "Avoid: Direct public branch updates bypassed the repository’s pull-request-only rules, so ancestry and target refs required explicit validation to avoid unsafe rewrites.",
728
+ "kind": "antiPattern",
729
+ "hits": 1,
730
+ "score": 1,
731
+ "firstSeen": "2026-08-18T19:46:43.092Z",
732
+ "lastSeen": "2026-08-18T19:46:43.092Z"
733
+ }
734
+ ]
735
+ }