Files
harness-tools/docs/fast-browser-live-validation.md
jb fd63b0ccec
fast-browser scenarios / Offline scenario contract tests (push) Failing after 44s
fast-browser scenarios / Local Chromium headless scenarios (push) Failing after 23s
fast-browser-with-jev
2026-09-20 12:24:08 -04:00

20 KiB

Fast-browser live validation (milestone 1)

extensions/fast-browser/live-validation.ts is an opt-in, local-only integration harness. It starts an HTTP fixture on a random loopback port, creates a unique temporary profile root and CDP lease root, and uses only the typed BrowserSessionManager, BrowserBridge, and runSteps APIs. It never loads normal user profiles, reads existing manual sessions, contacts JEV/provider APIs, or uses external accounts. Literal accessible names are required; the injected decider throws if a semantic decision is attempted.

The fixture's /setup response sets a synthetic, run-unique persistent cookie (Max-Age=3600). Its page makes a bounded /ready?case=setup request; the server resolves readiness only after receiving that cookie, so this is evidence that Chromium processed the seed response—not a manual credential login. The visible setup launch is asserted to have no CDP or headless flags. Automation, when unblocked, reopens the same named profile and verifies retention with a visible postcondition after a literal fill and click. Headless launch, two concurrent isolated named profiles, same-profile contention, closing one while the other remains alive, and cleanup are covered independently.

Integration command

From the repository root, with the existing pinned environment:

node --experimental-strip-types extensions/fast-browser/live-validation.ts

The focused test is opt-in and does not start a browser during ordinary test runs. It currently fails honestly on the visible-close blocker above until the production lifecycle fix lands:

FAST_BROWSER_LIVE=1 node --test --experimental-strip-types \
  extensions/fast-browser/live-validation.test.ts

The harness requires /usr/bin/chromium and .pi/fast-browser-demo/venv/bin/python with browser-harness==0.1.13. It uses finite startup, action, readiness, worker, and close deadlines. Cleanup reports owned process/root leftovers rather than terminating anything it did not own.

Validation evidence (2026-09-19)

Runtime versions: Chromium 152.0.7977.75 Arch Linux; Python 3.12.13; browser-harness 0.1.13; Node v22.23.2.

  • Blocked: visible manual setup seeded the cookie and /ready proved it. manager.close() returned, establishing that the manager's tracked Chromium root had exited, but owned GPU descendant PID 2870508 still carried the temporary profile --user-data-dir; SingletonLock was still present and the next manager.open() rejected the profile. Visible automation was skipped rather than bypassing the lock. The PID is from this disposable test profile only.
  • Passed: headless launch with bounded literal fill/click and verified visible postcondition.
  • Passed: two different headless named profiles concurrently, server-verified cookie/state isolation, same-profile contention rejection, and profile B remaining alive after profile A closed.
  • Passed: final owned cleanup; no test processes or temporary root remained.

This is a production lifecycle blocker, not a fixture workaround. The minimal proposed fix is for the session manager to own/wait for the complete Chromium process tree (including GPU/renderer descendants) and confirm the profile lock is gone before releasing a named-profile lease, within its existing bounded close deadline. No production module or package manifest was changed in this milestone.

Controlled Linux rerun (2026-09-20)

The Linux-only PID review was cleared for this controlled run. Preflight used the real /usr/bin/chromium and a real positive version-check PID 3222628 (parent 3222621, not the validator process); the pinned environment remained Python 3.12.13 with browser-harness 0.1.13. No fake process or PID seam, user profile, or external origin was used.

The original M1 command was bounded with timeout -k 5s 180s. Cookie seed/readiness reached the visible setup close boundary, but close failed closed with browser process-group ownership could not be confirmed; preserving session state; automation reuse was skipped. Headless bounded fill/click and concurrent profile isolation/contention passed. Cleanup correctly preserved the uncertain owned root /tmp/fast-browser-live-validation-5HvmBr rather than deleting it. Ten owned Chromium processes remained at capture: 3222696, 3222709, 3222710, 3222712, 3222738, 3222740, 3222752, 3222761, 3222770, 3222771; the root process was 3222696 and GPU descendant 3222738. This run exited 124 from the finite timeout and is evidence for the lifecycle blocker, not a release claim.

The canonical development validator then ran with a bounded 240s timeout. All six cases passed: input-safe read/assert/navigation/reload, popup/no-auto-switch/targeted-read selection, A→B evidence isolation, narrowed peer-origin filtering with base restoration, private PNG+diagnostics capture, and honest provenance handling (Evidence provenance unavailable: false). Its private artifacts lived under a unique /tmp/fast-browser-runtime-live-* root and were removed after owned cleanup; no canonical-run process/root remained. The report artifact is this document; non-Linux/global release clearance remains pending.

Isolated managed-CLI batch (2026-09-20, no rerun after this evidence)

After the independent broker/provenance review cleared, a single sequential native-Linux batch ran with /usr/bin/chromium 152.0.7977.75, Python 3.12.13, browser-harness 0.1.13, and Node v22.23.2. HOME, PI_CODING_AGENT_DIR, XDG config/data/cache/state/runtime roots, TMPDIR, cwd, scenario JSON copies, and report roots were unique and owned. The four loopback ports were unique and all managed servers were started only by scenario-cli; no active harness-test profile, provider key, JEV call, or external origin was used. Synthetic FAST_BROWSER_TEST_PASSWORD=demo-password was local-only and did not appear in reports, stdout, or diagnostics.

The M1 validator reported: manual setup reached cookie readiness and manager.close() returned, but its stale tombstone assertion expected /Unknown browser session/; the manager intentionally returned Browser session is no longer available: session closed. Headless launch and the two-profile case then failed closed on process-group ownership, and the finite command exited 124. The validator assertion is now corrected; the fixture now sends Max-Age=3600, but no post-change browser rerun has been made.

The preserved setup profile from that batch was inspected without launching a browser. The fixture source used at capture time had only Path=/; SameSite=Lax and no Expires or Max-Age, so it created a session cookie rather than a persistent cookie. HTTP receipt is nevertheless proven: /ready?case=setup only resolves after the server sees the exact cookie value, and the M1 run reached that readiness. After the setup close, Default/Cookies was SQLite journal_mode=delete with zero rows, Cookies-journal was zero bytes, no Cookies-wal existed, and SingletonLock was absent; no pending WAL/checkpoint explains the absence. The tracked root had closed and no live process for the named profile remained. Therefore this batch does not prove profile-cookie reuse: the zero row is explained by the old validator fixture's session-cookie attribute, while the independent root-exit-before-first-census lifecycle blocker still prevented the automation phase.

The three managed examples ran from disposable JSON copies. local-form and local-login completed all browser operations, but their reports were failed because owned browser cleanup did not complete before the bounded outer timeout (124). The corrected-port local-popup navigation passed, then its click failed because the example's postcondition (Scenario fixture ready) already held before the click; the worker correctly refused to treat that as proof of a mutation. The intentional failed-assert scenario reached assertion timed_out after a credential fill, retained a real PNG and diagnostics JSON, and produced a nonzero failed report. Its report was mode 0600 in an owned mode-0755 directory; artifact files were 0600 under mode-0700 roots; diagnostics contained password=[redacted] and the synthetic secret was absent.

Every browser and crashpad group left by the timed-out commands was later cleaned only after verifying current PID/start-time/UID/executable/argv/ancestry/PGID/SID ownership against the unique batch roots. All managed ports were closed and no batch-owned Chromium or crashpad process remained. The M1 cleanup proof is retained at /tmp/fast-browser-next-validation-uqoyor/m1-cleanup.json; per-scenario cleanup proofs and the complete summary are under /tmp/fast-browser-next-validation-uqoyor/ (batch-summary.json). Disposable profile and retained-evidence roots remain preserved for diagnosis. This batch is validation evidence, not release clearance.

Follow-up M1 gate rerun (2026-09-20, Linux; CLI batch intentionally not started)

A final ownership review cleared another single native-Linux run with fresh roots, /usr/bin/chromium, the pinned Python environment, isolated HOME/XDG/TMPDIR/cwd/config, and no provider or active user-profile state. The original M1 ran first and was not a full pass: visible manual setup seeded the synthetic cookie and closed cleanly, but visible automation failed its retained-session click with Postcondition not verified; action was not retried. Headless bounded fill/click and the two named-profile isolation/contention cases passed. The validator reported owned cleanup passed and exited 1 solely for the automation case.

Because M1 did not pass, the gate correctly stopped before launching any managed CLI examples or intentional-failure scenario. A post-run /proc census found no Chromium or crashpad process carrying the fresh batch roots, and no subsequent CLI ports existed. The batch root is /tmp/fast-browser-final-validation-ewubUW; its bounded M1 output is m1.stdout. Chromium left only disposable org.chromium.Chromium.* socket directories under the isolated TMPDIR; these contain no live owned process and were preserved rather than removed blindly.

The stale recent-exit tombstone assertion and validator cookie are now corrected in live-validation.ts: closed-session lookup expects session closed, and /setup sends Max-Age=3600. This rerun therefore does not reproduce the earlier session-cookie explanation, but it also does not prove profile-cookie reuse: the named profile was removed after owned cleanup and no retained profile database remained for inspection. The remaining failure is separate from the validator assertion and broker census fixes: Chromium processed the seed request, yet the post-close automation session did not reach the retained postcondition. No production spec was changed and no release or cookie-reuse claim is made.

Independent managed-CLI rerun after the M1 gate (2026-09-20)

The M1 cleanup gate passed even though retained-session automation did not, so the independent CLI proof was run separately with fresh isolated state at /tmp/fast-browser-cli-final-OqHfA6. The environment again used only /usr/bin/chromium, the pinned Python environment, unique loopback ports, isolated HOME/XDG/TMPDIR/cwd/config roots, synthetic local credentials, and no provider keys or user profiles.

Successful managed examples:

  • local-form: exit 0; all two operations passed; cleanup passed.
  • local-login: exit 0; all three operations passed using the synthetic environment credential; cleanup passed.
  • local-popup: the original immediate-popup copy reached the click but timed out in waitPopup (exit 1, cleanup passed). The failure is retained as a production target-inventory race finding: the click postcondition passed, but the popup was not discovered by the opener-bound wait. A disposable diagnostic copy with a 100 ms window.open delay then passed navigation, click, popup discovery, and read with exit 0; that delay is diagnostic evidence only, not popup clearance. The source fixture was restored to immediate popup behavior with its status-change fix preserved.

The intentional failed-assert scenario was changed to wait: "none" so the expected assertion failure remains non-aborted and evidence capture can complete. It exited 1 promptly (~506 ms), reported assertion not_satisfied, retained a real PNG and diagnostics JSON, and cleaned owned browser/fixture resources. The report is 0600 in the owned 0755 reports directory; artifact files are 0600 under 0700 roots. Diagnostics contain password=[redacted]; the synthetic credential is absent from report and stdout. Final census found no batch-owned Chromium/crashpad processes and no open managed ports.

The immediate-popup failure produced no retained evidence because the runtime operation aborted before the CLI's failure capture could run; cleanup still passed and the CLI removed its disposable profile. This is reported as a production/runtime evidence-retention finding, not hidden by treating cleanup success as scenario success. The validator now preserves failed M1 roots even when process cleanup succeeds and records automation cookie/outcome evidence for any future rerun.

Diagnostic retained-profile rerun (2026-09-20)

With process cleanup verified and no popup workaround retained, only the retained-profile prerequisite and automation case were run from a fresh isolated root /tmp/fast-browser-retained-run-gizlJ0. No headless, isolation, popup, or managed-CLI case was launched.

  • Setup readiness passed, named setup closed, the profile remained, and no profile process remained.
  • Setup persistence metadata was directly inspected at .../Default/Cookies: 20,480-byte database, zero rows, zero journal bytes, zero WAL bytes. The final post-automation metadata was identical. There was therefore no persisted Chromium cookie row to reuse; no cookie value or token was logged.
  • Automation readiness passed, but server telemetry recorded cookieHeaderPresent: false, cookieReceived: false, and retained: false.
  • The exact fill of Milestone verified. The exact click dispatched once with mutationState: unknown / actionMayHaveExecuted: true; it was never retried. Expected text was Retained session verified; the fixture's actual bounded outcome was Fixture action did not meet its bounded condition, and the controller returned Postcondition not verified; action was not retried.
  • Cleanup passed with no remaining owned processes. The failed diagnostic root was deliberately preserved mode 0700, with summary mode 0600 at /tmp/fast-browser-retained-run-gizlJ0/tmp/fast-browser-retained-diagnostic-0Rkywk/retained-diagnostic-summary.json.

This proves the retained-session failure is an actual missing-cookie condition in this isolated run, not merely an unrecorded postcondition mismatch. It does not establish cross-platform or global release clearance.

Immediate-popup managed rerun (2026-09-20)

The restored immediate-popup fixture was run alone from fresh isolated state at /tmp/fast-browser-popup-final-8qElTH, using native Linux /usr/bin/chromium, the pinned Python environment, an explicit unique loopback port, and isolated HOME/XDG/TMPDIR/cwd/config roots. The disposable scenario verified the full sequence click → waitPopup → selectTab → read without exposing tab IDs to the model.

The managed CLI exited 0 in 530 ms. Navigate, click (Popup opened postcondition), waitPopup, explicit selectTab, and popup read all passed; cleanup passed. Final ownership census found no batch-owned Chromium/crashpad processes and no open managed port. No popup delay workaround was used, and no timeout-metadata clearance claim is made.

The current leader-first implementation was tested once on a fresh isolated Linux profile at /tmp/fast-browser-persist-run-9J6jRV/tmp/fast-browser-persistent-cookie-m1-NKPozp. It used /usr/bin/chromium, the pinned Python environment, a disposable loopback fixture, and the explicit Max-Age=3600; Path=/; SameSite=Lax cookie. Setup was visible and no-CDP: its process argv had no --headless or remote-debugging flag.

  • Setup readiness and close passed; the server received the synthetic cookie and the named profile survived with no remaining browser process.
  • Post-close SQLite metadata showed one persistent row for the loopback host, hasExpires: true, isPersistent: true, expiresUtc populated, and encrypted value storage; raw cookie values were not recorded.
  • Same-profile automation received the cookie (cookieHeaderPresent: true, cookieReceived: true, retained: true). Fill and retained-session click both verified; actual outcome was Retained session verified; no action retry occurred.
  • Final metadata still showed the one persistent row. Cleanup passed with no owned Chromium/crashpad processes; the failed-case-safe root was preserved mode 0700 and the summary is mode 0600.

Conclusion: root-first fixes persistent-cookie reuse in this controlled current Linux run. This is scoped evidence only; it is not global or non-Linux release clearance.

Final live integration pass (2026-09-20)

The current tree was validated with fresh isolated Linux roots and no user profiles, providers, or fixture timing workaround.

  • Full original M1: exit 0; visible setup persistence, retained-session automation, headless bounded action, and two-profile isolation/contention all passed. Cleanup passed.
  • Canonical development matrix: exit 0; all six cases passed with Evidence provenance unavailable: false.
  • Managed local-form: exit 0; cleanup passed.
  • Managed local-login: exit 0; cleanup passed.
  • Managed immediate local-popup: exit 0 in 531 ms; navigate, click, waitPopup, explicit selectTab, and read passed; cleanup passed.
  • Intentional assertion failure: exit 1 in 558 ms with assertion not_satisfied, retry: never; private PNG and diagnostics were retained. Report mode was 0600 in a 0755 report directory, artifacts were 0600 under 0700 roots, and diagnostics retained password=[redacted] only.

The real overall-deadline case used a no-popup waitPopup with a 3,000 ms scenario budget, below the operation's bounded wait. It exited nonzero (1) naturally in 3,066 ms; navigation passed and waitPopup aborted with mutationState: not_attempted, retry: never. Evidence was explicitly represented as private unavailable evidence (status: failed, error browser session is unavailable). The exact report is /tmp/fast-browser-final-batch-boijm3/reports/overall-deadline-no-popup.json; its scenario cleanup is { status: "failed", detail: "browser session cleanup failed" }, and the top-level cleanup is { status: "failed", detail: "one or more owned resources failed cleanup" }. This is the handoff finding for the CLI owner. Validation stopped immediately as required. No force-kill or mutation retry was used. A final ownership census still found no batch-owned Chromium/crashpad processes and no open managed ports, but this does not convert the reported cleanup failure into a pass.

The failed batch root remains preserved at /tmp/fast-browser-final-batch-boijm3; the complete summary is /tmp/fast-browser-final-batch-boijm3/final-validation-summary.json. No production or validation-source changes were needed for this pass.

Deadline cleanup-race rerun after manager fix (2026-09-20)

After the manager/CLI cleanup-race fix, only the real overall-deadline/no-popup case was rerun from fresh native-Linux isolated state at /tmp/fast-browser-deadline-rerun-iaE8de; the earlier failure report was not overwritten. The no-popup fixture used a 3,000 ms scenario budget below the bounded popup wait.

The new report is /tmp/fast-browser-deadline-rerun-iaE8de/reports/overall-deadline-no-popup-rerun.json. It exited 1 naturally in 3,090 ms: navigation passed, waitPopup aborted, and the report preserved retry: never, mutationState: not_attempted, and private unavailable evidence (browser session is unavailable). Cleanup now passed with owned runtime, artifacts, browser session, and fixture resources cleaned. Final census found no rerun-owned Chromium/crashpad process and no open port. The rerun root remains preserved mode 0700, and the report is mode 0600.