fix(ai): bound the HTTP 400 request-dump directory - #3319
Conversation
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Signed exact-head verdict — PR #3319 @ 54305360580e8edc57bc974426c07cfe97507a3e
Disposition: APPROVE
Clean successor to conflict-closed #3317 for HTTP 400 dump retention. Independent review: bounds ~/.gjc/logs/http-400-requests to the newest 50 dumps (unbounded growth measured at 27,249 files / 7.0 GB, 96% of everything under ~/.gjc), matching the retention policy the rotating application log already uses. Pruning runs after each write, is best-effort (swallows errors so a diagnostics path can't cause a second failure), and file-name lexical sort is chronological for this writer. Covered by http-inspector-dump-retention.test.ts (under-cap no-op, trims to cap keeping newest, non-dump files untouched, missing-dir no-op). CI green, MERGEABLE, head is clean.
No blocker found. Not merged (dev is red).
appendRawHttpRequestDumpFor400 writes one file per HTTP 400 containing the full
sanitized request body, and nothing ever removed one. There is no cap, no
rotation, and no sweep anywhere in the tree.
Measured on a developer machine:
~/.gjc 7.3 GB
~/.gjc/logs 7.0 GB
~/.gjc/logs/http-400-requests 7.0 GB across 27,249 files, 264 KB average
That single directory was 96% of everything the agent stores under $HOME. The
rotating application log next to it is bounded (maxSize 10m, maxFiles 5,
gzipped) and occupies 12 KB per archived day, so the policy already exists —
these diagnostics simply never adopted it.
Retain the newest 50 dumps. File names are `${Date.now()}-${hash}.json`, so a
lexical sort is chronological for this writer. Pruning runs after each write and
swallows every error: a diagnostics path must never turn a request failure into
a second failure, which is why the write itself already catches.
Regression: retention holds at the cap, trims to it while keeping the newest,
leaves non-dump files alone, and treats a missing directory as a no-op rather
than throwing. Disabling the trim fails the trim case.
5430536 to
20afc45
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Signed exact-head verdict — PR #3319 @ 20afc455745c939550b3fbb52288bc2a52cbf460
Disposition: APPROVE (MERGE_READY)
Prior approval was stale-head (54305360); this is a fresh review against the current exact head.
Independent review
Bounds ~/.gjc/logs/http-400-requests to the newest 50 dumps. Each dump carries the full sanitized request body (avg 264 KB), and nothing ever removed one — measured growth to 27,249 files / 7.0 GB (96% of everything under ~/.gjc).
Correctness checks
- Retention logic is correct:
pruneHttpRequestDumpsreads dir, filters.json, sorts lexically, and iflength > MAX_RETAINED_DUMPSremovesslice(0, length - MAX_RETAINED_DUMPS)— the oldest. File names are${Date.now()}-${hash}.json, so lexical sort = chronological order. ✓ - Pruning site is correct: called after every
Bun.writeinappendRawHttpRequestDumpFor400, so the cap is enforced on every dump, not just on startup. ✓ - Best-effort contract:
readdirand everyrmare.catch(() => ...)— diagnostics never turn a request failure into a second failure. Missing dir returns 0, no throw. ✓ - Non-dump files preserved: filter is
name.endsWith(".json")only — aREADME.txtor unrelated file survives. ✓ - Extracted helpers are clean:
httpRequestDumpDir()andpruneHttpRequestDumps()are exported, single-responsibility, default-arg for testability. ✓
Adversarial edge cases probed
- Clock skew / non-monotonic
Date.now(): if timestamps aren't monotonic, lexical order may not be perfectly chronological, but this only affects which dumps are kept — the cap is still enforced. Acceptable for best-effort diagnostics. - Concurrent writers: two processes pruning simultaneously could each compute the same removal set and race on
rm— butforce: truemakes the loser a no-op. ✓
Test verification
- 4 tests: under-cap no-op, over-cap trims to 50 keeping newest, unrelated file preserved, missing-dir no-op. All externally observable contract behavior. ✓
- CI: all checks pass (run 30409891893), including
test:http-inspector-dump-retention.test.ts.
Verdict
No defects found. No rebase needed. MERGE_READY — merge blocked only on dev CI green.
Supersedes #3317, closed as
mergeable: CONFLICTINGunder the external-PR policy. The verdict there was explicit that this was not a rejection of the approach:Rebased onto
devatb853f15d. Zero conflicts — the diff toucheshttp-inspector.tsand its new test only. Content is byte-identical to the reviewed head.Measured on a developer machine
That one directory is 96% of everything the agent stores under
$HOME.Cause
appendRawHttpRequestDumpFor400writes one file per HTTP 400 containing the full sanitized request body — and nothing ever removes one. No cap, no rotation, no sweep anywhere in the tree;http-400-requestsappears exactly once inpackages/*/src, at the write site.The rotating application log right next to it is already bounded (
maxSize: 10m,maxFiles: 5, gzipped) and occupies 12 KB per archived day. The policy exists; these diagnostics never adopted it.Fix
Retain the newest 50 dumps. File names are
${Date.now()}-${hash}.json, so a lexical sort is chronological for this writer.Pruning runs after each write and swallows every error — a diagnostics path must never turn a request failure into a second failure, which is exactly why the write itself already catches.
Tests
http-inspector-dump-retention.test.ts(4 cases): holds everything under the cap, trims to the cap while keeping the newest, leaves non-dump files alone, and treats a missing directory as a no-op rather than throwing.Proof-first: disabling the trim fails the trim case.
Verification
Re-run after the rebase:
bun run checkinpackages/aiexit 0, new suite 4 pass / 0 fail.