Files
harness-tools/docs/fast-browser-live-validation.md
T
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

126 lines
20 KiB
Markdown

# 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:
```bash
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:
```bash
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.
## Controlled persistent-cookie M1 comparison (2026-09-20)
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`.