[PR #4163] feat(analysis-view): page retained browser scenario steps and text #6103

Open
opened 2026-10-31 01:11:53 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4163
Author: @kangyishuai
Created: 10/15/2026
Status: 🔄 Open

Base: main ← Head: feat/4069-ui-state-transition-view


📝 Commits (10+)

  • 8ead9e6 feat(analysis-view): page retained browser scenario steps and text
  • 2155fa2 fix(analysis-view): order empty scenario receipt windows
  • a9b3c8c Merge origin/main into feat/4069-ui-state-transition-view
  • 32dc60f chore(adb): keep main's AdbProvider formatting
  • 92fe95e Merge origin/main into feat/4069-ui-state-transition-view
  • d24c2fe fix(browser-scenario): reject captures that contradict their selection or start step
  • b4463fd fix(browser-scenario): keep step completeness equal to its artifacts
  • 0cde897 fix(browser-scenario): keep capture accounting equal to its steps and events
  • 0d50bfe fix(browser-scenario): back transaction omissions with per-kind drops
  • 1982ecf fix(browser-scenario): accept metadata-budget transaction omissions

📊 Changes

46 files changed (+6288 additions, -476 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/runtime-observation.md (+5 -0)
📝 docs/browser-scenario-contract.md (+92 -9)
📝 docs/mcp-contracts.md (+6 -0)
➕ scripts/lib/browser-scenario-view-e2e.mjs (+317 -0)
📝 scripts/verify-real-browser.mjs (+4 -0)
📝 src/application/analysisView/AnalysisViewService.test.ts (+57 -1)
📝 src/application/analysisView/AnalysisViewService.ts (+8 -1)
📝 src/application/environment/environmentScenario.test.ts (+12 -11)
📝 src/browser/BrowserScenarioPartialObservation.ts (+2 -10)
📝 src/browser/PlaywrightBrowserScenarioProvider.ts (+15 -33)
➕ src/browser/PlaywrightScenarioEvents.stepAttribution.test.ts (+153 -0)
📝 src/browser/PlaywrightScenarioEvents.test.ts (+100 -9)
📝 src/browser/PlaywrightScenarioEvents.ts (+8 -9)
📝 src/browser/assets/ScenarioScriptCapture.ts (+3 -13)
📝 src/browser/assets/ScriptCaptureAdapters.test.ts (+11 -8)
📝 src/browser/network/PlaywrightScenarioNetwork.ts (+6 -9)
📝 src/cli/analysisViewCommands.ts (+1 -1)
📝 src/contracts/analysisViewToolContracts.ts (+2 -2)
📝 src/contracts/browserToolContracts.ts (+6 -5)
📝 src/domain/analysisView/analysisView.ts (+27 -3)

...and 26 more files

📄 Description

Summary

Refs #4069 (RFC: evidence-backed UI state/transition observation primitives). Related: #39 (browser tracker) and draft #4107. #4107 derives one transition from two passive inspect_web_page records plus a caller-declared external action. This PR projects the steps of one REA-controlled capture_browser_scenario run. The two sources differ, and neither introduces a shared "UI state" owner. A shared vocabulary is left for the maintainer discussion in #4069.

capture_browser_scenario already records a post-action capture for every declared step. Agents still could not read a completed capture step by step:

  • inspect_analysis_view rejected it as an unsupported parent.
  • compare_web_captures compares two runs, not consecutive steps within one run.
  • A 20-action capture with screenshot, DOM, accessibility and URL after each step is about 2.2 MB, measured below.

Problem and expected behavior

An external agent reconstructing an application's behavior needs, for each declared step:

  • what was done, as the recorded declaration;
  • whether the step completed;
  • which recorded page artifacts differ from the previous step's capture;
  • which events and requests were received in that step's receipt window.

It needs this without re-reading the whole capture or re-running the browser.

  • Before: inspect_analysis_view returned Parent Evidence operation "capture_browser_scenario" does not support analysis views (source-derived from unsupportedParent in src/domain/analysisView/analysisView.ts on main). An oversized capture could only be exported.
  • After: three views of retained (MCP) or inline (CLI) capture Evidence:
    • {kind: "summary"}
    • {kind: "browser-scenario", collection: "steps", offset, limit}
    • {kind: "browser-scenario", collection: "text", step_index, artifact: "accessibility" | "dom", offset, limit}

Change and scope

  • A new capture_browser_scenario parent for inspect_analysis_view and inspect-analysis-view.

    • Summary: browser identity, step outcomes, per-artifact capture coverage and step changes, declared and enabled event families, events received outside every step window, and network transaction counts. Transactions dropped by the transaction limit are counted separately, and without_retained_request counts retained transactions whose request event was dropped or never observed.
    • Steps: each row has the exact recorded action declaration and its family, outcome, recorded URLs, per-artifact coverage, comparison digest and change from the previous step's capture, the events received in the step's sequence window, and the network transactions whose retained request event was made at that step, with their own coverage. A transaction without a retained request counts toward no step; an unknown names such transactions. When network was not observed, the count is null with not_requested coverage, never 0. Every page also carries enabled_event_families and received_outside_step_windows.
    • Text: UTF-16 pages of one step's retained accessibility or DOM text, with the producer's artifact SHA-256.
  • Single owners:

    • Step changes reuse the compare_web_captures comparison owner. browserScenarioDiffEvidence now exports per-step artifact observation, and browserScenarioDiff exports pair classification. Run comparison output is unchanged; see the evidence below.
    • The rule that selecting network content enables network events now lives in enabledScenarioEventFamilies. The Playwright producer and the view both call it, with no behavior change.
  • The view fails closed on inconsistent recorded data:

    • recorded actions must match steps by step_id and action;
    • step receipt windows must rise with step_index and stay disjoint;
    • every event must carry the step current at receipt: the last step that ran whose window had opened. This covers events between windows and after the final capture, not only those inside a window;
    • the capture must restate the scenario recorded in the parent parameters. This covers the browser mode; the scenario fields (start origin, action count, secret references, network content, event families, limits); and each step's artifacts, which follow after_each_step on every step and at_end on the final one. The producer and the views share one domain owner for the scenario projection and for the per-step artifact selection.
  • Capture schema invariants, added from review. browserScenarioCaptureSchema now rejects:

    • retained events whose family was not selected;
    • network transaction omissions when network was not selected;
    • a step 0 other than the completed scenario_start/goto_start step whose receipt window starts at event sequence 1, now a shared constant that the producer uses;
    • step completeness that does not list exactly the missing and truncated artifacts, plus action when the action did not complete. This is how the producer derives it. The comparison owner's completeness downgrade could no longer be reached, so it now depends only on the action outcome, and the text view reads recorded artifact states directly;
    • capture completeness that does not aggregate those step ledgers (each section once, adding only missing events), or that omits events despite connect mode, dropped events, a retained unfinished request, unavailable network content, or a popup while a gap family is selected;
    • a network_transaction_range that does not match the network events dropped for max-network-transactions (a reason only network kinds may carry), or per-transaction omissions that do not correlate with by_kind in both directions: each omitted kind and budget reason is dropped there, no kind is omitted by more transactions than its drops, and every budget drop of a network kind names its transaction;
    • launch or connect ownership and cleanup values that do not pair;
    • a cancelled step with captured artifacts, or a step after the first unfinished action that is not a never-ran cancellation;
    • a retained event sequence or step window end above the sequenced events: retained plus dropped, less the drops known to belong to refused transactions, which spend no sequence numbers;
    • a by_kind row counting fewer drops than its listed reasons and naming transaction records imply. Because by_kind keeps one count per kind across its reasons, refused-transaction drops are bounded rather than counted, and the omitted range may not exceed that bound. Exact per-reason counts would need a producer format change, which is left for a separate PR;
    • network content whose headers or body disagree with header_values and the phase's body selection, or content kept or dropped with no content facet selected;
    • a second request event for one transaction;
    • more retained events than max_events, or more admitted transactions than max_network_transactions.

    Step-completeness derivation, the popup gap families and the unavailable-content rule now each have one domain owner that the producer uses. Event rules live in browserScenarioCaptureEvents.ts and the capture fields in browserScenarioCaptureFields.ts. The comparison owner's per-step event sections could no longer be reached, so they now reduce to one capture-wide event coverage rule.

    Omission accounting keeps the producer's admission rule: a popup page refused by an event budget remains an omitted popup-opened record whenever any family is selected, because popup discovery runs for every selected family. The producer's final parse, compare_web_captures and these views share this schema, so all three reject such inline captures. Producer output already satisfied these rules; see Validation.

  • Inline capture validation boundary. Views and comparisons reject inline captures that:

    • are structurally invalid;
    • contradict the relations above, which the views compute from;
    • do not restate the scenario recorded in their parameters.

    They do not try to prove that any inline Evidence could have come from the recorder. These remaining recorder guarantees are left for a separate hardening PR:

    • strictly ascending event order;
    • consistent redirect fields, and the request coming first, within each transaction;
    • moving the receipt-window and step-provenance checks from the step projection into the capture schema, so that compare_web_captures gets them too;
    • per-reason drop counts in by_kind, which would replace the current bounds on refused-transaction drops but is a producer format change;
    • removing completeness sections that scenario captures can no longer use.

    Further findings that only hand-built captures can trigger will be answered with this boundary rather than with new rules here. Findings that affect producer output, view results for producer Evidence, or regressions will still be fixed.

  • Intentionally not included:

    • UI state keys and revisit detection, and cross-run state graphs. These need a shared owner with #4107 and maintainer input on #4069.
    • Control and executable-action inventory, which needs structured accessibility at the producer.
    • detail: "summary" delivery for capture_browser_scenario, planned as a separate PR because it changes a producer tool's input.
    • Native and Electron parents.
    • Coverage export.
    • Cross-layer joins.
  • Alternatives rejected:

    • A new tool: same intent as the existing view tool, plus catalog growth.
    • A within-run mode of compare_web_captures: inline-only inputs, a reduced compact schema, and observed/external-service labelling of a derivation.
    • A producer change: it would put derived facts into observed Evidence.

Contract and boundary impact

  • Semantic owner and earliest changed stage:
    • domain step projection: src/domain/browserScenarioSteps.ts;
    • shared comparison owner: browserScenarioDiffEvidence.ts and browserScenarioDiff.ts;
    • view: src/domain/analysisView/browserScenarioView*.ts.
  • Contribution scope fit and linked maintainer decision: not applicable. This is a derived view over existing Evidence; it adds no tool, provider, execution, injection or mutation. Feedback on contract shape and ownership is requested on #4069.
  • CLI and MCP/tool-catalog contract: additive view kind and parent on inspect_analysis_view and inspect-analysis-view. The tool count stays at 207. Measured byte and token deltas are under Validation.
  • Provider, bridge, target-format, or platform compatibility: the producer emits the same data. PlaywrightScenarioEvents now calls the shared family rule. Views accept the current capture schema only. compare_web_captures now also rejects inline captures that contradict these capture rules; the producer never wrote such captures. The compare_web_captures contract example now records a consistent launch-mode capture with the producer's goto_start start step.
  • Evidence, artifact, provenance, or reconstruction contract: view Evidence is rea.analysis-view with confidence derived. It inherits the parent's controlled-replay authority under the existing view rule and links the parent.
    • This is the first view parent with controlled-replay authority, so exported bundle scenarios manifests will list view records with operation inspect_analysis_view. Maintainer input is welcome.
    • For subject-less parents, the analysis-view service now states the missing artifact identity once in view Evidence instead of repeating the parent's copy.
  • Target selection, authority, upstream behavior, and cleanup details: none; no target interaction.
  • Generated metadata, package, or installation impact: no tracked generated files changed.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • src/domain/analysisView/browserScenarioView.test.ts, 13 cases:
      • consecutive comparison and revisit-equal digests;
      • failed and cancelled steps;
      • receipt windows, gaps and the after-final bucket;
      • capture-wide event coverage, including dropped events and connect mode;
      • truncated artifacts;
      • unobserved network reported as null, not 0;
      • pages past the end;
      • fail-closed declarations and windows;
      • view-owned limitations kept under the message budget.
    • src/domain/browserScenarioDiff.test.ts: a new characterization case pins every incomparable branch with full objects (states, captured-side digest, exact reason).
    • src/application/analysisView/AnalysisViewService.test.ts: authority, links, parameters, and a single subject limitation.
    • tests/boundary/mcp/analysisViewMcp.test.ts: retained parent, Ajv validation against the advertised input and output schemas, and the public error projection.
    • tests/boundary/cli/analysisViewCli.test.ts: portable parent through the compiled CLI.
    • scripts/lib/browser-scenario-view-e2e.mjs: wired into verify:browser.
    • Capture invariants:
      • src/domain/browserScenarioCapture.test.ts: every capture rule, including omission accounting, the popup-discovery case and each event-gap cause. Mutation checks removed each rule branch one at a time, and each removal failed a test.
      • src/browser/PlaywrightScenarioEvents.test.ts: the producer's budget-refused popup page and network metadata over its budget both parse under the schema.
      • src/domain/analysisView/browserScenarioView.test.ts: exact-issue rejection of unselected-family events, of a foreign or failed step 0, of a ledger that contradicts retained text, and of capture accounting that contradicts its steps or omissions.
      • Hand-built fixtures in browserScenarioDiff.test.ts, browserCli.test.ts, tests/fixtures/webScriptCapture.ts, ScriptCaptureAdapters.test.ts and webScriptExport.test.ts now record producer-shaped captures. Each test keeps its property; for example, differing comparison anchors now come from unrelated events instead of unaccounted sequence gaps.
  • Base reproduction or other evidence:
    • The new comparison characterization case was also run with the identical fixture against origin/main's comparison implementation in a temporary worktree. The serialized step results were identical, so the shared comparison refactor did not change these outputs.
    • The view also fails closed on malformed inline Evidence; see the domain tests.
  • User-visible CLI/MCP output: see the real step row below.
  • Remaining proof gaps:
    • No model-host discoverability evaluation was run.
    • Model token usage for agent tasks is unmeasured; tokenx estimates only.
    • Linux and Windows real-browser lanes were not run locally.
    • CPU time and memory cover the MCP server process only.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.
Real step row (Chrome 155, self-owned fixture, trimmed)
{
 "context": {
  "source_completeness": "complete",
  "enabled_event_families": [
   "console",
   "network"
  ],
  "received_outside_step_windows": {
   "retained": 0,
   "kinds": []
  }
 },
 "row": {
  "step_index": 1,
  "step_id": "open-settings",
  "action_class": "input",
  "action": {
   "step_id": "open-settings",
   "action": "click",
   "locator": {
    "kind": "role",
    "role": "button",
    "name": "Open settings"
   },
   "button": "left",
   "click_count": 1
  },
  "status": "completed",
  "error": null,
  "elapsed_ms": 31,
  "before_url": {
   "url": "http://127.0.0.1:<port>/app",
   "origin": "http://127.0.0.1:<port>",
   "query_parameter_names": [],
   "redacted": false
  },
  "after_url": {
   "url": "http://127.0.0.1:<port>/app",
   "origin": "http://127.0.0.1:<port>",
   "query_parameter_names": [],
   "redacted": false
  },
  "artifacts": [
   {
    "artifact": "accessibility",
    "coverage": "captured",
    "reason": null,
    "comparison_sha256": "bfbf5cd355389a3ac0153e2ad7e8c0efd1390a65bb2f80e181e8ed78fd2c94d1",
    "change": "changed"
   },
   {
    "artifact": "url",
    "coverage": "captured",
    "reason": null,
    "comparison_sha256": "94611b2e44231cb6051645e0f41a503056bfeb4dba3b4c90005ff8867750d843",
    "change": "unchanged"
   }
  ],
  "events_received": {
   "retained": 1,
   "kinds": [
    {
     "kind": "request",
     "count": 1
    }
   ],
   "coverage": "captured",
   "sequence_start": 5,
   "sequence_end": 5
  },
  "network_transactions": {
   "coverage": "captured",
   "retained": 1
  }
 }
}

Validation performed

All results are on macOS arm64 with Node 24.14.0.

The current head 848847efe merges main 524159759 (82 new commits). The only conflict was the inspect_analysis_view description, from main's Rust dependency view (#5815). The merge keeps main's text and adds only this PR's browser scenario additions. Main's scenario-producer change only stops passing the already-absolute executable_path through resolve(). Main's new optional resource_root view field is not set by these views. On this head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on the 42 source test files outside src/application/environment/ (692 tests).
    • Run together with main's 11 environment test files, 4 tests in main's environmentExport.test.ts failed (heap-headroom and snapshot-change checks). In another run, 1 test in main's environmentBundleWrite.test.ts failed.
    • On a clean main 524159759 worktree, the same file set failed the same way in two runs.
    • The 11 environment files passed 3 runs out of 3 on their own, both on this head and on clean main.
    • This PR does not touch those files.
  • npm run test:focused passed on 187 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,692 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser failed before reaching the scenario checks, at main's passive-observation check ("Page-scoped CDP disconnect was not reported as target_terminated", after main's 5891bfb30). It fails the same way on a clean main 524159759 worktree.
  • This PR's real-Chrome scenario-view e2e (scenarioViews) passed when run directly: 7 steps, accessibility text reassembled, steps view 14,713 bytes.

The previous merge head 29d6cb932 merged main 4bfdadb7d. The only conflict was the inspect_analysis_view description, from main's Frida session-status summaries (#5661). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 52 source test files (757 tests).
  • npm run test:focused passed on 177 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,658 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head e76981a41 merged main 17f581ff7. The only conflict was the inspect_analysis_view description, from main's retained GDB and objdump summaries (#5614). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 52 source test files (756 tests).
  • npm run test:focused passed on 173 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,632 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head 91cb1b197 merged main 7d6f90485. The only conflict was the inspect_analysis_view description, from main's model artifact view (#5603). The merge keeps main's text, including inspect_model_artifact, and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 52 source test files (756 tests).
  • npm run test:focused passed on 170 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,614 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head 4e25db1b0 merged main c2858d2aa. The only conflicts were the inspect_analysis_view and inspect-analysis-view descriptions, from main's Go build metadata view (#5524). The merge keeps main's text and adds only this PR's browser scenario additions. Main's scenario timer bound (#3515) and session-test cleanup merged cleanly and do not change the capture shape. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 52 source test files (756 tests).
  • npm run test:focused passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,632 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous head 28af2811d fixed a step-attribution error found in review. It affects real producer output. Step rows used to count a transaction under the step that scenarioNetworkTransactions assigned it. When the request event was not retained, that function falls back to the step of the first retained event. The recorder can drop a request over max_event_allocation_bytes and still keep its response from a later step, and connect mode can attach while a request is in flight.

  • Fix: scenarioNetworkTransactions now reports request_retained, and step rows count only transactions with a retained request. The summary adds without_retained_request, and an unknown names those transactions. Comparisons keep their attribution, and fingerprints do not read the new field.
  • Regression test: PlaywrightScenarioEvents.stepAttribution.test.ts drives the real recorder until it drops a step-1 request and keeps its step-3 response, then projects the capture through the summary and step views. With every transaction attributed to a step again, the test fails.

On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 51 source test files (738 tests).
  • npm run test:focused passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,617 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews. The steps view is 14,747 bytes, unchanged.

The previous merge head 43c13190a merged main cf5194f68 (41 new commits). The only textual conflict was the inspect_analysis_view description, from main's IL2CPP native-method linking change. The merge keeps main's text and adds only this PR's browser scenario additions. Two main changes touch the scenario producer and merged cleanly:

  • 599fdac02 waits for late navigation to stop before releasing attached browser sessions.
  • 0e83d92c5 changes executable_path validation.

Neither changes the capture's steps, events, completeness or cleanup values, which this PR's schema and views read. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 50 source test files (737 tests), including main's updated scenario session, browser and provider tests.
  • npm run test:focused passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,617 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true. That includes scenarioViews, the failure scenario, and cleanup recorded as terminated-owned-process in launch mode and disconnected-external in connect mode.

The previous merge head d82100dc4 merged main 21e0c580e. The only conflict was the inspect_analysis_view description, from main's IL2CPP change (#5326). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 49 source test files (698 tests).
  • npm run test:focused passed on 164 boundary, acceptance, contract, catalog and environment files: 1,528 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head aac8f3136 merged main 1417bf943. Main's native function matching view (#5390) added its summary result schema at the same place as this PR's. The merge keeps both in the summary union keyed on parent_operation. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 49 source test files (692 tests).
  • npm run test:focused passed on 164 boundary, acceptance, contract, catalog and environment files: 1,493 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head c1fcb5c5f merged main 2538214e2. Main's native inventory view (#5378) changed how summaries are typed: each parent operation now has its own summary result schema, and the summaries form a union keyed on parent_operation. This PR's summary now follows that pattern with browserScenarioSummaryViewResultSchema, replacing the earlier single summary schema whose body could be any of several shapes. The tool description, the CLI description and the generic unsupported-parent message are main's text plus only this PR's browser scenario additions. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 49 source test files (692 tests).
  • npm run test:focused passed on 163 boundary, acceptance, contract, catalog and environment files: 1,492 passed, 48 skipped. This includes the analysis-view MCP output-schema validation.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head bddd1eaae merged main 0134732fc. Main's protobuf capture view (#5031) conflicted in two places:

  • UnsignedAnalysisView: main now derives this type from AnalysisViewResult instead of listing each kind, so the browser-scenario result needs no separate entry.
  • The tool description: main's text, including the protobuf sentence, plus only this PR's additions.

Main's other new commits (#5350, #5355, #5356) merged without conflicts and do not change the browser scenario producer, capture schema or scenario fixtures. Main also added a dependency, so I reran npm ci. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 48 source test files (687 tests).
  • npm run test:focused passed on 161 boundary, acceptance, contract, catalog and environment files: 1,464 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head 05a17923a merged main 3fca2e0ca. Main's #5319 adds an environment-state view that also reads capture_browser_scenario parents. The merge keeps both sides:

  • Routing: grouping views (api-surface, environment-manifest, environment-state) still go to main's projectors first, and this PR's projector serves summary and browser-scenario. This PR's refusal for any other view kind now names all four supported views.
  • Fallback error: main's "only environment-manifest and environment-state" branch can no longer be reached, so the generic unsupported-parent message stays.
  • Docs: docs/mcp-contracts.md places main's environment-state paragraph right after the environment-manifest paragraph it refers to, followed by this PR's paragraph.
  • Tool description: main's text plus only this PR's additions.
  • Main's test: environmentScenario.test.ts keeps this PR's summary-succeeds check, with the four-view message.
  • Shared fixture: main's new test builds the scenario fixture with header_values selected. The fixture now records content-event headers as captured when header values are selected (an empty list, since the fixture records no header names), as this PR's capture schema requires.

On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 46 source test files (679 tests), including main's environmentStateView, environment and scenarioCaptureRecords tests.
  • npm run test:focused passed on 160 boundary, acceptance, contract, catalog and environment files: 1,460 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head ae827b13f merged main f93a0335a. Main's #5293 adds an evidence-file source to inspect_analysis_view and new service tests. Two conflicts came from it:

  • the tool description, which now keeps main's evidence-file sentence alongside this PR's capture_browser_scenario text;
  • AnalysisViewService.test.ts, which now keeps both this PR's browser-scenario authority test and main's inspectAnalysisViewWithFiles tests.

For the summary, steps and text views of the fixture capture, an evidence-file source gives the same normalized_result, controlled-replay authority and parent link as the same Evidence passed inline. I checked this with a local script, not a committed test. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 43 source test files (632 tests), including main's new evidence-file and scenarioCaptureRecords tests.
  • npm run test:focused passed on 158 boundary, acceptance, contract, catalog and environment files: 1,448 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.85 and verified: true, including scenarioViews.

The previous merge head 150d147ec merged main 5fa4defcb. The only conflict was the inspect_analysis_view description, where main's Godot PCK change (#3634) rewrote the inventory text. The merge keeps main's text and adds only this PR's capture_browser_scenario parent and view sentences. #3634 does not touch the browser scenario producer, capture schema, views or fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 42 source test files (616 tests).
  • npm run test:focused passed on 158 boundary, acceptance, contract, catalog and environment files: 1,447 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews.

The previous merge head d499af870 merged main 833136507. Main's new il2cpp-metadata view (#3807) conflicted with this PR's browser-scenario views in the analysis-view request and result unions, parent operations, summaries, unsigned view types and the tool description. The merge keeps both view kinds. The generic unsupported-parent message names both IL2CPP metadata and browser scenario captures. The tool description is main's text plus only this PR's additions. #3807 does not touch the browser scenario producer, capture schema or fixtures. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 41 source test files (605 tests), including main's environment, scenarioCaptureRecords and IL2CPP view tests.
  • npm run test:focused passed on 158 boundary, acceptance, contract, catalog and environment files: 1,447 passed, 48 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews.

The previous merge head 2cae99f53 merged main bf1eeee94. Main's #5182 builds environment manifests from capture_browser_scenario parents, so one parent kind now has two view paths:

  • Routing: environment-manifest views still go to main's projector first. This PR's projector serves summary and browser-scenario views for the same parents, and its refusal for any other view kind now names all three supported views.
  • Fallback error: main's "supports only the environment-manifest view" branch can no longer be reached, so the generic unsupported-parent message stays.
  • Tool description: keeps both sides' text.
  • Main's fixture: tests/fixtures/browserScenarioCapture.ts built captures the recorder cannot emit, which this PR's capture schema rejects. I made it match the recorder without changing its transactions or record content:
    • step 0 is the implicit start navigation;
    • step 1 is the declared wait_for_timeout and receives every event;
    • content events exist only for selected body facets;
    • the recorded scenario declares the capture's event, network and artifact selection.
  • Main's test: environmentScenario.test.ts used summary as its unsupported example. It now expects summary to succeed on the same Evidence, and checks that an unsupported view names the supported ones.

On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 40 source test files (595 tests), including main's environment and scenarioCaptureRecords tests.
  • npm run test:focused ran 327 boundary, acceptance, contract and environment files: 3,529 passed, 122 skipped, 2 failed. The failures are javascriptRuntimeObservation ("includes targets outside the old caller root filter") and filesystemObservationRaceCli ("keeps a stable symlink observation complete"). Both fail the same way on a clean main bf1eeee94 worktree on this macOS machine, and this PR does not touch those areas.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews.

The previous head 757bbdfc3 fixed a paging cost found in review. Every page used to rerun the declaration checks and the page-shared limitations and unknowns over the whole capture; these results now join the sealed-parent cache. A new test pages one sealed result and checks that another Evidence ID over the same frozen result is checked on its own. With the Evidence ID binding removed, that test fails. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 29 capture, view, producer, network, scenario-service, observation and Evidence test files (477 tests).
  • npm run test:focused passed on 64 boundary, acceptance and contract files: 526 passed, 5 skipped.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews; the steps view is 14,713 bytes, unchanged.
  • In-process paging measurement: a sealed parent with N click actions, all steps paged 20 at a time through projectAnalysisView, two runs each on macOS arm64. Output is unchanged, so view bytes and tokens are unchanged. MCP end-to-end time was not re-measured.
Actions Warm ms per page, before → after All warm pages, before → after Process user CPU, before → after Process RSS MiB, before → after
200 0.97–1.07 → 0.33–0.36 10 ms → 3–4 ms 0.29–0.39 s → 0.25–0.26 s 237–241 → 204–216
2,000 3.5–3.7 → 0.26 354–368 ms → 26 ms 0.96–0.99 s → 0.51–0.53 s 289–295 → 282–283
10,000 17.0–19.1 → 0.20 8.5–9.5 s → 0.1 s 10.8–11.8 s → 1.2 s 671–672 → 529–547

The cold first page, which parses and projects the capture, is unchanged (8–9 ms, 42–52 ms and 180–190 ms). A summary request still tallies every step once per request.

The previous merge head aa689d960 merged main b4938aed5. Main's new desktop-application view (#5207) conflicted with this PR's browser-scenario views in the inspect_analysis_view tool description and the inspect-analysis-view CLI command description. The merge keeps both view kinds, so each description is main's text plus this PR's additions. None of the main commits since the previous merge touched the browser scenario producer or capture schema. On that head:

  • npm run check:fast passed (typecheck and lint).
  • npm run test:local passed on 29 capture, view, producer, network, scenario-service, observation and Evidence test files (476 tests).
  • npm run test:focused passed on 64 boundary, acceptance and contract files: 526 passed, 5 skipped.
  • npm run docs:check passed.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews.

The previous merge head 2fa0ba9e8 merged main fdaffe21c. There, main's environment-manifest view (#4954) conflicted in the tool description, docs/mcp-contracts.md and tests/boundary/cli/analysisViewCli.test.ts, and the merge kept both view kinds and all three CLI tests. The same checks passed on that head: 476 unit tests, 464 boundary tests (5 skipped), docs:check and verify:browser.

The previous merge head 6ae23996e merged main bec88b9d7. Its only conflict was in scripts/verify-real-browser.mjs, where both sides added a verifier import, and the merge kept both. verify:browser passed there too, including main's urlUserInfo check.

The previous head bf85798c4 added only a change to the views, which now merge the capture's own limitations with the envelope's. I validated it with npm run check:fast, the capture, view and producer unit tests (277), the analysis-view MCP/CLI boundary and output-schema tests (35), and the real-Chrome scenario-view e2e.

On 688f33b2a, which merges main d9d085a15 and includes the earlier review fixes:

  • npm run check passed, with lint at 0 errors.
  • npm run docs:check, npm run verify:test-discovery, npm run jscpd (3.28%) and npm run scan:todos passed.
  • npm run test:local passed on 16 capture, view and producer test files (280 tests).
  • npm run test:focused passed on 46 boundary, acceptance and contract files: 482 passed, and 2 tests in serverLifecycleMcp.test.ts were skipped as unrelated. On the previous head, 1dd14fe0b, one run hit 30-second timeouts in 5 unrelated files while the machine's load average was about 10. Those 5 files and then the full set passed when rerun, and the full set passed in one run on this head.
  • npm run verify:browser passed with Google Chrome 155.0.8059.80 and verified: true, including scenarioViews and popup coverage.
  • Producer conformance stress run: the real PlaywrightScenarioEvents recorder and network collector, driven by fake Playwright objects, across all 256 family subsets, with and without network content, 8 budget settings (including network metadata budgets), and launch and connect ownership. Capture completeness was computed with the provider's own rule. All 8192 captures were accepted by the schema. The max-listener-owners reason was not exercised.
  • A real Chrome capture with only console selected, max_events: 1 and a window.open popup completes, with two omitted popup-opened records. Before the popup adjustment, the stricter rule rejected this producer output.

Earlier results on the first PR commit, rebased onto main 9d6651e5b:

  • npm run check passed, with lint at 0 errors.
  • npm run docs:check passed.
  • npm run verify:test-discovery passed.
  • npm run jscpd passed (3.18%).
  • npm run scan:todos passed.
  • npm run test:local passed on 19 domain, service and producer test files (291 tests), including src/domain/analysisView/*, src/application/analysisView/*, src/domain/browserScenario*.test.ts, src/domain/evidence.test.ts and src/browser/PlaywrightScenario*.test.ts.
  • npm run test:focused passed on 17 files (194 tests): the analysis-view MCP and CLI boundaries plus toolSchemaValidity, contractPresentation, toolInputAdmission, compactInputSchemaMcp, outputSchemaProfile, serverIdentity, cliToolCoverage, jsonInputStatus, cliStartupImports, productCatalog, documentationFacts, targetContentProvenance, toolEffects, toolInputReferences and tupleSchemaParity.
  • npm run verify:browser passed with Google Chrome 155.0.8059.40 and verified: true. That includes the new scenarioViews check: a self-owned fixture with known transitions, run through stdio MCP and the compiled CLI on the same Evidence with identical view output. It also confirms paged accessibility and DOM text reassemble to the producer SHA-256, and that per-window plus outside-window event counts equal the retained total.

Representative task measurement: one stdio MCP server process, 3 runs, 20 click actions with screenshot, DOM, accessibility and URL captured after each step.

Call Wall ms Server CPU s Server peak RSS MiB Response bytes tokenx estimate
capture_browser_scenario (21 steps) 2505–2678 0.37–0.52 457–461 2,189,439 461,055
summary, cold (first view of the parent) 17–19 0.02–0.03 458–461 6,908 1,869
summary, warm (cached projection) 2 ≤0.01 450–453 6,908 1,869
steps page, 21 rows, warm 4–6 ≤0.01 450–453 34,746 10,151
text page, 4,096 code units, warm 2 ≤0.01 450–453 7,536 1,975

The capture call is unchanged by this PR. Capture time and RSS cover the MCP server process only; Chromium processes are not included. Token counts are tokenx estimates of the returned text, not billed model usage.

Catalog effect, comparing main 9d6651e5b with the first PR commit (not re-measured after the merge and the review fix):

  • tools: 207 → 207 (+0)
  • tools/list full bytes (name+description+input): 824,508 → 826,128 (+1,620)
  • tools/list compact bytes: 189,737 → 190,263 (+526)
  • inspect_analysis_view description chars: 6,249 → 6,761 (+512)
  • inspect_analysis_view description tokenx: 1,586 → 1,726 (+140)
  • inspect_analysis_view full input bytes: 23,095 → 24,189 (+1,094)
  • inspect_analysis_view full input tokenx: 6,593 → 6,895 (+302)
  • inspect_analysis_view compact input bytes: 333 → 333 (+0)
  • inspect_analysis_view output schema bytes: 128,229 → 142,453 (+14,224)

Compatibility, safety, and release

  • Breaking changes or migration steps: no producer output changes. Hand-built inline captures that contradict these capture rules are now rejected as malformed input. This includes inline captures passed to main's environment-manifest view (#5182), which parses the same schema. Main's hand-built fixture in tests/fixtures/browserScenarioCapture.ts was one of them, and this PR updates it to match what the recorder emits (details under Validation).
  • Real Hopper/Ghidra, browser, or OS coverage: real Chrome on macOS arm64 only. The CI real-browser lane is selected by the touched src/domain/browser* and src/browser/** files.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review:
    • Views return producer-redacted text and recorded declarations: caller literals and secret references, never resolved secret values.
    • No new data collection, process, network, or filesystem effect.
    • Inline captures are fully schema-validated on each call. Sealed retained parents cache, by object identity through a WeakMap/WeakRef, their validated capture, step projection, checked declarations and page-shared limitations and unknowns, following the managedMemberView precedent. An entry is reused only for the same Evidence ID, which authenticates the parameters and limitations those checks read. Failed checks are not cached. Later pages therefore do only page-sized work.

Author readiness

Human owner: @kangyishuai

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4163 **Author:** [@kangyishuai](https://github.com/kangyishuai) **Created:** 10/15/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/4069-ui-state-transition-view` --- ### 📝 Commits (10+) - [`8ead9e6`](https://github.com/morluto/rea/commit/8ead9e633f09e3aea74df294da713667cda3c3f2) feat(analysis-view): page retained browser scenario steps and text - [`2155fa2`](https://github.com/morluto/rea/commit/2155fa2f45ccf2564df4b0c1cc12b0fae851dc9c) fix(analysis-view): order empty scenario receipt windows - [`a9b3c8c`](https://github.com/morluto/rea/commit/a9b3c8cc8fc342eb39334f77541ed302f9405d5b) Merge origin/main into feat/4069-ui-state-transition-view - [`32dc60f`](https://github.com/morluto/rea/commit/32dc60fc7deea0c88392c0548b690b1e30734f4d) chore(adb): keep main's AdbProvider formatting - [`92fe95e`](https://github.com/morluto/rea/commit/92fe95e87a268b37096e46a527e47c15661b6f4d) Merge origin/main into feat/4069-ui-state-transition-view - [`d24c2fe`](https://github.com/morluto/rea/commit/d24c2fe42ab8df1969b1032a991dfc6981849513) fix(browser-scenario): reject captures that contradict their selection or start step - [`b4463fd`](https://github.com/morluto/rea/commit/b4463fd9e07ac50276b458af4661e0badfbda385) fix(browser-scenario): keep step completeness equal to its artifacts - [`0cde897`](https://github.com/morluto/rea/commit/0cde8970c3a76d4bf5d5afc2e58f7c2bd4d88885) fix(browser-scenario): keep capture accounting equal to its steps and events - [`0d50bfe`](https://github.com/morluto/rea/commit/0d50bfe89615fd1e89c281a2bcfdd6613e511381) fix(browser-scenario): back transaction omissions with per-kind drops - [`1982ecf`](https://github.com/morluto/rea/commit/1982ecf500bb2b2f04cbd36f52b389a4023465da) fix(browser-scenario): accept metadata-budget transaction omissions ### 📊 Changes **46 files changed** (+6288 additions, -476 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/runtime-observation.md` (+5 -0) 📝 `docs/browser-scenario-contract.md` (+92 -9) 📝 `docs/mcp-contracts.md` (+6 -0) ➕ `scripts/lib/browser-scenario-view-e2e.mjs` (+317 -0) 📝 `scripts/verify-real-browser.mjs` (+4 -0) 📝 `src/application/analysisView/AnalysisViewService.test.ts` (+57 -1) 📝 `src/application/analysisView/AnalysisViewService.ts` (+8 -1) 📝 `src/application/environment/environmentScenario.test.ts` (+12 -11) 📝 `src/browser/BrowserScenarioPartialObservation.ts` (+2 -10) 📝 `src/browser/PlaywrightBrowserScenarioProvider.ts` (+15 -33) ➕ `src/browser/PlaywrightScenarioEvents.stepAttribution.test.ts` (+153 -0) 📝 `src/browser/PlaywrightScenarioEvents.test.ts` (+100 -9) 📝 `src/browser/PlaywrightScenarioEvents.ts` (+8 -9) 📝 `src/browser/assets/ScenarioScriptCapture.ts` (+3 -13) 📝 `src/browser/assets/ScriptCaptureAdapters.test.ts` (+11 -8) 📝 `src/browser/network/PlaywrightScenarioNetwork.ts` (+6 -9) 📝 `src/cli/analysisViewCommands.ts` (+1 -1) 📝 `src/contracts/analysisViewToolContracts.ts` (+2 -2) 📝 `src/contracts/browserToolContracts.ts` (+6 -5) 📝 `src/domain/analysisView/analysisView.ts` (+27 -3) _...and 26 more files_ </details> ### 📄 Description ## Summary Refs #4069 (RFC: evidence-backed UI state/transition observation primitives). Related: #39 (browser tracker) and draft #4107. #4107 derives one transition from two passive `inspect_web_page` records plus a caller-declared external action. This PR projects the steps of one REA-controlled `capture_browser_scenario` run. The two sources differ, and neither introduces a shared "UI state" owner. A shared vocabulary is left for the maintainer discussion in #4069. `capture_browser_scenario` already records a post-action capture for every declared step. Agents still could not read a completed capture step by step: - `inspect_analysis_view` rejected it as an unsupported parent. - `compare_web_captures` compares two runs, not consecutive steps within one run. - A 20-action capture with screenshot, DOM, accessibility and URL after each step is about 2.2 MB, measured below. ## Problem and expected behavior An external agent reconstructing an application's behavior needs, for each declared step: - what was done, as the recorded declaration; - whether the step completed; - which recorded page artifacts differ from the previous step's capture; - which events and requests were received in that step's receipt window. It needs this without re-reading the whole capture or re-running the browser. - Before: `inspect_analysis_view` returned `Parent Evidence operation "capture_browser_scenario" does not support analysis views` (source-derived from `unsupportedParent` in `src/domain/analysisView/analysisView.ts` on main). An oversized capture could only be exported. - After: three views of retained (MCP) or inline (CLI) capture Evidence: - `{kind: "summary"}` - `{kind: "browser-scenario", collection: "steps", offset, limit}` - `{kind: "browser-scenario", collection: "text", step_index, artifact: "accessibility" | "dom", offset, limit}` ## Change and scope - A new `capture_browser_scenario` parent for `inspect_analysis_view` and `inspect-analysis-view`. - **Summary:** browser identity, step outcomes, per-artifact capture coverage and step changes, declared and enabled event families, events received outside every step window, and network transaction counts. Transactions dropped by the transaction limit are counted separately, and `without_retained_request` counts retained transactions whose request event was dropped or never observed. - **Steps:** each row has the exact recorded action declaration and its family, outcome, recorded URLs, per-artifact coverage, comparison digest and `change` from the previous step's capture, the events received in the step's sequence window, and the network transactions whose retained request event was made at that step, with their own coverage. A transaction without a retained request counts toward no step; an unknown names such transactions. When network was not observed, the count is `null` with `not_requested` coverage, never `0`. Every page also carries `enabled_event_families` and `received_outside_step_windows`. - **Text:** UTF-16 pages of one step's retained accessibility or DOM text, with the producer's artifact SHA-256. - Single owners: - Step changes reuse the `compare_web_captures` comparison owner. `browserScenarioDiffEvidence` now exports per-step artifact observation, and `browserScenarioDiff` exports pair classification. Run comparison output is unchanged; see the evidence below. - The rule that selecting network content enables network events now lives in `enabledScenarioEventFamilies`. The Playwright producer and the view both call it, with no behavior change. - The view fails closed on inconsistent recorded data: - recorded actions must match steps by `step_id` and action; - step receipt windows must rise with `step_index` and stay disjoint; - every event must carry the step current at receipt: the last step that ran whose window had opened. This covers events between windows and after the final capture, not only those inside a window; - the capture must restate the scenario recorded in the parent parameters. This covers the browser mode; the `scenario` fields (start origin, action count, secret references, network content, event families, limits); and each step's artifacts, which follow `after_each_step` on every step and `at_end` on the final one. The producer and the views share one domain owner for the scenario projection and for the per-step artifact selection. - **Capture schema invariants, added from review.** `browserScenarioCaptureSchema` now rejects: - retained events whose family was not selected; - network transaction omissions when network was not selected; - a step 0 other than the completed `scenario_start`/`goto_start` step whose receipt window starts at event sequence 1, now a shared constant that the producer uses; - step completeness that does not list exactly the missing and truncated artifacts, plus `action` when the action did not complete. This is how the producer derives it. The comparison owner's completeness downgrade could no longer be reached, so it now depends only on the action outcome, and the text view reads recorded artifact states directly; - capture completeness that does not aggregate those step ledgers (each section once, adding only missing `events`), or that omits `events` despite connect mode, dropped events, a retained unfinished request, unavailable network content, or a popup while a gap family is selected; - a `network_transaction_range` that does not match the network events dropped for `max-network-transactions` (a reason only network kinds may carry), or per-transaction omissions that do not correlate with `by_kind` in both directions: each omitted kind and budget reason is dropped there, no kind is omitted by more transactions than its drops, and every budget drop of a network kind names its transaction; - launch or connect ownership and cleanup values that do not pair; - a cancelled step with captured artifacts, or a step after the first unfinished action that is not a never-ran cancellation; - a retained event sequence or step window end above the sequenced events: retained plus dropped, less the drops known to belong to refused transactions, which spend no sequence numbers; - a `by_kind` row counting fewer drops than its listed reasons and naming transaction records imply. Because `by_kind` keeps one count per kind across its reasons, refused-transaction drops are bounded rather than counted, and the omitted range may not exceed that bound. Exact per-reason counts would need a producer format change, which is left for a separate PR; - network content whose headers or body disagree with `header_values` and the phase's body selection, or content kept or dropped with no content facet selected; - a second `request` event for one transaction; - more retained events than `max_events`, or more admitted transactions than `max_network_transactions`. Step-completeness derivation, the popup gap families and the unavailable-content rule now each have one domain owner that the producer uses. Event rules live in `browserScenarioCaptureEvents.ts` and the capture fields in `browserScenarioCaptureFields.ts`. The comparison owner's per-step event sections could no longer be reached, so they now reduce to one capture-wide event coverage rule. Omission accounting keeps the producer's admission rule: a popup page refused by an event budget remains an omitted `popup-opened` record whenever any family is selected, because popup discovery runs for every selected family. The producer's final parse, `compare_web_captures` and these views share this schema, so all three reject such inline captures. Producer output already satisfied these rules; see Validation. - **Inline capture validation boundary.** Views and comparisons reject inline captures that: - are structurally invalid; - contradict the relations above, which the views compute from; - do not restate the scenario recorded in their parameters. They do not try to prove that any inline Evidence could have come from the recorder. These remaining recorder guarantees are left for a separate hardening PR: - strictly ascending event order; - consistent redirect fields, and the request coming first, within each transaction; - moving the receipt-window and step-provenance checks from the step projection into the capture schema, so that `compare_web_captures` gets them too; - per-reason drop counts in `by_kind`, which would replace the current bounds on refused-transaction drops but is a producer format change; - removing completeness sections that scenario captures can no longer use. Further findings that only hand-built captures can trigger will be answered with this boundary rather than with new rules here. Findings that affect producer output, view results for producer Evidence, or regressions will still be fixed. - **Intentionally not included:** - UI state keys and revisit detection, and cross-run state graphs. These need a shared owner with #4107 and maintainer input on #4069. - Control and executable-action inventory, which needs structured accessibility at the producer. - `detail: "summary"` delivery for `capture_browser_scenario`, planned as a separate PR because it changes a producer tool's input. - Native and Electron parents. - Coverage export. - Cross-layer joins. - **Alternatives rejected:** - A new tool: same intent as the existing view tool, plus catalog growth. - A within-run mode of `compare_web_captures`: inline-only inputs, a reduced compact schema, and observed/external-service labelling of a derivation. - A producer change: it would put derived facts into observed Evidence. ## Contract and boundary impact - **Semantic owner and earliest changed stage:** - domain step projection: `src/domain/browserScenarioSteps.ts`; - shared comparison owner: `browserScenarioDiffEvidence.ts` and `browserScenarioDiff.ts`; - view: `src/domain/analysisView/browserScenarioView*.ts`. - **Contribution scope fit and linked maintainer decision:** not applicable. This is a derived view over existing Evidence; it adds no tool, provider, execution, injection or mutation. Feedback on contract shape and ownership is requested on #4069. - **CLI and MCP/tool-catalog contract:** additive view kind and parent on `inspect_analysis_view` and `inspect-analysis-view`. The tool count stays at 207. Measured byte and token deltas are under Validation. - **Provider, bridge, target-format, or platform compatibility:** the producer emits the same data. `PlaywrightScenarioEvents` now calls the shared family rule. Views accept the current capture schema only. `compare_web_captures` now also rejects inline captures that contradict these capture rules; the producer never wrote such captures. The `compare_web_captures` contract example now records a consistent launch-mode capture with the producer's `goto_start` start step. - **Evidence, artifact, provenance, or reconstruction contract:** view Evidence is `rea.analysis-view` with confidence `derived`. It inherits the parent's `controlled-replay` authority under the existing view rule and links the parent. - This is the first view parent with `controlled-replay` authority, so exported bundle `scenarios` manifests will list view records with operation `inspect_analysis_view`. Maintainer input is welcome. - For subject-less parents, the analysis-view service now states the missing artifact identity once in view Evidence instead of repeating the parent's copy. - **Target selection, authority, upstream behavior, and cleanup details:** none; no target interaction. - **Generated metadata, package, or installation impact:** no tracked generated files changed. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - **Tests added or updated:** - `src/domain/analysisView/browserScenarioView.test.ts`, 13 cases: - consecutive comparison and revisit-equal digests; - failed and cancelled steps; - receipt windows, gaps and the after-final bucket; - capture-wide event coverage, including dropped events and connect mode; - truncated artifacts; - unobserved network reported as `null`, not `0`; - pages past the end; - fail-closed declarations and windows; - view-owned limitations kept under the message budget. - `src/domain/browserScenarioDiff.test.ts`: a new characterization case pins every incomparable branch with full objects (states, captured-side digest, exact reason). - `src/application/analysisView/AnalysisViewService.test.ts`: authority, links, parameters, and a single subject limitation. - `tests/boundary/mcp/analysisViewMcp.test.ts`: retained parent, Ajv validation against the advertised input and output schemas, and the public error projection. - `tests/boundary/cli/analysisViewCli.test.ts`: portable parent through the compiled CLI. - `scripts/lib/browser-scenario-view-e2e.mjs`: wired into `verify:browser`. - Capture invariants: - `src/domain/browserScenarioCapture.test.ts`: every capture rule, including omission accounting, the popup-discovery case and each event-gap cause. Mutation checks removed each rule branch one at a time, and each removal failed a test. - `src/browser/PlaywrightScenarioEvents.test.ts`: the producer's budget-refused popup page and network metadata over its budget both parse under the schema. - `src/domain/analysisView/browserScenarioView.test.ts`: exact-issue rejection of unselected-family events, of a foreign or failed step 0, of a ledger that contradicts retained text, and of capture accounting that contradicts its steps or omissions. - Hand-built fixtures in `browserScenarioDiff.test.ts`, `browserCli.test.ts`, `tests/fixtures/webScriptCapture.ts`, `ScriptCaptureAdapters.test.ts` and `webScriptExport.test.ts` now record producer-shaped captures. Each test keeps its property; for example, differing comparison anchors now come from unrelated events instead of unaccounted sequence gaps. - **Base reproduction or other evidence:** - The new comparison characterization case was also run with the identical fixture against `origin/main`'s comparison implementation in a temporary worktree. The serialized step results were identical, so the shared comparison refactor did not change these outputs. - The view also fails closed on malformed inline Evidence; see the domain tests. - **User-visible CLI/MCP output:** see the real step row below. - **Remaining proof gaps:** - No model-host discoverability evaluation was run. - Model token usage for agent tasks is unmeasured; tokenx estimates only. - Linux and Windows real-browser lanes were not run locally. - CPU time and memory cover the MCP server process only. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. <details><summary>Real step row (Chrome 155, self-owned fixture, trimmed)</summary> ```json { "context": { "source_completeness": "complete", "enabled_event_families": [ "console", "network" ], "received_outside_step_windows": { "retained": 0, "kinds": [] } }, "row": { "step_index": 1, "step_id": "open-settings", "action_class": "input", "action": { "step_id": "open-settings", "action": "click", "locator": { "kind": "role", "role": "button", "name": "Open settings" }, "button": "left", "click_count": 1 }, "status": "completed", "error": null, "elapsed_ms": 31, "before_url": { "url": "http://127.0.0.1:<port>/app", "origin": "http://127.0.0.1:<port>", "query_parameter_names": [], "redacted": false }, "after_url": { "url": "http://127.0.0.1:<port>/app", "origin": "http://127.0.0.1:<port>", "query_parameter_names": [], "redacted": false }, "artifacts": [ { "artifact": "accessibility", "coverage": "captured", "reason": null, "comparison_sha256": "bfbf5cd355389a3ac0153e2ad7e8c0efd1390a65bb2f80e181e8ed78fd2c94d1", "change": "changed" }, { "artifact": "url", "coverage": "captured", "reason": null, "comparison_sha256": "94611b2e44231cb6051645e0f41a503056bfeb4dba3b4c90005ff8867750d843", "change": "unchanged" } ], "events_received": { "retained": 1, "kinds": [ { "kind": "request", "count": 1 } ], "coverage": "captured", "sequence_start": 5, "sequence_end": 5 }, "network_transactions": { "coverage": "captured", "retained": 1 } } } ``` </details> ## Validation performed All results are on macOS arm64 with Node 24.14.0. The current head `848847efe` merges main `524159759` (82 new commits). The only conflict was the `inspect_analysis_view` description, from main's Rust dependency view (#5815). The merge keeps main's text and adds only this PR's browser scenario additions. Main's scenario-producer change only stops passing the already-absolute `executable_path` through `resolve()`. Main's new optional `resource_root` view field is not set by these views. On this head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on the 42 source test files outside `src/application/environment/` (692 tests). - Run together with main's 11 environment test files, 4 tests in main's `environmentExport.test.ts` failed (heap-headroom and snapshot-change checks). In another run, 1 test in main's `environmentBundleWrite.test.ts` failed. - On a clean main `524159759` worktree, the same file set failed the same way in two runs. - The 11 environment files passed 3 runs out of 3 on their own, both on this head and on clean main. - This PR does not touch those files. - `npm run test:focused` passed on 187 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,692 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` failed before reaching the scenario checks, at main's passive-observation check ("Page-scoped CDP disconnect was not reported as target_terminated", after main's 5891bfb30). It fails the same way on a clean main `524159759` worktree. - This PR's real-Chrome scenario-view e2e (`scenarioViews`) passed when run directly: 7 steps, accessibility text reassembled, steps view 14,713 bytes. The previous merge head `29d6cb932` merged main `4bfdadb7d`. The only conflict was the `inspect_analysis_view` description, from main's Frida session-status summaries (#5661). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 52 source test files (757 tests). - `npm run test:focused` passed on 177 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,658 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `e76981a41` merged main `17f581ff7`. The only conflict was the `inspect_analysis_view` description, from main's retained GDB and objdump summaries (#5614). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 52 source test files (756 tests). - `npm run test:focused` passed on 173 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,632 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `91cb1b197` merged main `7d6f90485`. The only conflict was the `inspect_analysis_view` description, from main's model artifact view (#5603). The merge keeps main's text, including `inspect_model_artifact`, and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 52 source test files (756 tests). - `npm run test:focused` passed on 170 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,614 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `4e25db1b0` merged main `c2858d2aa`. The only conflicts were the `inspect_analysis_view` and `inspect-analysis-view` descriptions, from main's Go build metadata view (#5524). The merge keeps main's text and adds only this PR's browser scenario additions. Main's scenario timer bound (#3515) and session-test cleanup merged cleanly and do not change the capture shape. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 52 source test files (756 tests). - `npm run test:focused` passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,632 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous head `28af2811d` fixed a step-attribution error found in review. It affects real producer output. Step rows used to count a transaction under the step that `scenarioNetworkTransactions` assigned it. When the request event was not retained, that function falls back to the step of the first retained event. The recorder can drop a request over `max_event_allocation_bytes` and still keep its response from a later step, and connect mode can attach while a request is in flight. - **Fix:** `scenarioNetworkTransactions` now reports `request_retained`, and step rows count only transactions with a retained request. The summary adds `without_retained_request`, and an unknown names those transactions. Comparisons keep their attribution, and fingerprints do not read the new field. - **Regression test:** `PlaywrightScenarioEvents.stepAttribution.test.ts` drives the real recorder until it drops a step-1 request and keeps its step-3 response, then projects the capture through the summary and step views. With every transaction attributed to a step again, the test fails. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 51 source test files (738 tests). - `npm run test:focused` passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,617 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The steps view is 14,747 bytes, unchanged. The previous merge head `43c13190a` merged main `cf5194f68` (41 new commits). The only textual conflict was the `inspect_analysis_view` description, from main's IL2CPP native-method linking change. The merge keeps main's text and adds only this PR's browser scenario additions. Two main changes touch the scenario producer and merged cleanly: - `599fdac02` waits for late navigation to stop before releasing attached browser sessions. - `0e83d92c5` changes `executable_path` validation. Neither changes the capture's steps, events, completeness or cleanup values, which this PR's schema and views read. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 50 source test files (737 tests), including main's updated scenario session, browser and provider tests. - `npm run test:focused` passed on 167 boundary, acceptance, contract, catalog, environment and scenario-cleanup files: 1,617 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`. That includes `scenarioViews`, the failure scenario, and cleanup recorded as `terminated-owned-process` in launch mode and `disconnected-external` in connect mode. The previous merge head `d82100dc4` merged main `21e0c580e`. The only conflict was the `inspect_analysis_view` description, from main's IL2CPP change (#5326). The merge keeps main's text and adds only this PR's browser scenario additions. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 49 source test files (698 tests). - `npm run test:focused` passed on 164 boundary, acceptance, contract, catalog and environment files: 1,528 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `aac8f3136` merged main `1417bf943`. Main's native function matching view (#5390) added its summary result schema at the same place as this PR's. The merge keeps both in the summary union keyed on `parent_operation`. Main's other new commits do not touch the browser scenario producer, capture schema, views or scenario fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 49 source test files (692 tests). - `npm run test:focused` passed on 164 boundary, acceptance, contract, catalog and environment files: 1,493 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `c1fcb5c5f` merged main `2538214e2`. Main's native inventory view (#5378) changed how summaries are typed: each parent operation now has its own summary result schema, and the summaries form a union keyed on `parent_operation`. This PR's summary now follows that pattern with `browserScenarioSummaryViewResultSchema`, replacing the earlier single summary schema whose body could be any of several shapes. The tool description, the CLI description and the generic unsupported-parent message are main's text plus only this PR's browser scenario additions. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 49 source test files (692 tests). - `npm run test:focused` passed on 163 boundary, acceptance, contract, catalog and environment files: 1,492 passed, 48 skipped. This includes the analysis-view MCP output-schema validation. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `bddd1eaae` merged main `0134732fc`. Main's protobuf capture view (#5031) conflicted in two places: - `UnsignedAnalysisView`: main now derives this type from `AnalysisViewResult` instead of listing each kind, so the browser-scenario result needs no separate entry. - The tool description: main's text, including the protobuf sentence, plus only this PR's additions. Main's other new commits (#5350, #5355, #5356) merged without conflicts and do not change the browser scenario producer, capture schema or scenario fixtures. Main also added a dependency, so I reran `npm ci`. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 48 source test files (687 tests). - `npm run test:focused` passed on 161 boundary, acceptance, contract, catalog and environment files: 1,464 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `05a17923a` merged main `3fca2e0ca`. Main's #5319 adds an `environment-state` view that also reads `capture_browser_scenario` parents. The merge keeps both sides: - **Routing:** grouping views (`api-surface`, `environment-manifest`, `environment-state`) still go to main's projectors first, and this PR's projector serves `summary` and `browser-scenario`. This PR's refusal for any other view kind now names all four supported views. - **Fallback error:** main's "only environment-manifest and environment-state" branch can no longer be reached, so the generic unsupported-parent message stays. - **Docs:** `docs/mcp-contracts.md` places main's environment-state paragraph right after the environment-manifest paragraph it refers to, followed by this PR's paragraph. - **Tool description:** main's text plus only this PR's additions. - **Main's test:** `environmentScenario.test.ts` keeps this PR's summary-succeeds check, with the four-view message. - **Shared fixture:** main's new test builds the scenario fixture with `header_values` selected. The fixture now records content-event headers as captured when header values are selected (an empty list, since the fixture records no header names), as this PR's capture schema requires. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 46 source test files (679 tests), including main's `environmentStateView`, environment and `scenarioCaptureRecords` tests. - `npm run test:focused` passed on 160 boundary, acceptance, contract, catalog and environment files: 1,460 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `ae827b13f` merged main `f93a0335a`. Main's #5293 adds an `evidence-file` source to `inspect_analysis_view` and new service tests. Two conflicts came from it: - the tool description, which now keeps main's evidence-file sentence alongside this PR's `capture_browser_scenario` text; - `AnalysisViewService.test.ts`, which now keeps both this PR's browser-scenario authority test and main's `inspectAnalysisViewWithFiles` tests. For the summary, steps and text views of the fixture capture, an `evidence-file` source gives the same `normalized_result`, `controlled-replay` authority and parent link as the same Evidence passed inline. I checked this with a local script, not a committed test. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 43 source test files (632 tests), including main's new evidence-file and `scenarioCaptureRecords` tests. - `npm run test:focused` passed on 158 boundary, acceptance, contract, catalog and environment files: 1,448 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.85 and `verified: true`, including `scenarioViews`. The previous merge head `150d147ec` merged main `5fa4defcb`. The only conflict was the `inspect_analysis_view` description, where main's Godot PCK change (#3634) rewrote the inventory text. The merge keeps main's text and adds only this PR's `capture_browser_scenario` parent and view sentences. #3634 does not touch the browser scenario producer, capture schema, views or fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 42 source test files (616 tests). - `npm run test:focused` passed on 158 boundary, acceptance, contract, catalog and environment files: 1,447 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews`. The previous merge head `d499af870` merged main `833136507`. Main's new `il2cpp-metadata` view (#3807) conflicted with this PR's browser-scenario views in the analysis-view request and result unions, parent operations, summaries, unsigned view types and the tool description. The merge keeps both view kinds. The generic unsupported-parent message names both IL2CPP metadata and browser scenario captures. The tool description is main's text plus only this PR's additions. #3807 does not touch the browser scenario producer, capture schema or fixtures. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 41 source test files (605 tests), including main's environment, `scenarioCaptureRecords` and IL2CPP view tests. - `npm run test:focused` passed on 158 boundary, acceptance, contract, catalog and environment files: 1,447 passed, 48 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews`. The previous merge head `2cae99f53` merged main `bf1eeee94`. Main's #5182 builds environment manifests from `capture_browser_scenario` parents, so one parent kind now has two view paths: - **Routing:** `environment-manifest` views still go to main's projector first. This PR's projector serves `summary` and `browser-scenario` views for the same parents, and its refusal for any other view kind now names all three supported views. - **Fallback error:** main's "supports only the environment-manifest view" branch can no longer be reached, so the generic unsupported-parent message stays. - **Tool description:** keeps both sides' text. - **Main's fixture:** `tests/fixtures/browserScenarioCapture.ts` built captures the recorder cannot emit, which this PR's capture schema rejects. I made it match the recorder without changing its transactions or record content: - step 0 is the implicit start navigation; - step 1 is the declared `wait_for_timeout` and receives every event; - content events exist only for selected body facets; - the recorded scenario declares the capture's event, network and artifact selection. - **Main's test:** `environmentScenario.test.ts` used `summary` as its unsupported example. It now expects `summary` to succeed on the same Evidence, and checks that an unsupported view names the supported ones. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 40 source test files (595 tests), including main's environment and `scenarioCaptureRecords` tests. - `npm run test:focused` ran 327 boundary, acceptance, contract and environment files: 3,529 passed, 122 skipped, 2 failed. The failures are `javascriptRuntimeObservation` ("includes targets outside the old caller root filter") and `filesystemObservationRaceCli` ("keeps a stable symlink observation complete"). Both fail the same way on a clean main `bf1eeee94` worktree on this macOS machine, and this PR does not touch those areas. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews`. The previous head `757bbdfc3` fixed a paging cost found in review. Every page used to rerun the declaration checks and the page-shared limitations and unknowns over the whole capture; these results now join the sealed-parent cache. A new test pages one sealed result and checks that another Evidence ID over the same frozen result is checked on its own. With the Evidence ID binding removed, that test fails. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 29 capture, view, producer, network, scenario-service, observation and Evidence test files (477 tests). - `npm run test:focused` passed on 64 boundary, acceptance and contract files: 526 passed, 5 skipped. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews`; the steps view is 14,713 bytes, unchanged. - In-process paging measurement: a sealed parent with N click actions, all steps paged 20 at a time through `projectAnalysisView`, two runs each on macOS arm64. Output is unchanged, so view bytes and tokens are unchanged. MCP end-to-end time was not re-measured. | Actions | Warm ms per page, before → after | All warm pages, before → after | Process user CPU, before → after | Process RSS MiB, before → after | |---|---|---|---|---| | 200 | 0.97–1.07 → 0.33–0.36 | 10 ms → 3–4 ms | 0.29–0.39 s → 0.25–0.26 s | 237–241 → 204–216 | | 2,000 | 3.5–3.7 → 0.26 | 354–368 ms → 26 ms | 0.96–0.99 s → 0.51–0.53 s | 289–295 → 282–283 | | 10,000 | 17.0–19.1 → 0.20 | 8.5–9.5 s → 0.1 s | 10.8–11.8 s → 1.2 s | 671–672 → 529–547 | The cold first page, which parses and projects the capture, is unchanged (8–9 ms, 42–52 ms and 180–190 ms). A summary request still tallies every step once per request. The previous merge head `aa689d960` merged main `b4938aed5`. Main's new `desktop-application` view (#5207) conflicted with this PR's browser-scenario views in the `inspect_analysis_view` tool description and the `inspect-analysis-view` CLI command description. The merge keeps both view kinds, so each description is main's text plus this PR's additions. None of the main commits since the previous merge touched the browser scenario producer or capture schema. On that head: - `npm run check:fast` passed (typecheck and lint). - `npm run test:local` passed on 29 capture, view, producer, network, scenario-service, observation and Evidence test files (476 tests). - `npm run test:focused` passed on 64 boundary, acceptance and contract files: 526 passed, 5 skipped. - `npm run docs:check` passed. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews`. The previous merge head `2fa0ba9e8` merged main `fdaffe21c`. There, main's `environment-manifest` view (#4954) conflicted in the tool description, `docs/mcp-contracts.md` and `tests/boundary/cli/analysisViewCli.test.ts`, and the merge kept both view kinds and all three CLI tests. The same checks passed on that head: 476 unit tests, 464 boundary tests (5 skipped), `docs:check` and `verify:browser`. The previous merge head `6ae23996e` merged main `bec88b9d7`. Its only conflict was in `scripts/verify-real-browser.mjs`, where both sides added a verifier import, and the merge kept both. `verify:browser` passed there too, including main's `urlUserInfo` check. The previous head `bf85798c4` added only a change to the views, which now merge the capture's own limitations with the envelope's. I validated it with `npm run check:fast`, the capture, view and producer unit tests (277), the analysis-view MCP/CLI boundary and output-schema tests (35), and the real-Chrome scenario-view e2e. On `688f33b2a`, which merges main `d9d085a15` and includes the earlier review fixes: - `npm run check` passed, with lint at 0 errors. - `npm run docs:check`, `npm run verify:test-discovery`, `npm run jscpd` (3.28%) and `npm run scan:todos` passed. - `npm run test:local` passed on 16 capture, view and producer test files (280 tests). - `npm run test:focused` passed on 46 boundary, acceptance and contract files: 482 passed, and 2 tests in `serverLifecycleMcp.test.ts` were skipped as unrelated. On the previous head, 1dd14fe0b, one run hit 30-second timeouts in 5 unrelated files while the machine's load average was about 10. Those 5 files and then the full set passed when rerun, and the full set passed in one run on this head. - `npm run verify:browser` passed with Google Chrome 155.0.8059.80 and `verified: true`, including `scenarioViews` and popup coverage. - Producer conformance stress run: the real `PlaywrightScenarioEvents` recorder and network collector, driven by fake Playwright objects, across all 256 family subsets, with and without network content, 8 budget settings (including network metadata budgets), and launch and connect ownership. Capture completeness was computed with the provider's own rule. All 8192 captures were accepted by the schema. The `max-listener-owners` reason was not exercised. - A real Chrome capture with only `console` selected, `max_events: 1` and a `window.open` popup completes, with two omitted `popup-opened` records. Before the popup adjustment, the stricter rule rejected this producer output. Earlier results on the first PR commit, rebased onto main `9d6651e5b`: - `npm run check` passed, with lint at 0 errors. - `npm run docs:check` passed. - `npm run verify:test-discovery` passed. - `npm run jscpd` passed (3.18%). - `npm run scan:todos` passed. - `npm run test:local` passed on 19 domain, service and producer test files (291 tests), including `src/domain/analysisView/*`, `src/application/analysisView/*`, `src/domain/browserScenario*.test.ts`, `src/domain/evidence.test.ts` and `src/browser/PlaywrightScenario*.test.ts`. - `npm run test:focused` passed on 17 files (194 tests): the analysis-view MCP and CLI boundaries plus `toolSchemaValidity`, `contractPresentation`, `toolInputAdmission`, `compactInputSchemaMcp`, `outputSchemaProfile`, `serverIdentity`, `cliToolCoverage`, `jsonInputStatus`, `cliStartupImports`, `productCatalog`, `documentationFacts`, `targetContentProvenance`, `toolEffects`, `toolInputReferences` and `tupleSchemaParity`. - `npm run verify:browser` passed with Google Chrome 155.0.8059.40 and `verified: true`. That includes the new `scenarioViews` check: a self-owned fixture with known transitions, run through stdio MCP and the compiled CLI on the same Evidence with identical view output. It also confirms paged accessibility and DOM text reassemble to the producer SHA-256, and that per-window plus outside-window event counts equal the retained total. Representative task measurement: one stdio MCP server process, 3 runs, 20 click actions with screenshot, DOM, accessibility and URL captured after each step. | Call | Wall ms | Server CPU s | Server peak RSS MiB | Response bytes | tokenx estimate | |---|---|---|---|---|---| | `capture_browser_scenario` (21 steps) | 2505–2678 | 0.37–0.52 | 457–461 | 2,189,439 | 461,055 | | summary, cold (first view of the parent) | 17–19 | 0.02–0.03 | 458–461 | 6,908 | 1,869 | | summary, warm (cached projection) | 2 | ≤0.01 | 450–453 | 6,908 | 1,869 | | steps page, 21 rows, warm | 4–6 | ≤0.01 | 450–453 | 34,746 | 10,151 | | text page, 4,096 code units, warm | 2 | ≤0.01 | 450–453 | 7,536 | 1,975 | The capture call is unchanged by this PR. Capture time and RSS cover the MCP server process only; Chromium processes are not included. Token counts are `tokenx` estimates of the returned text, not billed model usage. Catalog effect, comparing main `9d6651e5b` with the first PR commit (not re-measured after the merge and the review fix): - tools: 207 → 207 (+0) - tools/list full bytes (name+description+input): 824,508 → 826,128 (+1,620) - tools/list compact bytes: 189,737 → 190,263 (+526) - inspect_analysis_view description chars: 6,249 → 6,761 (+512) - inspect_analysis_view description tokenx: 1,586 → 1,726 (+140) - inspect_analysis_view full input bytes: 23,095 → 24,189 (+1,094) - inspect_analysis_view full input tokenx: 6,593 → 6,895 (+302) - inspect_analysis_view compact input bytes: 333 → 333 (+0) - inspect_analysis_view output schema bytes: 128,229 → 142,453 (+14,224) ## Compatibility, safety, and release - **Breaking changes or migration steps:** no producer output changes. Hand-built inline captures that contradict these capture rules are now rejected as malformed input. This includes inline captures passed to main's `environment-manifest` view (#5182), which parses the same schema. Main's hand-built fixture in `tests/fixtures/browserScenarioCapture.ts` was one of them, and this PR updates it to match what the recorder emits (details under Validation). - **Real Hopper/Ghidra, browser, or OS coverage:** real Chrome on macOS arm64 only. The CI real-browser lane is selected by the touched `src/domain/browser*` and `src/browser/**` files. - **Package or release metadata impact:** none. - **Security, privacy, process, or containment review:** - Views return producer-redacted text and recorded declarations: caller literals and secret references, never resolved secret values. - No new data collection, process, network, or filesystem effect. - Inline captures are fully schema-validated on each call. Sealed retained parents cache, by object identity through a WeakMap/WeakRef, their validated capture, step projection, checked declarations and page-shared limitations and unknowns, following the `managedMemberView` precedent. An entry is reused only for the same Evidence ID, which authenticates the parameters and limitations those checks read. Failed checks are not cached. Later pages therefore do only page-sized work. ## Author readiness Human owner: @kangyishuai - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6103
No description provided.