Repository navigation
Description
Summary
pm-stack/meta-exporter/meta-quota-export.mjs has no timeout anywhere in its CDP plumbing, so a browser debug endpoint that accepts the connection but never answers hangs the exporter forever instead of producing one of its designed failure documents:
httpJson(meta-quota-export.mjs, CDP plumbing section) —await fetch(url)with noAbortSignal; used for${cdpBase}/json/listin bothrun()andfetchPaygBalance().connectWs— the promise resolves only onws.onopenand rejects only onws.onerror; if the socket opens but the page never answers,send()'s per-call promise is stored inpendingand never settled (noonclosehandler rejects pending calls), soevaluate()(which awaitsclient.send("Runtime.evaluate", …)) hangs indefinitely.- The in-page
fetchcalls infetchQuotaPayloadslikewise carry no timeout, thoughRuntime.evaluatewithawaitPromise: truemeans a hung in-page fetch also never returns.
The module's own header documents the contract this breaks: every failure mode is supposed to write a failure document (cdp-unavailable exit 4, fetch-failed exit 7, …) so a failed tick "can never be mistaken for a reading", and consumers derive laptop-unreachable only from the age of latest.json. A hang produces no document at all — under the shipped systemd units the only backstop is meta-quota-export.service's TimeoutStartSec=120, which SIGTERMs the process: still no failure document, no exit-code semantics, and the stale latest.json is then misread downstream as the laptop being unreachable rather than the browser/CDP being wedged.
Evidence (verified this run on main c58507a3)
- Grep: no
timeout/AbortSignal/setTimeoutanywhere inmeta-quota-export.mjs. connectWs:pendingpromises are resolved/rejected only inws.onmessage; there is nows.onclosehandler at all, so a socket that closes mid-Runtime.evaluatealso leavesevaluate()pending forever.- Sandbox probe: started a TCP server on
127.0.0.1:19299that accepts connections and never replies, then ran the shipped exporter against it:
node meta-quota-export.mjs --cdp-port 19299 --out-dir /tmp/probe-metaexp/outundertimeout 8→ exit 124 (killed bytimeout; the exporter never returned), and the out-dir contained zero files — nolatest.jsonfailure document, nohistory.jsonlentry. - Control behaviour by contrast: an unreachable port (connection refused) does produce the designed
cdp-unavailablefailure document via thecatchinrun()— it is specifically the accepts-but-never-answers case that has no path to any outcome.
Why it matters
This exporter is the Meta pool's only quota feed (code#1301, epic #1149); the aggregator's pool decisions ingest its file export every 15 minutes. A wedged browser page or CDP endpoint currently turns into up to 120 s of systemd kill-wait per tick, zero diagnostics in the export stream (the failure taxonomy exists precisely to name this state), and a downstream signal that points operators at the laptop's reachability instead of the browser session.
Acceptance criteria
- Every CDP HTTP fetch (
/json/list) carries a bounded timeout (e.g.AbortSignal.timeout(...)) that surfaces as the existingcdp-unavailablefailure document + exit 4. - WebSocket
send()calls carry a bounded deadline, andws.onclose/onerrorrejects all pending promises, so a hung or closed socket surfaces asfetch-failed(exit 7) instead of hanging. - The in-page quota fetch is bounded (directly, or transitively via the
Runtime.evaluatedeadline above). - A test (the file already has
meta-quota-export.test.mjs) covers the accepts-but-never-answers endpoint: the run terminates with a failure document well inside the systemd 120 s budget. - Healthy-path behaviour unchanged: success document shape, failure taxonomy, and exit codes all as documented in the module header.
Dedup / distinctness
- meta-exporter: window_duration_secs is never validated — a missing/junk duration exports a 'window-NaNs' window as a successful reading #1566 (CLOSED) was
window_duration_secsvalidation in the quota parser — unrelated to CDP transport timeouts. - feat(quota-plane): Meta adapter Phase 1 — laptop-side quota exporter (subscription-quota API, 15-min file export) #1301 / plan(quota-plane): exception integration — Meta (login, token capture, console API) #1167 / epic(quota-plane): Stage 2 — 15-minute quota gathering in the PM stack, token adapters, automated pool decisions #1149 (OPEN) are the feature/epic for this exporter; none tracks a transport-deadline gap (searched
meta-exporter OR meta-quota-export, all states). - The proxy timeout issues (antigravity-ls-proxy: callRpc has no timeout — a language server that heartbeats but never answers an RPC hangs /v1/models and every chat request forever #2102, codex-proxy: exec transports have no turn deadline — a hung codex exec child hangs the request forever and is never killed #2157, muse-proxy: exec fallback path has no turn deadline — a hung muse exec child hangs the request forever #2159, clef-proxy: data-path upstream fetch has no timeout — a gateway that accepts and never responds holds the /v1/systemone request open (models fetch in the same file pins 10s) #2100) concern other services' upstream calls, not this exporter's CDP channel.
Activity
developing-today commentedon Oct 29, 2026
CODE-1068
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
34 remaining items
dezren39 commentedon Nov 2, 2026
PR opened: Task 5204 implementation is in PR #4138.
origin/main; output-document and exit mapping regressions are covered.dezren39 commentedon Nov 2, 2026
Parked: Task 5204 is parked for 20 minutes while PR #4138 checks run.
testworkflow is still queued/pending. CodeRabbit skipped.origin/main.dezren39 commentedon Nov 2, 2026
agent-note:v1 — task 5204 done
PR #4138 merged (merge commit 56e66c9): bounded CDP waits in pm-stack/meta-exporter (fetch/WebSocket/evaluate deadlines, pending-request rejection on close/error) with failure documents preserved; diff is exactly the two in-scope files.
Verification:
node --test pm-stack/meta-exporter/meta-quota-export.test.mjson the laptop worktree at PR head 3494110 — 21/21 pass (re-run fresh this shift). Codex review on the PR head completed with no findings. GitHub Actions test check was still queued in the Actions backlog (>50 min) at merge time; merged under Drew's standing merge authority with laptop verification, same as prior fleet merges during this backlog. Closing #2175 as fixed.