Probe rendering and modal focus boundaries in pinned browser fixture

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
This commit is contained in:
tegwick 2026-09-26 22:01:35 +02:00
parent 50c68575f1
commit 3eecd74b0d
18 changed files with 2067 additions and 0 deletions

10
TREE.md
View file

@ -348,6 +348,15 @@ research/evaluations/language-pilot-01/summarize.py
research/evaluations/language-pilot-01/summary.json
research/examples/SchemaEvidence.md
research/examples/schema-contract.yaml
research/experiments/boundary-recovery/README.md
research/experiments/boundary-recovery/development-results.json
research/experiments/boundary-recovery/experiment.mjs
research/experiments/boundary-recovery/fixture.jsx
research/experiments/boundary-recovery/package-lock.json
research/experiments/boundary-recovery/package.json
research/experiments/boundary-recovery/provenance.json
research/experiments/boundary-recovery/result-immediate.json
research/experiments/boundary-recovery/result-settled.json
research/experiments/matrix-recovery/README.md
research/experiments/matrix-recovery/experiment.mjs
research/experiments/matrix-recovery/package-lock.json
@ -359,6 +368,7 @@ research/plan/ResearchProgram.md
research/plan/ResearchProtocol.md
research/plan/ValidationCases.md
research/reviews/AuthorityPilot.md
research/reviews/BoundaryRecoveryExperiment.md
research/reviews/CarniatoAdmissionRegression.json
research/reviews/FrontendRecoveryImplementations.md
research/reviews/FrontendRecoverySources.json

View file

@ -48,3 +48,7 @@ Latest expansion: [six broader candidates and decision probes](reviews/PatternBr
cover presentation logic, derived state, feature organization, client activation,
failure containment and modal focus. Current total: sixteen developed hypotheses;
previous counts above describe earlier slices. No additional runtime or model runs.
Latest execution: [rendering/focus boundary experiment](reviews/BoundaryRecoveryExperiment.md)
uses pinned React/Radix packages in Chromium. It records seven stable case observations
and a timing-sensitive nested-dialog failure; no production or accessibility certification.

View file

@ -38,3 +38,11 @@ Evidence locators: [breadth review](../reviews/PatternBreadthExpansion.md).
All relations are research proposals; no canonical identity or admitted graph edges.
The next test is planned, not executed. Known uses of the complete proposed contract
have not been verified in this investigation.
## Executed partial evidence — 2026-09-26
[Boundary experiment](../reviews/BoundaryRecoveryExperiment.md) X01–X04 runs real
React 19.3.0 in a browser fixture. A render fault stays inside its region; an event
handler error does not; a handled 503 uses explicit request state; a broken fallback
reaches the enclosing boundary. These are bounded mechanism observations, not
production recurrence or evidence for mutation rollback. Retain hypothesis maturity.

View file

@ -40,3 +40,14 @@ Evidence locators: [breadth review](../reviews/PatternBreadthExpansion.md).
All relations are research proposals; no canonical identity or admitted graph edges.
The next test is planned, not executed. Known uses of the complete proposed contract
have not been verified in this investigation.
## Executed partial evidence — 2026-09-26
[Boundary experiment](../reviews/BoundaryRecoveryExperiment.md) X05–X08 runs Radix
Dialog 1.1.23. Ordinary return succeeds; removal of the trigger leaves BODY focused
unless the application supplies a surviving target. Nested dismissal is timing-sensitive:
immediate Escape failed the parent-return obligation in the final recorded immediate
run, while a 100 ms settling variant passed. Neither focus arrival nor dialog visibility
alone proves dismissal ownership has stabilized. The delay is an experimental variable,
not a recommended fix. Root cause and accessibility conformance remain unverified;
retain hypothesis maturity and test lifecycle ownership before a stronger claim.

View file

@ -122,3 +122,11 @@ addition. Six further developed hypotheses bring the current total to **sixteen*
non-overlap, counterexamples, synthetic probes and deferred candidates. Initial seed
rows above are historical inventory, not additional developed records. No canonical
admission, proven recurrence or demonstrated presentation benefit is claimed.
## Partial boundary execution — 2026-09-26
[Browser experiment](../reviews/BoundaryRecoveryExperiment.md) adds partial evidence
for Failure Boundary and Focus Return. Seven of eight immediate-run assertions met;
a separate 100 ms nested-settling condition met eight. Removed-trigger default behavior
is a negative control, and immediate nested dismissal remains unresolved. Sixteen
hypotheses retained, no admission or full-pattern recurrence claim.

View file

@ -109,3 +109,16 @@ All accessed 2026-09-26; exact revisions unknown unless stated. Bounded document
review, no runtime or deployment validation. [Claims B01–B06 and dispositions](../reviews/PatternBreadthExpansion.md).
These sources address different mechanisms and do not establish independent
recurrence of any one complete candidate.
## Browser boundary execution — 2026-09-26
SRC-0029: React 19.3.0 / React DOM 19.3.0 and Radix Dialog 1.1.23, with pinned
transitive dependencies and inspected published modules. [Provenance](../experiments/boundary-recovery/provenance.json)
and lockfile identify archives/integrity; synthetic Chromium fixture, not a deployed
application. One mechanism study, not independent full-pattern recurrence.
SRC-0030: [Radix Dialog documentation](https://www.radix-ui.com/primitives/docs/components/dialog),
Content API and keyboard interaction; living page accessed 2026-09-26, revision
unknown. Displayed documentation version differs from executed package; runtime
claims use the pinned archive, not an assumption that the page exactly matches it.
[Results and limits](../reviews/BoundaryRecoveryExperiment.md).

View file

@ -0,0 +1,63 @@
# 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](../../reviews/BoundaryRecoveryExperiment.md).
## Reproduce
From this directory:
```bash
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](provenance.json); 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](result-immediate.json) records 7/8; [result-settled.json](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](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.

View file

@ -0,0 +1,333 @@
[
{
"date": "2026-09-26T19:57:23.882Z",
"evidence": "synthetic-browser-fixture-real-libraries",
"node": "v24.11.1",
"browser": "151.0.7922.34",
"browserSelection": "explicit executable override",
"packages": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
},
"files": {
"fixture.jsx": "d01a78f533a8ac4c086fc93845ce5b86a5b676aee86a22ee53ac91ad2a77f936",
"experiment.mjs": "981d39a45d22503077817cb032145f1e05221821c157be18ca7a509667a5e384",
"package.json": "a0ad37e8518b0c561d6b4a091ee51f5bca90e0ce329b2be7ba75347f6dc26ae7",
"package-lock.json": "ec4b544d441f89d9c512bc9931c1818473327105d5ef9047dc04d6a721e68463"
},
"cases": [
{
"id": "X01-render-contained",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "inner",
"message": "render fault"
}
],
"globalErrors": [],
"requests": []
},
"siblingUsable": true,
"browserErrors": []
},
{
"id": "X02-event-not-contained",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [
"Uncaught Error: event fault"
],
"requests": []
},
"panelPresent": true,
"browserErrors": [
"event fault"
]
},
{
"id": "X03-request-explicit-state",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [
503
]
},
"state": "request failed",
"browserErrors": []
},
{
"id": "X04-fallback-escapes-inner",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "outer",
"message": "fallback fault"
}
],
"globalErrors": [],
"requests": []
},
"enclosingFallback": true,
"browserErrors": []
},
{
"id": "X05-focus-existing-invoker",
"status": "pass",
"activeId": "invoker",
"browserErrors": []
},
{
"id": "X06-focus-removed-default",
"status": "pass",
"activeTag": "BODY",
"workflowTargetSelected": false,
"browserErrors": []
},
{
"id": "X07-focus-removed-explicit",
"status": "pass",
"activeId": "next",
"applicationPolicy": true,
"browserErrors": []
},
{
"id": "X08-nested-return",
"status": "fail",
"error": "page.waitForFunction: Timeout 5000ms exceeded.",
"browserErrors": []
}
],
"passed": 7,
"total": 8
},
{
"date": "2026-09-26T19:57:42.875Z",
"evidence": "synthetic-browser-fixture-real-libraries",
"node": "v24.11.1",
"browser": "151.0.7922.34",
"browserSelection": "explicit executable override",
"packages": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
},
"files": {
"fixture.jsx": "d01a78f533a8ac4c086fc93845ce5b86a5b676aee86a22ee53ac91ad2a77f936",
"experiment.mjs": "5268d5cfefedb6186333bcc014080757821f2bb4820f70397b22474bd3264414",
"package.json": "a0ad37e8518b0c561d6b4a091ee51f5bca90e0ce329b2be7ba75347f6dc26ae7",
"package-lock.json": "ec4b544d441f89d9c512bc9931c1818473327105d5ef9047dc04d6a721e68463"
},
"cases": [
{
"id": "X01-render-contained",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "inner",
"message": "render fault"
}
],
"globalErrors": [],
"requests": []
},
"siblingUsable": true,
"browserErrors": []
},
{
"id": "X02-event-not-contained",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [
"Uncaught Error: event fault"
],
"requests": []
},
"panelPresent": true,
"browserErrors": [
"event fault"
]
},
{
"id": "X03-request-explicit-state",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [
503
]
},
"state": "request failed",
"browserErrors": []
},
{
"id": "X04-fallback-escapes-inner",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "outer",
"message": "fallback fault"
}
],
"globalErrors": [],
"requests": []
},
"enclosingFallback": true,
"browserErrors": []
},
{
"id": "X05-focus-existing-invoker",
"status": "pass",
"activeId": "invoker",
"browserErrors": []
},
{
"id": "X06-focus-removed-default",
"status": "pass",
"activeTag": "BODY",
"workflowTargetSelected": false,
"browserErrors": []
},
{
"id": "X07-focus-removed-explicit",
"status": "pass",
"activeId": "next",
"applicationPolicy": true,
"browserErrors": []
},
{
"id": "X08-nested-return",
"status": "pass",
"innerReturn": "nested-invoker",
"outerReturn": "invoker",
"browserErrors": []
}
],
"passed": 8,
"total": 8
},
{
"date": "2026-09-26T19:58:16.005Z",
"evidence": "synthetic-browser-fixture-real-libraries",
"node": "v24.11.1",
"browser": "151.0.7922.34",
"browserSelection": "explicit executable override",
"packages": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
},
"files": {
"fixture.jsx": "d01a78f533a8ac4c086fc93845ce5b86a5b676aee86a22ee53ac91ad2a77f936",
"experiment.mjs": "df9912cacd5b01b10fdc70a45d50b36d91202c44c0f57358334a1d3c41b4c88d",
"package.json": "a0ad37e8518b0c561d6b4a091ee51f5bca90e0ce329b2be7ba75347f6dc26ae7",
"package-lock.json": "ec4b544d441f89d9c512bc9931c1818473327105d5ef9047dc04d6a721e68463"
},
"cases": [
{
"id": "X01-render-contained",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "inner",
"message": "render fault"
}
],
"globalErrors": [],
"requests": []
},
"siblingUsable": true,
"browserErrors": []
},
{
"id": "X02-event-not-contained",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [
"Uncaught Error: event fault"
],
"requests": []
},
"panelPresent": true,
"browserErrors": [
"event fault"
]
},
{
"id": "X03-request-explicit-state",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [
503
]
},
"state": "request failed",
"browserErrors": []
},
{
"id": "X04-fallback-escapes-inner",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "outer",
"message": "fallback fault"
}
],
"globalErrors": [],
"requests": []
},
"enclosingFallback": true,
"browserErrors": []
},
{
"id": "X05-focus-existing-invoker",
"status": "pass",
"activeId": "invoker",
"browserErrors": []
},
{
"id": "X06-focus-removed-default",
"status": "pass",
"activeTag": "BODY",
"workflowTargetSelected": false,
"browserErrors": []
},
{
"id": "X07-focus-removed-explicit",
"status": "pass",
"activeId": "next",
"applicationPolicy": true,
"browserErrors": []
},
{
"id": "X08-nested-return",
"status": "fail",
"error": "Expected focus nested-invoker; got {\"id\":\"invoker\",\"tag\":\"BUTTON\",\"dialogs\":0}: page.waitForFunction: Timeout 5000ms exceeded.",
"browserErrors": []
}
],
"passed": 7,
"total": 8
}
]

View file

@ -0,0 +1,124 @@
import {build} from 'esbuild';
import {chromium} from 'playwright';
import {createServer} from 'node:http';
import {once} from 'node:events';
import {readFile, writeFile} from 'node:fs/promises';
import {createHash} from 'node:crypto';
import {fileURLToPath} from 'node:url';
import assert from 'node:assert/strict';
const dir = fileURLToPath(new URL('.', import.meta.url));
const bundle = await build({entryPoints: [dir + 'fixture.jsx'], bundle: true, write: false,
define: {'process.env.NODE_ENV': '"development"'}, format: 'iife'});
const server = createServer((req, res) => {
if (req.url === '/bundle.js') {
res.writeHead(200, {'Content-Type': 'application/javascript'}); res.end(bundle.outputFiles[0].contents);
} else if (req.url === '/failure') {
res.writeHead(503, {'Content-Type': 'text/plain'}); res.end('synthetic unavailable');
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<!doctype html><html lang="en"><head><title>Boundary probe</title></head><body><div id="root"></div><script src="/bundle.js"></script></body></html>');
}
});
server.listen(0, '127.0.0.1'); await once(server, 'listening');
const url = `http://127.0.0.1:${server.address().port}`;
let browser;
const cases = [];
async function probe(id, query, action) {
const page = await browser.newPage();
page.setDefaultTimeout(5000);
const browserErrors = [];
page.on('pageerror', e => browserErrors.push(e.message));
try {
await page.goto(url + query);
await page.getByRole('button').first().waitFor();
const observed = await action(page);
cases.push({id, status: 'pass', ...observed, browserErrors});
} catch (error) {
cases.push({id, status: 'fail', error: error.message, observed: await page.evaluate(() => window.observed).catch(() => null), browserErrors});
} finally {await page.close();}
}
async function active(page, id) {
try { await page.waitForFunction(id => document.activeElement?.id === id, id, {timeout: 5000}); }
catch (error) { throw new Error(`Expected focus ${id}; got ${await page.evaluate(() => ({id: document.activeElement?.id, tag: document.activeElement?.tagName, dialogs: document.querySelectorAll('[role=dialog]').length})).then(JSON.stringify)}: ${error.message}`); }
}
try {
browser = await chromium.launch({headless: true,
...(process.env.FEP_CHROMIUM_PATH ? {executablePath: process.env.FEP_CHROMIUM_PATH} : {})});
await probe('X01-render-contained', '/', async page => {
await page.getByText('Break render', {exact: true}).click();
await page.getByTestId('inner').waitFor();
await page.getByText('Sibling 0', {exact: true}).click();
await page.getByText('Sibling 1', {exact: true}).waitFor();
const o = await page.evaluate(() => window.observed);
assert(o.caught.some(x => x.boundary === 'inner' && x.message === 'render fault'));
assert.equal(await page.getByTestId('outer').count(), 0);
return {observed: o, siblingUsable: true};
});
await probe('X02-event-not-contained', '/', async page => {
await page.getByText('Break event', {exact: true}).click();
await page.waitForFunction(() => window.observed.globalErrors.some(x => x.includes('event fault')));
const o = await page.evaluate(() => window.observed);
assert.equal(o.caught.length, 0); assert.equal(await page.getByTestId('panel').count(), 1);
return {observed: o, panelPresent: true};
});
await probe('X03-request-explicit-state', '/', async page => {
await page.getByText('Fail request', {exact: true}).click();
await page.getByRole('status').filter({hasText: 'request failed'}).waitFor();
const o = await page.evaluate(() => window.observed);
assert.deepEqual(o.requests, [503]); assert.equal(o.caught.length, 0);
return {observed: o, state: 'request failed'};
});
await probe('X04-fallback-escapes-inner', '/?brokenFallback=1', async page => {
await page.getByText('Break render', {exact: true}).click();
await page.getByTestId('outer').waitFor();
const o = await page.evaluate(() => window.observed);
assert(o.caught.some(x => x.boundary === 'outer' && x.message === 'fallback fault'));
return {observed: o, enclosingFallback: true};
});
await probe('X05-focus-existing-invoker', '/?mode=focus', async page => {
await page.locator('#invoker').focus(); await page.keyboard.press('Enter');
await active(page, 'remove'); await page.keyboard.press('Escape'); await active(page, 'invoker');
return {activeId: await page.evaluate(() => document.activeElement.id)};
});
await probe('X06-focus-removed-default', '/?mode=focus', async page => {
await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove');
await page.keyboard.press('Enter'); await page.getByRole('dialog').waitFor({state: 'detached'});
await page.waitForFunction(() => document.activeElement === document.body);
// Negative control: default restoration cannot choose a workflow target for the removed trigger.
assert.equal(await page.locator('#invoker').count(), 0);
return {activeTag: await page.evaluate(() => document.activeElement.tagName), workflowTargetSelected: false};
});
await probe('X07-focus-removed-explicit', '/?mode=focus&explicit=1', async page => {
await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove');
await page.keyboard.press('Enter'); await active(page, 'next');
return {activeId: await page.evaluate(() => document.activeElement.id), applicationPolicy: true};
});
await probe('X08-nested-return', '/?mode=focus', async page => {
await page.locator('#invoker').focus(); await page.keyboard.press('Enter'); await active(page, 'remove');
await page.keyboard.press('Tab'); await active(page, 'nested-invoker');
await page.keyboard.press('Enter'); await active(page, 'nested-close');
// Experimental timing variable, not a hidden retry: compare immediate versus settled Escape.
if (Number(process.env.FEP_NESTED_SETTLE_MS)) await page.waitForTimeout(Number(process.env.FEP_NESTED_SETTLE_MS));
await page.keyboard.press('Escape'); await active(page, 'nested-invoker');
assert.equal(await page.getByRole('dialog').count(), 1);
await page.keyboard.press('Escape'); await active(page, 'invoker');
return {innerReturn: 'nested-invoker', outerReturn: 'invoker', observed: await page.evaluate(() => window.observed)};
});
const files = {};
for (const name of ['fixture.jsx', 'experiment.mjs', 'package.json', 'package-lock.json']) {
files[name] = createHash('sha256').update(await readFile(dir + name)).digest('hex');
}
const result = {date: new Date().toISOString(), evidence: 'synthetic-browser-fixture-real-libraries',
node: process.version, browser: browser.version(), nestedSettleMs: Number(process.env.FEP_NESTED_SETTLE_MS || 0),
browserSelection: process.env.FEP_CHROMIUM_PATH ? 'explicit executable override' : 'playwright bundled',
packages: JSON.parse(await readFile(dir + 'package.json')).dependencies,
files, cases, passed: cases.filter(x => x.status === 'pass').length, total: cases.length};
const output = process.env.FEP_RESULT_PATH || '/tmp/fep-boundary-result.json';
await writeFile(output, JSON.stringify(result, null, 2) + '\n');
console.log(JSON.stringify({output, passed: result.passed, total: result.total, cases}, null, 2));
if (result.passed !== result.total) process.exitCode = 1;
} finally {
if (browser) await browser.close();
await new Promise(resolve => server.close(resolve));
}

View file

@ -0,0 +1,81 @@
import React, {useState, useRef} from 'react';
import {createRoot} from 'react-dom/client';
import * as Dialog from '@radix-ui/react-dialog';
window.observed = {caught: [], globalErrors: [], requests: [], dialogTransitions: []};
window.addEventListener('error', e => window.observed.globalErrors.push(e.message));
class Boundary extends React.Component {
state = {failed: false};
static getDerivedStateFromError() { return {failed: true}; }
componentDidCatch(error) { window.observed.caught.push({boundary: this.props.name, message: error.message}); }
render() {
if (this.state.failed) return this.props.brokenFallback
? <BrokenFallback /> : <p role="alert" data-testid={this.props.name}>Region unavailable</p>;
return this.props.children;
}
}
function BrokenFallback() { throw new Error('fallback fault'); }
function Panel() {
const [broken, setBroken] = useState(false);
const [request, setRequest] = useState('idle');
if (broken) throw new Error('render fault');
return <section data-testid="panel">
<button onClick={() => setBroken(true)}>Break render</button>
<button onClick={() => {throw new Error('event fault');}}>Break event</button>
<button onClick={async () => {
setRequest('pending');
try {
const r = await fetch('/failure');
window.observed.requests.push(r.status);
if (!r.ok) throw new Error('request rejected');
setRequest('accepted');
} catch {setRequest('request failed');}
}}>Fail request</button>
<p role="status">{request}</p>
</section>;
}
function FailureFixture() {
const [clicks, setClicks] = useState(0);
const brokenFallback = new URLSearchParams(location.search).has('brokenFallback');
return <main>
<button onClick={() => setClicks(clicks + 1)}>Sibling {clicks}</button>
<Boundary name="outer"><Boundary name="inner" brokenFallback={brokenFallback}>
<Panel />
</Boundary></Boundary>
</main>;
}
function Modal({children, title}) {
return <Dialog.Portal><Dialog.Overlay />
<Dialog.Content aria-describedby={undefined}>
<Dialog.Title>{title}</Dialog.Title>{children}
</Dialog.Content>
</Dialog.Portal>;
}
function FocusFixture() {
const [open, setOpen] = useState(false);
const [present, setPresent] = useState(true);
const next = useRef(null);
const explicit = new URLSearchParams(location.search).has('explicit');
return <main>
<button id="next" ref={next}>Next record</button>
<Dialog.Root open={open} onOpenChange={value => {window.observed.dialogTransitions.push({dialog: "outer", open: value}); setOpen(value);}}>
{present && <Dialog.Trigger id="invoker">Open task</Dialog.Trigger>}
<Dialog.Portal><Dialog.Overlay />
<Dialog.Content aria-describedby={undefined} onCloseAutoFocus={event => {
if (explicit && !present) {event.preventDefault(); next.current?.focus();}
}}>
<Dialog.Title>Record task</Dialog.Title>
<button id="remove" onClick={() => {setPresent(false); setOpen(false);}}>Remove invoking record</button>
<Dialog.Root onOpenChange={value => window.observed.dialogTransitions.push({dialog: "inner", open: value})}>
<Dialog.Trigger id="nested-invoker">Nested task</Dialog.Trigger>
<Modal title="Nested task"><Dialog.Close id="nested-close">Close nested</Dialog.Close></Modal>
</Dialog.Root>
<Dialog.Close id="close">Close task</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</main>;
}
createRoot(document.getElementById('root')).render(
new URLSearchParams(location.search).get('mode') === 'focus' ? <FocusFixture /> : <FailureFixture />
);

View file

@ -0,0 +1,989 @@
{
"name": "fep-boundary-recovery",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fep-boundary-recovery",
"dependencies": {
"@radix-ui/react-dialog": "1.1.23",
"esbuild": "0.28.2",
"playwright": "1.63.0",
"react": "19.3.0",
"react-dom": "19.3.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@radix-ui/primitive": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz",
"integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==",
"license": "MIT"
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
"integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-context": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz",
"integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dialog": {
"version": "1.1.23",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz",
"integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.5",
"@radix-ui/react-context": "1.2.2",
"@radix-ui/react-dismissable-layer": "1.1.19",
"@radix-ui/react-focus-guards": "1.1.6",
"@radix-ui/react-focus-scope": "1.1.16",
"@radix-ui/react-id": "1.1.4",
"@radix-ui/react-portal": "1.1.17",
"@radix-ui/react-presence": "1.1.10",
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-slot": "1.3.3",
"@radix-ui/react-use-controllable-state": "1.2.6",
"@radix-ui/react-use-layout-effect": "1.1.4",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.7.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.19",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz",
"integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.5",
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-use-callback-ref": "1.1.4",
"@radix-ui/react-use-effect-event": "0.0.5"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz",
"integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz",
"integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.5",
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-use-callback-ref": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-id": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz",
"integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": {
"version": "1.1.17",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz",
"integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz",
"integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-primitive": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz",
"integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.3.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
"integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.5"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz",
"integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz",
"integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.7",
"@radix-ui/react-use-effect-event": "0.0.5",
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-effect-event": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz",
"integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz",
"integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/aria-hidden": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.2",
"@esbuild/android-arm": "0.28.2",
"@esbuild/android-arm64": "0.28.2",
"@esbuild/android-x64": "0.28.2",
"@esbuild/darwin-arm64": "0.28.2",
"@esbuild/darwin-x64": "0.28.2",
"@esbuild/freebsd-arm64": "0.28.2",
"@esbuild/freebsd-x64": "0.28.2",
"@esbuild/linux-arm": "0.28.2",
"@esbuild/linux-arm64": "0.28.2",
"@esbuild/linux-ia32": "0.28.2",
"@esbuild/linux-loong64": "0.28.2",
"@esbuild/linux-mips64el": "0.28.2",
"@esbuild/linux-ppc64": "0.28.2",
"@esbuild/linux-riscv64": "0.28.2",
"@esbuild/linux-s390x": "0.28.2",
"@esbuild/linux-x64": "0.28.2",
"@esbuild/netbsd-arm64": "0.28.2",
"@esbuild/netbsd-x64": "0.28.2",
"@esbuild/openbsd-arm64": "0.28.2",
"@esbuild/openbsd-x64": "0.28.2",
"@esbuild/openharmony-arm64": "0.28.2",
"@esbuild/sunos-x64": "0.28.2",
"@esbuild/win32-arm64": "0.28.2",
"@esbuild/win32-ia32": "0.28.2",
"@esbuild/win32-x64": "0.28.2"
}
},
"node_modules/get-nonce": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/playwright": {
"version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
"integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.63.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/playwright-core": {
"version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
"integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/react": {
"version": "19.3.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
"integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.3.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
"integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.28.0"
},
"peerDependencies": {
"react": "^19.3.0"
}
},
"node_modules/react-remove-scroll": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
"integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
"react-style-singleton": "^2.2.3",
"tslib": "^2.1.0",
"use-callback-ref": "^1.3.3",
"use-sidecar": "^1.1.3"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-remove-scroll-bar": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
"license": "MIT",
"dependencies": {
"react-style-singleton": "^2.2.2",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
"license": "MIT",
"dependencies": {
"get-nonce": "^1.0.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/scheduler": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",
"integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==",
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/use-callback-ref": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/use-sidecar": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
"license": "MIT",
"dependencies": {
"detect-node-es": "^1.1.0",
"tslib": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
}
}
}

View file

@ -0,0 +1,13 @@
{
"name": "fep-boundary-recovery",
"private": true,
"type": "module",
"scripts": {"test": "node experiment.mjs"},
"dependencies": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
}
}

View file

@ -0,0 +1,57 @@
{
"date": "2026-09-26",
"source_id": "SRC-0029",
"packages": {
"node_modules/react": {
"version": "19.3.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
"integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="
},
"node_modules/react-dom": {
"version": "19.3.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz",
"integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="
},
"node_modules/@radix-ui/react-dialog": {
"version": "1.1.23",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz",
"integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA=="
},
"node_modules/@radix-ui/react-focus-scope": {
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz",
"integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ=="
},
"node_modules/playwright": {
"version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
"integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg=="
},
"node_modules/esbuild": {
"version": "0.28.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="
},
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.19",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz",
"integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w=="
}
},
"inspected_files": {
"@radix-ui/react-dialog/dist/index.mjs": {
"sha256": "a37daaef3f67c502500404ee93f60a04fdf05d2b0ce4783c858b1027c7f54792"
},
"@radix-ui/react-focus-scope/dist/index.mjs": {
"sha256": "798184fbe00268a6c4a539fd2073b85b6dfef6d89bd2cd547d9a66ae1024e55d"
},
"@radix-ui/react-dismissable-layer/dist/index.mjs": {
"sha256": "01df449a23d65bab1f2f2215a953cba74741419496ebd9a7932533f267cc873d"
}
},
"browser": {
"version": "151.0.7922.34",
"executable_sha256": "0b20b130e7edd9dd51873be867761295fe0cfad490c2b9a64f95bd3cfc08fa71",
"selection": "preinstalled Chromium build 1234, explicit executable override; not claimed to be Playwright 1.63 bundled revision"
}
}

View file

@ -0,0 +1,134 @@
{
"date": "2026-09-26T19:58:51.268Z",
"evidence": "synthetic-browser-fixture-real-libraries",
"node": "v24.11.1",
"browser": "151.0.7922.34",
"nestedSettleMs": 0,
"browserSelection": "explicit executable override",
"packages": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
},
"files": {
"fixture.jsx": "3dd538006affc9bcdb69c8b5cd1bb06ac335499f1143b91f06f1d455a127c525",
"experiment.mjs": "33ea08bcbd8fda533dc4bb44d5369b5453fd8e23940116597658435296d834fb",
"package.json": "a0ad37e8518b0c561d6b4a091ee51f5bca90e0ce329b2be7ba75347f6dc26ae7",
"package-lock.json": "ec4b544d441f89d9c512bc9931c1818473327105d5ef9047dc04d6a721e68463"
},
"cases": [
{
"id": "X01-render-contained",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "inner",
"message": "render fault"
}
],
"globalErrors": [],
"requests": [],
"dialogTransitions": []
},
"siblingUsable": true,
"browserErrors": []
},
{
"id": "X02-event-not-contained",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [
"Uncaught Error: event fault"
],
"requests": [],
"dialogTransitions": []
},
"panelPresent": true,
"browserErrors": [
"event fault"
]
},
{
"id": "X03-request-explicit-state",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [
503
],
"dialogTransitions": []
},
"state": "request failed",
"browserErrors": []
},
{
"id": "X04-fallback-escapes-inner",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "outer",
"message": "fallback fault"
}
],
"globalErrors": [],
"requests": [],
"dialogTransitions": []
},
"enclosingFallback": true,
"browserErrors": []
},
{
"id": "X05-focus-existing-invoker",
"status": "pass",
"activeId": "invoker",
"browserErrors": []
},
{
"id": "X06-focus-removed-default",
"status": "pass",
"activeTag": "BODY",
"workflowTargetSelected": false,
"browserErrors": []
},
{
"id": "X07-focus-removed-explicit",
"status": "pass",
"activeId": "next",
"applicationPolicy": true,
"browserErrors": []
},
{
"id": "X08-nested-return",
"status": "fail",
"error": "Expected focus nested-invoker; got {\"id\":\"invoker\",\"tag\":\"BUTTON\",\"dialogs\":0}: page.waitForFunction: Timeout 5000ms exceeded.",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [],
"dialogTransitions": [
{
"dialog": "outer",
"open": true
},
{
"dialog": "inner",
"open": true
},
{
"dialog": "outer",
"open": false
}
]
},
"browserErrors": []
}
],
"passed": 7,
"total": 8
}

View file

@ -0,0 +1,139 @@
{
"date": "2026-09-26T19:58:59.954Z",
"evidence": "synthetic-browser-fixture-real-libraries",
"node": "v24.11.1",
"browser": "151.0.7922.34",
"nestedSettleMs": 100,
"browserSelection": "explicit executable override",
"packages": {
"react": "19.3.0",
"react-dom": "19.3.0",
"@radix-ui/react-dialog": "1.1.23",
"playwright": "1.63.0",
"esbuild": "0.28.2"
},
"files": {
"fixture.jsx": "3dd538006affc9bcdb69c8b5cd1bb06ac335499f1143b91f06f1d455a127c525",
"experiment.mjs": "33ea08bcbd8fda533dc4bb44d5369b5453fd8e23940116597658435296d834fb",
"package.json": "a0ad37e8518b0c561d6b4a091ee51f5bca90e0ce329b2be7ba75347f6dc26ae7",
"package-lock.json": "ec4b544d441f89d9c512bc9931c1818473327105d5ef9047dc04d6a721e68463"
},
"cases": [
{
"id": "X01-render-contained",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "inner",
"message": "render fault"
}
],
"globalErrors": [],
"requests": [],
"dialogTransitions": []
},
"siblingUsable": true,
"browserErrors": []
},
{
"id": "X02-event-not-contained",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [
"Uncaught Error: event fault"
],
"requests": [],
"dialogTransitions": []
},
"panelPresent": true,
"browserErrors": [
"event fault"
]
},
{
"id": "X03-request-explicit-state",
"status": "pass",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [
503
],
"dialogTransitions": []
},
"state": "request failed",
"browserErrors": []
},
{
"id": "X04-fallback-escapes-inner",
"status": "pass",
"observed": {
"caught": [
{
"boundary": "outer",
"message": "fallback fault"
}
],
"globalErrors": [],
"requests": [],
"dialogTransitions": []
},
"enclosingFallback": true,
"browserErrors": []
},
{
"id": "X05-focus-existing-invoker",
"status": "pass",
"activeId": "invoker",
"browserErrors": []
},
{
"id": "X06-focus-removed-default",
"status": "pass",
"activeTag": "BODY",
"workflowTargetSelected": false,
"browserErrors": []
},
{
"id": "X07-focus-removed-explicit",
"status": "pass",
"activeId": "next",
"applicationPolicy": true,
"browserErrors": []
},
{
"id": "X08-nested-return",
"status": "pass",
"innerReturn": "nested-invoker",
"outerReturn": "invoker",
"observed": {
"caught": [],
"globalErrors": [],
"requests": [],
"dialogTransitions": [
{
"dialog": "outer",
"open": true
},
{
"dialog": "inner",
"open": true
},
{
"dialog": "inner",
"open": false
},
{
"dialog": "outer",
"open": false
}
]
},
"browserErrors": []
}
],
"passed": 8,
"total": 8
}

View file

@ -0,0 +1,60 @@
# Boundary recovery observations — 2026-09-26
Status: executed synthetic browser fixture, editorial interpretation by Codex.
WP-0003 T01; SRC-0029 pinned packages and SRC-0030 Radix documentation. This advances
implementation evidence for Failure Boundary and Focus Return, not full-pattern
recurrence, application validation or the M1/M2 gates. Sixteen hypotheses remain.
[Runner, fixture and raw results](../experiments/boundary-recovery/README.md) use
React 19.3.0, Radix Dialog 1.1.23 and Chromium 151.0.7922.34. Package archives,
inspected implementation hashes and browser provenance are recorded. Sources of
injected faults and application focus policy are our own fixture, not vendor examples.
No live model calls; future inference remains through llm-connect.
## Observations and implications
| Evidence | Executed observation | Bounded interpretation |
| --- | --- | --- |
| X01 | Inner boundary catches render fault; sibling button remains usable | Local rendering containment works for this subtree and fault |
| X02 | Event-handler exception is globally reported; no boundary catch | Do not promise catch-all UI recovery from a rendering boundary |
| X03 | GET /failure returns 503; explicit application catch displays failure state | Request state is separate; this does not establish behavior of unhandled async errors |
| X04 | Broken inner fallback reaches outer fallback | Recovery UI can fail and needs an enclosing policy |
| X05 | Escape returns focus to surviving trigger | A partial implementation of ordinary modal return |
| X06 | Removing the trigger and closing leaves BODY focused | A generic trigger reference cannot choose the next workflow target |
| X07 | Application onCloseAutoFocus override selects next record | A narrow application policy repairs this removed-trigger case |
| X08 | Immediate Escape after nested activation closed outer dialog in the recorded final immediate run; 100 ms pause preserved inner-then-outer return | Activation/focus arrival alone is insufficient evidence of stable dismissal ownership; timing cause remains unproven |
## Source inspection and uncertainty
[Radix Dialog docs](https://www.radix-ui.com/primitives/docs/components/dialog)
expose close-autofocus customization and describe Escape dismissal. Inspected package
code `@radix-ui/react-dialog/dist/index.mjs` lines 154–156 cancels the default unmount
focus action and attempts focus on the trigger reference. Our explicit target in X07
is an application addition, not a built-in workflow-selection guarantee.
In the pinned `@radix-ui/react-focus-scope/dist/index.mjs`, lines 94–103 defer unmount
focus handling. The pinned dismissable-layer module, lines 90–120, selects the highest
layer and registers Escape listeners/layers through effects. These are plausible
places to investigate the timing discrepancy, not a demonstrated causal diagnosis.
See [file hashes and transitive versions](../experiments/boundary-recovery/provenance.json).
The failing X08 transition log is outer-open, inner-open, outer-close; it does not
show inner-close before outer-close. The final settled run shows the intended order.
Three earlier development runs also alternated fail/pass/fail. Do not describe this
as an unconditional nested-focus guarantee, estimate a rate from five exploratory
runs, or prescribe a 100 ms sleep as a fix. Current result files record identical
fixture/runner/lock hashes, with the timing parameter explicitly different.
## Candidate revisions and remaining work
[Failure Boundary](../candidates/FailureBoundary.md) gains executed partial support
and concrete exclusions. [Focus Return](../candidates/FocusReturn.md) gains a negative
control, a conditional recovery policy and an unresolved nested-lifecycle case.
Both stay hypotheses. One synthetic fixture is not an independent application use.
Next under [WP-0003 T01](../../workplans/FEP-WP-0003-contrasting-schools.md): isolate
nested dismissal with event/effect timing instrumentation and compare production
build plus another browser before assigning cause. Preserve the immediate path as
a regression probe; test explicit readiness/ownership rather than adding a delay.
Screen-reader behavior remains untested. Feature/release coupling inspection and
navigation/history/cache checks remain separate pending work.

View file

@ -16,3 +16,7 @@ were added in the breadth review. Implementation evidence for B/C remains pendin
All six streams now have some documentary material; none is thereby complete.
VC-01/02 are synthetic reviewed drafts; VC-03 is provisional; VC-04–10 remain planned.
No source count is used as a recurrence score or evidence-gate substitute.
Boundary follow-up: [React/Radix browser observations](BoundaryRecoveryExperiment.md)
add bounded executable evidence to F. Nested dismissal remains timing-sensitive;
no screen-reader verification or full quality-stream completion.

View file

@ -81,3 +81,19 @@ pin an application and inspect feature imports versus release coupling; exercise
render versus request failures and modal focus after invoker removal. Previous
navigation/history/cache checks remain open. WP-0002 retains independent-human
validation. No frozen-pilot edits, canonical promotion or evidence-gate completion.
## Executed rendering/focus boundary slice — 2026-09-26
T01 progress: research/experiments/boundary-recovery pins React 19.3.0, Radix Dialog
1.1.23 and all dependencies; Chromium fixture exercises eight cases. Final immediate
run meets 7/8 assertions; separate 100 ms nested-settling condition meets 8/8. Three
earlier development results retained, including failures. Ordinary render containment,
event-handler exclusion, explicit 503 state, fallback escalation, existing/removed
trigger focus and explicit replacement target observed. Immediate nested Escape can
close the outer task; root cause unresolved. No delay prescribed as a fix.
Next T01 action: isolate nested dismissal timing, compare production and another
browser, verify readiness/ownership without arbitrary delay. Do not mark its assertion
resolved from the settled run. Feature/release inspection and navigation checks remain
pending. T02/T03 unchanged, sixteen hypotheses, no independent human validation or
M1/M2 completion. See research/reviews/BoundaryRecoveryExperiment.md; no model calls.