Context
Parent #16151 retained a real macOS Google Chrome startup abort whose faulting path is
_RegisterApplication → TransformProcessType → ChromeMain. The process died before Playwright
yielded a usable Browser fixture.
The E2E runner already exposes enough bounded evidence to make this class machine-readable:
- Playwright 1.61.1 races browser readiness against process exit. When launch rejects, its error
includes
<process did exit: exitCode=..., signal=...> in the browser log.
- Reporter
onError(error, workerInfo) receives errors outside test execution plus the producing
project when Playwright can bind one.
- Reporter
onTestEnd(test, result) receives fixture-launch failures bound to the exact test project.
- Existing local error artifacts preserve this shape for the related early-exit family, including
signal=SIGTRAP, no yielded Browser fixture, forced-kill escalation, and kill EPERM.
Neo's test/playwright/e2e/custom-reporter.js currently writes static system/project information at
run start and a terminal count to stdout. It does not normalize either reporter error path into the
retained benchmark-system-info.json receipt. The generic Playwright report can contain the raw
multi-line error, but downstream diagnosis must rediscover project, launch profile, transport state,
exit code, and signal by scraping prose.
The first focused unit run also exposed a prerequisite in that same owner: on this restricted Codex
seat, Node's os.uptime() throws EPERM. The reporter reads it outside its existing best-effort
system-info guard, so onBegin() aborts before creating the JSON receipt. Optional host telemetry
must not erase mandatory lifecycle evidence.
Live duplicate sweep: the latest open queue plus searches for pre-transport Chrome exit provenance, browser launch exit signal Playwright reporter, and RegisterApplication SIGABRT reporter found only parent #16151 and its single-owner child #16159.
The Problem
An early Chrome process exit already fails the affected test, but Neo retains no small,
privacy-bounded lifecycle record that answers:
- which Playwright project owned the launch;
- which Neo E2E launch profile was active;
- whether a usable
Browser fixture was ever established;
- which exit code or signal Playwright observed;
- whether the same event surfaced through both reporter paths.
The missing normalization is why a macOS dialog can become the practical diagnostic surface even
though the runner already held more precise launch-boundary evidence.
The Architectural Reality
- Playwright owns browser creation and graceful/forced cleanup for ordinary test fixtures.
- The E2E reporter owns run-level retained diagnostic metadata.
- Playwright can construct a pipe transport before
connectToTransport() rejects. Reporter callbacks
do not expose that internal boundary, so transport state must be recorded as not-observable.
- A reporter must classify only the exact rejected-launch process-exit shape. A generic
browser has been closed error can occur after a healthy transport and must not be relabeled.
- The process-exit line is the authoritative code/signal source. Absence must remain
null, never be
guessed from platform or dialog timing.
- Reporter callbacks can surface the same failure twice; one process event must produce one receipt.
- Raw launch command lines and user-data paths are not needed for this contract and must not be
copied into the normalized record.
The Fix
- Add a pure classifier to the existing E2E reporter for Playwright's exact rejected-launch
process-exit shape.
- Make nonessential host facts, including uptime and macOS CPU probes, fail soft without emitting
shell noise or preventing the run receipt.
- Capture both
onError(error, workerInfo) and onTestEnd(test, result) so global/fixture launch
failures share one path.
- Retain a deduplicated
browserLifecycle.launchExits collection in
benchmark-system-info.json with project, Neo launch profile, browserObjectEstablished: false,
transportState: not-observable, exit code, signal, abnormal/clean disposition, and a bounded
classification—not the raw command line. Persist when the incident arrives, not only at terminal.
- Unit-test positive parsing, negative classification, profile binding, duplicate collapse, and the
restricted-host telemetry fallback.
- Run one controlled non-GUI early-exit falsifier using a process that exits before Playwright
yields a
Browser; the runner must fail and the retained receipt must name the
project/profile/code without launching a visible browser.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| Browser launch rejection with process exit |
Playwright browser startup log + reporter worker/test project |
Persist one bounded lifecycle record |
Keep the ordinary Playwright failure; unknown fields remain null |
Reporter JSDoc |
Pure parser units + controlled non-GUI exit |
| Healthy or post-launch browser error |
Exact classifier boundary |
No rejected-launch record |
Generic Playwright reporting remains authoritative |
Inline avoided-trap guard |
Negative unit corpus |
| Duplicate reporter delivery |
Normalized incident identity |
Collapse to one record |
Preserve first bounded source attribution |
Reporter JSDoc |
Duplicate-delivery unit |
| Operator/browser privacy |
Neo E2E receipt contract |
Store no raw command line, URL, title, profile path, or personal browser content |
Omit rather than broaden |
Ticket + JSDoc |
Serialized-receipt assertion |
| Restricted host telemetry |
Existing reporter system-info block |
Optional facts fail soft and lifecycle receipt still writes |
null/existing fallback, no shell noise |
Reporter JSDoc |
Injected/seat EPERM unit |
Decision Record impact
none.
Acceptance Criteria
Out of Scope
- Reading or deleting macOS DiagnosticReports.
- Suppressing crash dialogs.
- Changing Playwright's browser ownership or cleanup implementation.
- Adding launch admission, sleeps, process-name cleanup, or a shared browser server.
- Proving the root cause of
_RegisterApplication aborts.
- Replacing the generic JSON/HTML Playwright reporters.
Avoided Traps
- Do not infer transport state from
browserType.launch() rejecting.
- Do not classify every
browser has been closed string as a launch-process exit.
- Do not infer
SIGABRT from the platform or dialog.
- Do not persist the raw launch command line merely because Playwright includes it.
- Do not create a second browser lifecycle to test the reporter.
- Do not turn a diagnostic reporter into the browser owner.
Related
- Parent: #16151
- #16159 / PR #16160 — removes the redundant no-op presenting owner.
- #15664 / #15813 — distinct GPU
SIGTRAP family whose artifacts establish the same Playwright
process-exit log grammar.
Origin Session ID: 019fac4d-7844-7422-9486-7f73ccf308f5
Retrieval Hint: "Playwright reporter rejected browser launch process exit project profile signal"
Context
Parent #16151 retained a real macOS Google Chrome startup abort whose faulting path is
_RegisterApplication → TransformProcessType → ChromeMain. The process died before Playwright yielded a usableBrowserfixture.The E2E runner already exposes enough bounded evidence to make this class machine-readable:
<process did exit: exitCode=..., signal=...>in the browser log.onError(error, workerInfo)receives errors outside test execution plus the producing project when Playwright can bind one.onTestEnd(test, result)receives fixture-launch failures bound to the exact test project.signal=SIGTRAP, no yieldedBrowserfixture, forced-kill escalation, andkill EPERM.Neo's
test/playwright/e2e/custom-reporter.jscurrently writes static system/project information at run start and a terminal count to stdout. It does not normalize either reporter error path into the retainedbenchmark-system-info.jsonreceipt. The generic Playwright report can contain the raw multi-line error, but downstream diagnosis must rediscover project, launch profile, transport state, exit code, and signal by scraping prose.The first focused unit run also exposed a prerequisite in that same owner: on this restricted Codex seat, Node's
os.uptime()throwsEPERM. The reporter reads it outside its existing best-effort system-info guard, soonBegin()aborts before creating the JSON receipt. Optional host telemetry must not erase mandatory lifecycle evidence.Live duplicate sweep: the latest open queue plus searches for
pre-transport Chrome exit provenance,browser launch exit signal Playwright reporter, andRegisterApplication SIGABRT reporterfound only parent #16151 and its single-owner child #16159.The Problem
An early Chrome process exit already fails the affected test, but Neo retains no small, privacy-bounded lifecycle record that answers:
Browserfixture was ever established;The missing normalization is why a macOS dialog can become the practical diagnostic surface even though the runner already held more precise launch-boundary evidence.
The Architectural Reality
connectToTransport()rejects. Reporter callbacks do not expose that internal boundary, so transport state must be recorded asnot-observable.browser has been closederror can occur after a healthy transport and must not be relabeled.null, never be guessed from platform or dialog timing.The Fix
onError(error, workerInfo)andonTestEnd(test, result)so global/fixture launch failures share one path.browserLifecycle.launchExitscollection inbenchmark-system-info.jsonwith project, Neo launch profile,browserObjectEstablished: false,transportState: not-observable, exit code, signal, abnormal/clean disposition, and a bounded classification—not the raw command line. Persist when the incident arrives, not only at terminal.Browser; the runner must fail and the retained receipt must name the project/profile/code without launching a visible browser.Contract Ledger
nullnull/existing fallback, no shell noiseEPERMunitDecision Record impact
none.
Acceptance Criteria
browserObjectEstablished: false,transportState: not-observable, exit code, and signal; unavailable values remainnull.browserType.launch()still records withabnormal: false.os.uptime()/platform-probe failure cannot prevent the bounded JSON receipt; unavailable optional host facts fail soft without shell noise.Out of Scope
_RegisterApplicationaborts.Avoided Traps
browserType.launch()rejecting.browser has been closedstring as a launch-process exit.SIGABRTfrom the platform or dialog.Related
SIGTRAPfamily whose artifacts establish the same Playwright process-exit log grammar.Origin Session ID: 019fac4d-7844-7422-9486-7f73ccf308f5
Retrieval Hint: "Playwright reporter rejected browser launch process exit project profile signal"