fix(test): resolve Biome noUnsafeOptionalChaining lint errors (#1341)#1343
fix(test): resolve Biome noUnsafeOptionalChaining lint errors (#1341)#1343github-actions[bot] wants to merge 21 commits into
Conversation
…ssion control Replace the single postgres.js pool with three isolated lanes (data, control, writer) that share a configurable total connection budget. Each lane gets a per-pool outstanding-operation admission wrapper that fast-fails with DB_POOL_ADMISSION_EXCEEDED before saturating the underlying driver queue, preventing unbounded memory growth under load. Route handlers now wrap with withDataDbScope so request-path queries use the data lane via AsyncLocalStorage, while control-plane and writer traffic remain isolated. Add statement_timeout and lock_timeout at the connection level so slow SQL cannot block the streaming finalization deadline. Introduce LOCAL_OVERLOAD error category so admission rejections are never retried, failovered, or counted against Provider/endpoint circuits.
Introduce createDemandDrivenResponsePump to replace the TransformStream + tee() pipeline with a single high-water-mark-0 ReadableStream that primes exactly one upstream chunk and never reads ahead past unconsumed downstream demand. This eliminates unbounded buffering for slow clients and removes the generic streaming stale watchdog. Rewrite nodeStreamToWebStreamSafe to pause/resume the Node source on controller.desiredSize backpressure, handle pre-existing errored/destroyed/closed states before listener registration, and guard delayed asynchronous destroy(error) events so they cannot surface as uncaught exceptions after the Web stream has settled. Defer all circuit-breaker, session-binding, and Codex cache side effects into post-terminal tasks so a slow Redis call cannot turn an already-completed billable request into a fallback 500 or block lease settlement.
…ssage requests Introduce enqueueMessageRequestUpdateDurably which returns a Promise that resolves only after the batch SQL commits, using RETURNING id and a status_code IS NULL fence to guarantee exactly-once terminal persistence. If the durable write fails or the row is already finalized, a conditional fallback (updateMessageRequestDetailsIfUnfinalized) prevents overwriting an existing terminal status. The write buffer now uses a dedicated writer-lane DB handle, an evictable min-heap for priority-aware overflow dropping, and aggregated overflow logging. Public-status rollup callbacks fire only after the batch commit acknowledgement, preventing premature or duplicate rollup emission when a timeout fallback races with a late primary write.
… leases atomically Consolidate all rolling-window and fixed-window cost writes into one Redis pipeline per trackCost / trackUserDailyCost call, eliminating sequential round-trips. The unified TRACK_COST_ROLLING_WINDOW script is now write-only (cleanup + append + TTL repair) and no longer scans the full ZSET on every successful request. Add settleLeaseBudgets: a single Lua invocation that validates all twelve lease keys, applies decrements, and writes an idempotency marker in one atomic step. Replace twelve fire-and-forget decrementLeaseBudget calls with one settlement, preventing duplicate deductions on ioredis reconnect. Configure commandTimeout, socketTimeout, and autoResendUnfulfilledCommands on the shared Redis client so a TCP blackhole cannot grow the command queue or replay timed-out writes after reconnection.
…uiescence AsyncTaskManager.register now accepts a factory invoked after admission rather than a pre-started Promise, enabling shutdown to abort and join all pending generations—including tail tasks registered by abort listeners. shutdownAll returns a shared Promise that loops until the pending set is empty. runApplicationCleanup treats async-task settlement, writer flush, and DB pool close as non-detachable critical barriers: per-step timeouts become soft warnings, and failures propagate to server.js which exits non-zero. The hard-exit watchdog timer is now referenced so the process stays alive long enough to report a truthful exit status.
Replace the raw headersToRecord helper with redactHeaders so authorization, cookie, x-api-key, and set-cookie values are masked before being written to Langfuse generation metadata, preventing secret leakage in observability exports.
Verify that buildRedisOptionsForUrl sets commandTimeout, socketTimeout, and autoResendUnfulfilledCommands on both redis:// and rediss:// URLs, and that REDIS_COMMAND_TIMEOUT_MS overrides the defaults. Guards the rate-limit performance commit against silent regressions in Redis client hardening.
…callback The public-status rollup test for timed-out durable waiters previously typed onCommitted as () => void and invoked it with no arguments, mismatching the real DurableMessageRequestUpdateOptions contract which receives the committed MessageRequestUpdatePatch. Align the test mock and invocation so the delayed-commit path exercises the actual callback signature. Also fix import ordering in message.ts to satisfy the formatter.
Replace fire-and-forget safeSend with a per-WebSocket outbound queue that caps pending bytes at 1 MiB, serializes sends behind a single in-flight callback, and pauses the upstream SSE response until the client drains. Late callbacks from a closed socket are invalidated by generation counter so they cannot deliver stale frames. Guard the internal HTTP request body write with a 30 s drain timeout and destroy both the request and response when the client vanishes mid-payload. Error and close paths now send a structured error frame before initiating the WebSocket close handshake so clients always receive a terminal event.
Capture the sessionId at increment time into a dedicated variable so the finally block decrements exactly the session it acquired, even if ProxySession.fromContext or the guard pipeline mutates session state before forwarding begins. Previously the finally block re-read session.sessionId, which could be null or different by the time the handler reached its cleanup path, leaking a concurrency slot. Add unit tests covering early guard rejection, successful forwarding, post-session error translation, and pre-session decode failures.
Replace the cloned-response reader pattern with a single demand-driven response pump that feeds the client stream and observes chunks for stats collection. The client now receives a new Response wrapping the pump stream instead of the original upstream Response, eliminating the unbounded clone that held a second copy of every streaming byte in memory. The pump arms a 60 s deadline on each unconsumed lookahead chunk so a connected-but-non-reading client cannot pin the upstream connection indefinitely. Client cancellation propagates to the source even when the cancel observer throws, and reentrant cancel calls preserve the first hard-cancel owner. Response handler terminal paths now use durable persistence with conditional fallback and bounded failure deadlines so a hanging database cannot stall the streaming task indefinitely.
…king Switch error-handler from separate duration and details writes to a single updateMessageRequestDetailsDurably call that includes durationMs, ensuring the Langfuse trace, persistence commit, and status-tracker endRequest fire in deterministic order. The trace is emitted first, the durable write is awaited, and only then is the status tracker released — so a database failure surfaces as a rejected handler rather than a silently orphaned trace. Add comprehensive unit tests for client-safe message sanitisation, override application, terminal status mapping, durable persistence ordering, and the end-to-end terminal outcome contract.
…ansport controller Replace combineAbortSignals polyfill with a dedicated AbortController whose abort is driven by lightweight bindClientAbortListener registrations on the response and client signals. The client-signal listener is cleaned up as soon as the forwarder obtains the upstream response, so a client disconnect after headers no longer aborts the in-flight transport — only the response controller retains that authority through stream consumption. Add an integration test exercising real loopback transports through the hedge lifecycle: winner settlement fences loser timers, each launched transport releases its agent exactly once, database overload is classified before fanout, and hedge winner/loser billing fires exactly once per request.
Add a PostgreSQL integration test that exercises the async message write buffer under row-level locks: mixed durable/ordinary batches settle only after commit, timed-out primaries yield to fallback ownership, retired pending patches are excluded from reinserted generations, a saturated 5000-entry queue evicts non-terminal patches while retaining terminal priority, and winner-cost retries respect the bounded cadence with authoritative loser-cost accumulation.
Align test assertions with the production call site, which now requires a timezone argument to resolve date presets deterministically.
…, and readback Add a shared drizzle query tracing harness and comprehensive unit tests for the message repository: terminal detail writes with durable acknowledgement and CAS-guarded unfinalized claims, winner and hedge-loser cost accounting with idempotent retries, session stats aggregation with provider/model/cache-TTL grouping, paged session request queries with adjacent-sequence lookup, usage-log filtering with ledger fallback, and public readback projections for single-request, session, and audit lookups.
Diagnostic reports now exclude the entire process environment via --report-exclude-env in both Dockerfiles. Crash handlers wrap database errors through findSafeDatabaseError before writing report files or stderr fallbacks, preventing DrizzleQueryError SQL text and bound parameters from reaching any public boundary. The proxy error handler returns a generic 503/500 for database failures without loading system settings or awaiting durable persistence. Langfuse traces and session metadata strip x-cch-* reserved internal headers. clearSessionProvider uses a Redis CAS compare so late cleanup from a prior provider cannot delete a binding already moved to a new one.
The first durable write claimant owns the terminal patch; later contenders observe its acknowledgement but cannot overwrite the committed outcome. Request duration is merged into the same CAS patch so overflow or process exit cannot leave a permanently active record. Post-terminal side effects (provider circuit mutation, session binding clear, Codex cache binding) fire from an onCommitted callback after the SQL write is acknowledged rather than speculatively before it. server.js WebSocket turns track per-turn request, response, and settle lifecycle: sendErrorAndClose aborts the active internal request before queuing a fatal frame, and terminal events wait for their WS send acknowledgement before releasing turn ownership.
Background schedulers (probe, public-status, log-cleanup) now expose their in-flight promise so shutdown awaits true quiescence instead of racing a fixed timeout. Bull queues are joined before DB pool and Redis closure; failures are collected into an AggregateError so critical cleanup still runs. Ledger backfill registers as a cancellable AsyncTaskManager job with abort-signal checkpoints between batches. The lease settlement Lua script consumes the cached slice when a request exceeds the remaining budget, preventing repeated overshoot within the refresh window.
Biome 2.5.4 emits lint/correctness/noUnsafeOptionalChaining on the two process.report?.writeReport accesses in the crash-handler test because the optional-chained result flows into an unconditional .mock property access (would throw TypeError if process.report were undefined). process.report is guaranteed present in these cases: writeReport is only spied when process.report exists (beforeEach), and excludeEnv is asserted on the line above. Switch to a non-null assertion (noNonNullAssertion is off in biome.json). Behavior unchanged; restores `bun run lint` to exit 0. CI Run: https://github.com/ding113/claude-code-hub/actions/runs/29559924912 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d9f63954c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| let errorCategory = await categorizeErrorAsync(error); | ||
| lastErrorCategory = errorCategory; | ||
| const statusCode = error instanceof ProxyError ? error.statusCode : undefined; | ||
| const databaseError = findSafeDatabaseError(error); |
There was a problem hiding this comment.
Handle local DB errors before retrying hedge providers
In hedge mode, when a Drizzle query error that is not a pool-admission error is thrown while an attempt is being prepared or handled, databaseError is non-null but errorCategory remains SYSTEM_ERROR because categorizeErrorAsync only maps admission errors. The code then records an endpoint failure and falls through to provider failure/alternative launch logic, even though switching providers cannot fix a local DB outage and can poison endpoint health. Mirror the non-hedge path by overriding the category or branching on any databaseError before endpoint accounting.
Useful? React with 👍 / 👎.
CI Auto-Fix
Original PR: #1341
Failed CI Run: Non-Main Branch CI/CD
Failed Job:
dev-build-deploy(bun run lintstep)Root Cause
bun run lintexited 1 with 2 errors (lint/correctness/noUnsafeOptionalChaining) intests/unit/instrumentation-crash-handler.test.ts. The Biome spec floats (@biomejs/biome: ^2, no lockfile) and resolved to 2.5.4, whosenoUnsafeOptionalChainingrule flagsprocess.report?.writeReportbecause the optional-chained result flows into an unconditional.mockaccess (would throwTypeErrorifprocess.reportwereundefined). The 196useOptionalChainfindings are warnings and do not fail the build.Fix Applied
tests/unit/instrumentation-crash-handler.test.tsprocess.report?.writeReport->process.report!.writeReportprocess.reportis guaranteed present here:writeReportis only spied whenprocess.reportexists (beforeEach), andexcludeEnvis asserted on the preceding line.noNonNullAssertionisoffinbiome.json, so the non-null assertion is permitted. Runtime behavior is unchanged.Verification
bun run typecheckpasses (exit 0)bun run lintpasses (exit 0, 2 errors resolved; only pre-existing warnings/infos remain)vitest run tests/unit/instrumentation-crash-handler.test.ts-> 9/9 tests pass?.->!on a guaranteed-present objectAuto-generated by Claude AI
Greptile Summary
This PR introduces a single commit on top of the
perf/full-path-benchmark-optimizationbranch that fixes two Biome 2.5.4lint/correctness/noUnsafeOptionalChainingerrors in the crash-handler test suite. The 129 files listed in the PR diff belong to the underlying branch, not this fix commit.process.report?.writeReport→process.report!.writeReportin the two "genuine errors" test cases; the optional-chain form was flagged because.mockis accessed unconditionally on the chain result, which would throwTypeErrorifprocess.reportwereundefined.expect(process.report?.excludeEnv).toBe(true)on the preceding line already acts as an implicit non-null guard. In the second test, thebeforeEachspy setup only runs whenprocess.reportexists, and Node.js always surfacesprocess.report. ThenoNonNullAssertionrule is confirmed\"off\"inbiome.json.Confidence Score: 5/5
The change is a two-line test-only substitution that replaces optional-chain syntax with non-null assertions; it introduces no logic changes and restores CI green.
The only touched file is a test file, and both non-null assertion sites are correctly backed — the first by an explicit
toBe(true)guard on the preceding line, the second by thebeforeEachspy condition that only runs whenprocess.reportexists. ThenoNonNullAssertionrule is confirmed disabled inbiome.json. No source files are modified.No files require special attention;
tests/unit/instrumentation-crash-handler.test.tsis the only changed file in the fix commit and the substitution is mechanically correct.Important Files Changed
?.→!.replacements onprocess.report.writeReportto resolve Biome noUnsafeOptionalChaining; both sites are correctly guarded (by assertion orbeforeEachspy condition), and noNonNullAssertion is off in biome.json.Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Test participant beforeEach participant processReport as process.report (Node built-in) participant uncaughtException as crash handler beforeEach->>processReport: if process.report exists → vi.spyOn writeReport processReport-->>beforeEach: spy installed Test->>uncaughtException: uncaughtException(error) uncaughtException->>processReport: "report.excludeEnv = true" uncaughtException->>processReport: report.writeReport(filename, toSafeCrashError(error)) processReport-->>uncaughtException: empty string (mocked) Test->>processReport: expect(process.report?.excludeEnv).toBe(true) — implicit guard Test->>processReport: process.report!.writeReport — non-null assertion safe Test->>Test: assert reportCalls[0][1] matches expected error%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Test participant beforeEach participant processReport as process.report (Node built-in) participant uncaughtException as crash handler beforeEach->>processReport: if process.report exists → vi.spyOn writeReport processReport-->>beforeEach: spy installed Test->>uncaughtException: uncaughtException(error) uncaughtException->>processReport: "report.excludeEnv = true" uncaughtException->>processReport: report.writeReport(filename, toSafeCrashError(error)) processReport-->>uncaughtException: empty string (mocked) Test->>processReport: expect(process.report?.excludeEnv).toBe(true) — implicit guard Test->>processReport: process.report!.writeReport — non-null assertion safe Test->>Test: assert reportCalls[0][1] matches expected errorReviews (1): Last reviewed commit: "fix(test): resolve unsafe optional chain..." | Re-trigger Greptile