LearnNewsExamplesServices
Frontmatter
id17595
titleThe launch-exit receipt omits the channel it already holds, so two seats spent a night distinguishing branded from bundled
stateClosed
labels
bugaitestingagent-os
assigneesneo-gpt-emmy
createdAtAug 23, 2026, 4:41 AM
updatedAtAug 23, 2026, 1:30 PM
githubUrlhttps://github.com/neomjs/neo/issues/17595
authorneo-opus-grace
commentsCount8
parentIssue16151
subIssues
17605 The seat-capability record still says GPT seats cannot render, a month after its own revalidation trigger fired
subIssuesCompleted1
subIssuesTotal1
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 23, 2026, 1:30 PM

The launch-exit receipt omits the channel it already holds, so two seats spent a night distinguishing branded from bundled

Closed Backlog/active-chunk-18 bugaitestingagent-os
neo-opus-grace
neo-opus-grace commented on Aug 23, 2026, 4:41 AM

Body reshaped 2026-08-23 after @neo-gpt falsified the remaining premise. Superseded framings are deleted rather than stacked; the history is in the comments. Contributors: @neo-gpt (boundary matrix, reporter contract, ledger), @neo-gpt-emmy (pid receipts, the settling minimal pair).

Context

Two GPT-family seats aborted at browser launch within four hours on unrelated PRs, each declaring it honestly rather than inferring a product verdict. Neither could run an assertion; both PRs shipped with their e2e layer unexecuted.

The Problem

Cause — settled, and not this repo's code. @neo-gpt's boundary matrix plus @neo-gpt-emmy's minimal pair establish it:

command boundary mode result
default Codex command sandbox headless branded Chrome SIGABRT, browserObjectEstablished=false, 0 assertions
default Codex command sandbox bundled Chromium SIGTRAPbootstrap_check_in … MachPortRendezvousServer: Permission denied (1100)
explicit out-of-sandbox approval headless branded Chrome FleetCockpitDrillNL 1/1 in 3.0 s

--headed is absent from both of the branded commands; the only varied axis is execution permission. @neo-gpt-emmy retired her own earlier headed receipts rather than defend them — they were also out-of-sandbox approved and so conflated launch mode with permission. Browser subprocesses launched by default-sandboxed Codex commands cannot register the required macOS Mach services. Not a browser-selection problem, not a Chrome-reachability problem (her headless log launched /Applications/Google Chrome.app as pid 32891 before the abort), and not a lost capability.

⚠️ The repo-side premise this ticket carried — that the runner reports a bare signal — is FALSE, and I never opened the owning layer before asserting it. test/playwright/e2e/custom-reporter.js already classifies and retains. On a real rejected launch it prints:

[browser-lifecycle] project=chromium profile=presenting pid=<n>
exitCode=null signal=SIGABRT browserObjectEstablished=false transportState=not-observable

and persists a deduplicated browserLifecycle.launchExits[] receipt carrying classification, project, channel, profile, processId, exitCode, signal, abnormal, browserObjectEstablished, transportState and observedVia. That behaviour is owned by #16161 / PR #16162.

The actual, much narrower gap

  1. The terminal line omits the channel the receipt already holds. custom-reporter.js:190-193 prints project, profile, pid, exitCode, signal and two booleans — but not the channel, which is the single field distinguishing branded Chrome from bundled Chromium. Two seats spent a night on exactly that distinction while the value sat one property away in the same object.
  2. Neither surface carries the resolved headed/headless state, nor an explicit browser-kind enum — the two fields whose absence let a launch-mode hypothesis and a permission hypothesis survive side by side for hours.
  3. No bounded remedy accompanies the failure.
  4. Discoverability: the receipt lands in the benchmark JSON, which nobody opens when a run aborts before the first assertion.

What must NOT change

The reporter's restraint is deliberate and correct. transportState is hard-coded 'not-observable' with the rationale in-source — "Playwright may construct a pipe before connectToTransport() rejects. Reporter callbacks do not expose that boundary, so a Boolean here would manufacture certainty." Any extension inherits that discipline: no cause may be named from SIGABRT alone.

The Architectural Reality

.github/workflows/test.yml runs integration / parity / unit / components. There is no e2e suite in CI — verified: no workflow references test-e2e or playwright.config.e2e. The layer is local-only, so a seat that cannot launch has no fallback path to that evidence, and nothing scheduled ever contradicts a green PR.

The cost was never a capability gap — both GPT seats could run e2e the whole time. It was that the working invocation was not discoverable from the failure, and the fields that would have discriminated the hypotheses were the ones not printed. Two PRs shipped unexecuted, one carrying a real regression, while the repair was a permission flag away.

The Fix

Extend the existing reporter. Do not add a preflight. An earlier draft of this ticket proposed a launch preflight; @neo-gpt correctly rejected it against #16161's ledger — it would introduce a second browser owner, and it still could not infer a sandbox boundary from branded Chrome's SIGABRT alone. Playwright stays the sole browser owner.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
browserLifecycle.launchExits[] custom-reporter.js#classifyBrowserLaunchExit + #16161 ledger Extend the existing receipt with resolved headed/headless state and a sanitized browser-kind enum; retain every current field Unknown or unobserved values stay null / unknown; never infer sandbox or root cause from a signal Reporter JSDoc Pure units over branded + bundled error grammars; the live rejected-launch receipt
Terminal [browser-lifecycle] summary Existing reporter console contract Include project, profile, channel / browser kind, headed state, failure-before-object, and one bounded remedy Generic "launch exited before Browser object" when the cause is not explicit JSDoc + E2E evidence note Captured stdout assertion + a negative post-launch corpus
Cause / remedy classification Exact bounded error tokens only Name Mach-service permission denial only when Permission denied (1100) is present; otherwise preserve unclassified-process-exit No "Codex sandbox" claim from SIGABRT alone Inline avoided-trap guard Positive explicit-denial arm + opaque-SIGABRT arm
Privacy #16161 privacy row Persist enums and booleans only; no raw executable path or launch args Omit rather than broaden Ticket + JSDoc Serialized receipt stays negative for path / args / URL / title / profile data
Browser ownership Playwright fixture ownership + #16161 Extend the actual rejected-launch reporter; do not launch a diagnostic browser A true preflight would need its own contract for the extra owner, cost and failure semantics Ticket Existing no-second-lifecycle unit philosophy
E2E evidence handoff learn/agentos/process/SeatEvidenceCapabilities.md (## Fallback rule), consulted by pr-review-guide.md:256 + whitebox-e2e-protocol.md:19 Document the author declaration + browser-capable-reviewer rerun when an author cannot execute, as a two-half contract; a stale seat block counts as unknown, not negative No claim that CI covered local-only e2e Same file's ## Fallback rule section Owned by child #17605, delivered in PR #17606 — the seat-capability rows and fallback contract

Acceptance Criteria

  • The SIGABRT cause is named from evidence — @neo-gpt's boundary matrix and @neo-gpt-emmy's single-axis minimal pair.
  • A Codex-harness seat runs one real e2e spec to a green assertion — satisfied twice.
  • The discriminator is settled: permission, not launch mode, is causal.
  • The terminal summary names the channel / browser kind it already holds, plus the resolved headed state.
  • The receipt carries headed state and a sanitized browser-kind enum, with unobserved values staying null / unknown.
  • A cause is named only from an exact bounded token (Permission denied (1100)); an opaque SIGABRT stays unclassified-process-exit. Both arms are tested — a green on the explicit-denial arm must not imply the opaque one.
  • No second browser lifecycle is created, and the serialized receipt stays negative for path, args, URL, title and profile data.
  • The e2e evidence handoff is written down: what an author owes when their seat cannot execute, and what the reviewer runs instead.

Out of Scope

  • Changing Neo's browser selection. channel: 'chrome' is not the cause; both binaries fail identically at the boundary, and bundled-Chromium fallback would trade an opaque abort for an explicit denial — a better message, not a working run.
  • A launch preflight / any second browser owner — rejected above on #16161's authority.
  • The Codex sandbox policy — harness-owned; the demonstrated repair is an approved execution path.
  • Fixing any red e2e spec#17596.
  • Adding e2e to CI — separate decision, own cost.

Avoided Traps

  • Asserting an absence without searching the layer that owns it. This ticket claimed the runner "reports none of it" while custom-reporter.js was already printing a structured incident line and persisting a receipt. The claim was never checked against the reporter — @neo-gpt opened it.
  • A probe that measured its own approval. @neo-gpt's node --input-type=module probe appeared to refute the sandbox reading because that prefix was already approved on his seat; re-running through the default sandbox failed for both browsers. On a seat with per-command approvals, any passing invocation may pass because of its approval rather than its flags, and nothing in the output says which. @neo-gpt-emmy hit the same trap from the opposite direction with --headed.
  • Manufacturing certainty from a signal. The reporter's transportState: 'not-observable' is the standard; a cause named from SIGABRT alone would be the same error one layer up.
  • Stacking corrections. This body has been wrong four times. Superseded framings are deleted, because the next reader inherits a body, not a thread.

Related

#16161 / PR #16162 (the reporter contract this extends) · PR #17588 · PR #17593 · #17596 · #17476 · #17422 · #14618

Retrieval Hint: browser launch exit receipt custom-reporter launchExits channel headed enum Codex command sandbox Mach port Permission denied 1100 unclassified-process-exit

Origin Session ID: 1b0d28eb-3461-40b6-bb35-88d6bf09ec94

tobiu referenced in commit 7cb287e - "fix(testing): enrich browser launch-exit receipts (#17595) (#17609) on Aug 23, 2026, 1:30 PM
tobiu closed this issue on Aug 23, 2026, 1:30 PM