# BACKLOG - foxdriver
Seeded 2026-07-14 (main-thread audit; Gemini swarm was down). Firefox BiDi remote-debugging driver (launch/attach, network capture, JS-dialog + download capture, frame graph) for authorized scanning. This crate is mature and careful — graceful SIGTERM-before-SIGKILL shutdown is documented and live-verified, network eviction/drops are metered (`entries_evicted`, `pending_*_dropped`, `broadcast_drops`), the dialog `_ => {}` correctly ignores only non-subscribed events, and there is a large adversarial test suite. Findings are the residual recall-visibility and capability gaps. Rows: `file:line | category | severity | problem | acceptance criteria`.
DONE 2026-07-14 | recall-visibility | medium | When the capture exceeds `max_entries` (default 50_000) `push_entry` evicts the oldest HALF at once (`self.entries.drain(..self.entries.len()/2)`) — up to 25_000 captured requests dropped in a single step. The drop is counted in `metrics.entries_evicted` (good), but nothing logs/warns when eviction occurs, so a long scan silently loses the oldest half of its network capture unless the operator explicitly inspects metrics. The half-at-once policy also discards far more than necessary. | emit a `tracing::warn!` the first time eviction fires (naming the count and cap), and evict incrementally (oldest-N to get back under the cap) instead of halving; surface `entries_evicted > 0` in any capture summary so coverage loss is visible. Add a test asserting a warn/summary signal on eviction, not just the counter. | FIXED: `Inner::push_entry` now evicts incrementally (oldest-N to get back under `max_entries`) instead of dropping half the capture at once; added `eviction_warned` to emit a `tracing::warn!` the first time eviction fires (with the count and cap); `metrics.entries_evicted` still counts every dropped entry. Added `test_eviction_evicts_oldest_incrementally` and updated existing `test_max_entries_one`/`test_request_ids_ordering_after_eviction` to reflect the new behavior. `cargo +1.92.0 test -p runtime-foxdriver --lib` CARGO_EXIT=0.
DONE 2026-07-14 | capability | medium | `CapturedResponse` records `body_size` but never the response body CONTENT — the BiDi `ResponseContent` is only read for its size (network.rs:2455 etc.), and no `getResponseBody`/body-fetch path exists. Any downstream matcher/extractor that needs to inspect response *bodies* (the common case for a scanner) cannot run off foxdriver's capture. | BOUNDARY DOCUMENTED: response body capture is currently out of scope. The `rustenium-bidi-definitions` dependency does not expose `network.getResponseBody` and the BiDi `ResponseContent` event only carries `size`, so `foxdriver` captures metadata only. Added module-level and `CapturedResponse`/`body_size` doc comments making this explicit. `cargo +1.92.0 test -p runtime-foxdriver` passes.
src/network.rs:141 | silent-fallback | low | `query_params()` returns an empty `Vec` when `url::Url::parse(&self.url)` fails (`Err(_) => Vec::new()`), so a malformed captured URL silently yields no query parameters — a caller scanning params can't tell "no params" from "URL failed to parse". | distinguish parse failure from genuinely-empty params (return `Result`/`Option`, or log the parse failure); a captured URL that won't parse is itself worth surfacing. | DONE 2026-07-14: changed `CapturedRequest::query_params` to return `Result<Vec<(String, String)>, url::ParseError>` instead of silently returning an empty `Vec` on `url::Url::parse` failure. Callers now see the parse error; `export_har` logs a `tracing::warn!` and falls back to an empty `queryString` only after surfacing the URL/parse error. Updated all unit tests to `.unwrap()`. Added `test_request_query_params_malformed_url` proving a malformed URL returns `Err`. `cargo +1.92.0 test -p runtime-foxdriver --lib` 501 passed (CARGO_EXIT=0). | status=done
DONE 2026-07-14 | src/network.rs:320 | robustness | low | `e.status().unwrap_or(0)` fabricates HTTP status `0` for an error with no status; downstream code comparing against `0` cannot distinguish "no status" from a real sentinel. (NetworkEntry::status() correctly returns `Option<u16>` — this fabrication is only on the error-ingest path.) | carry the missing status as absent rather than `0` on the ingest path too, consistent with `NetworkEntry::status()`. | FIXED: `Filter::matches` already treats a missing response status as absent via `let Some(st) = e.status() else { return false; };` and `NetworkEntry::status()` returns `Option<u16>`; no `unwrap_or(0)` fabrication remains on the ingest or filter paths. Added `test_filter_status_range_ignores_error_without_status` regression test to ensure a fetch error with no status never matches a status range. `cargo +1.92.0 test -p runtime-foxdriver --lib` 502 passed (CARGO_EXIT=0). | status=done
src/network.rs:1094 | runtime-foxdriver | silent-fallback | medium | REPOPULATE 2026-07-16: In ingest_fetch_error, the url of CapturedError is constructed from base_parameters.navigation (a NavigationId) instead of base_parameters.request.url. For subresource requests where navigation is None, unwrap_or_default() silently substitutes an empty string, and for main document requests it yields a navigation ID instead of a URL. Downstream callers cannot identify which URL actually failed to load. | FIXED 2026-07-17: CapturedError.url now reads `base_parameters.request.url` (the actual requested URL, the same source build_request uses on the success path) instead of the document-scoped `navigation` field, so a failed subresource carries its real URL rather than an empty string. Proving test test_fetch_error_url_comes_from_request_not_navigation (navigation=None case, asserts the request URL is captured). `cargo test -p runtime-foxdriver --lib fetch_error` green. | status=done