frontend-patterns/research/experiments/boundary-recovery/README.md
tegwick 3eecd74b0d Probe rendering and modal focus boundaries in pinned browser fixture
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-26 22:01:35 +02:00

3.5 KiB

Rendering and focus boundary experiment

Executed 2026-09-26. Real React 19.3.0 / Radix Dialog 1.1.23 in a synthetic browser fixture; no deployed application, provider inference or production requests. Review and interpretation.

Reproduce

From this directory:

npm ci --ignore-scripts --cache /tmp/fep-npm-cache
FEP_CHROMIUM_PATH=/path/to/chrome FEP_RESULT_PATH=/tmp/immediate.json npm test
FEP_CHROMIUM_PATH=/path/to/chrome FEP_NESTED_SETTLE_MS=100 FEP_RESULT_PATH=/tmp/settled.json npm test

Use a locally available Chromium executable, or omit FEP_CHROMIUM_PATH to use Playwright's installed browser. The recorded runs used preinstalled Chromium 151.0.7922.34, not an assertion of compatibility with every browser. Version and executable hash are in provenance; the lockfile pins all packages with archive integrity. Installation does not download a browser automatically here. The runner binds an ephemeral loopback port, bundles the fixture in memory, and closes browser/server in finally. No external network is used by the cases.

Output defaults to /tmp/fep-boundary-result.json; reviewed receipts are not overwritten automatically. A failed obligation makes the runner exit nonzero. The immediate nested-dialog case is known to be timing-sensitive; do not hide it with retries or interpret a later pass as its resolution. FEP_NESTED_SETTLE_MS changes only the pause after nested activation before the first Escape. It is an experimental variable, not a proposed product fix. The browser uses React's development build, without StrictMode. Production builds and other browsers remain untested.

Recorded cases

Case Observation Immediate / 100 ms settled
X01 Render fault uses inner fallback; sibling stays interactive pass / pass
X02 Event-handler exception reaches browser error reporting, not boundary pass / pass
X03 Synthetic GET returns 503; explicit catch sets request-failure state pass / pass
X04 Fault in inner fallback reaches outer boundary pass / pass
X05 Closing modal returns focus to surviving invoker pass / pass
X06 Removing invoker leaves BODY focused with default behavior pass / pass (negative control)
X07 Application close policy selects surviving next-record button pass / pass
X08 Nested close should return to parent trigger, then outer trigger fail / pass

result-immediate.json records 7/8; result-settled.json records 8/8 with identical code/input hashes, except the declared timing parameter. “Pass” means an observation matched its assertion, not that the UX is acceptable: X06 deliberately expects the missing workflow target.

development-results.json preserves three earlier runs in order: 7/8, 8/8, 7/8. They preceded final diagnostic instrumentation; their old runner bytes are not archived, so they are development history, not exact reproducible snapshots. Current-code receipts above retain their matching SHA-256 input hashes. All five runs are reported; no estimate of failure frequency is justified.

Scope

Focus checks inspect activeElement and keyboard behavior, not screen-reader output or full accessibility conformance. X03 handles a response error explicitly; it does not test unhandled promises, timeouts, mutation idempotency or remote rollback. Nested dismissal remains unresolved. Dependency/version compatibility and fixture lifecycle effects must be separated before attributing a library defect.