diff --git a/TREE.md b/TREE.md
index 19bc9a7..ffdfd6b 100644
--- a/TREE.md
+++ b/TREE.md
@@ -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
diff --git a/research/README.md b/research/README.md
index 17137f0..528dc88 100644
--- a/research/README.md
+++ b/research/README.md
@@ -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.
diff --git a/research/candidates/FailureBoundary.md b/research/candidates/FailureBoundary.md
index 0ebf905..d818975 100644
--- a/research/candidates/FailureBoundary.md
+++ b/research/candidates/FailureBoundary.md
@@ -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.
diff --git a/research/candidates/FocusReturn.md b/research/candidates/FocusReturn.md
index e65b6f4..a53c180 100644
--- a/research/candidates/FocusReturn.md
+++ b/research/candidates/FocusReturn.md
@@ -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.
diff --git a/research/catalog/PatternCandidateCatalog.md b/research/catalog/PatternCandidateCatalog.md
index 0681bd4..9689758 100644
--- a/research/catalog/PatternCandidateCatalog.md
+++ b/research/catalog/PatternCandidateCatalog.md
@@ -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.
diff --git a/research/catalog/SourceCatalog.md b/research/catalog/SourceCatalog.md
index b2852c8..36b30c5 100644
--- a/research/catalog/SourceCatalog.md
+++ b/research/catalog/SourceCatalog.md
@@ -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).
diff --git a/research/experiments/boundary-recovery/README.md b/research/experiments/boundary-recovery/README.md
new file mode 100644
index 0000000..6df069e
--- /dev/null
+++ b/research/experiments/boundary-recovery/README.md
@@ -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.
diff --git a/research/experiments/boundary-recovery/development-results.json b/research/experiments/boundary-recovery/development-results.json
new file mode 100644
index 0000000..46c4da0
--- /dev/null
+++ b/research/experiments/boundary-recovery/development-results.json
@@ -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
+ }
+]
diff --git a/research/experiments/boundary-recovery/experiment.mjs b/research/experiments/boundary-recovery/experiment.mjs
new file mode 100644
index 0000000..ad920cd
--- /dev/null
+++ b/research/experiments/boundary-recovery/experiment.mjs
@@ -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('
Boundary probe');
+ }
+});
+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));
+}
diff --git a/research/experiments/boundary-recovery/fixture.jsx b/research/experiments/boundary-recovery/fixture.jsx
new file mode 100644
index 0000000..fcf3091
--- /dev/null
+++ b/research/experiments/boundary-recovery/fixture.jsx
@@ -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
+ ? : Region unavailable
;
+ 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
+
+
+
+ {request}
+ ;
+}
+function FailureFixture() {
+ const [clicks, setClicks] = useState(0);
+ const brokenFallback = new URLSearchParams(location.search).has('brokenFallback');
+ return
+
+
+
+
+ ;
+}
+function Modal({children, title}) {
+ return
+
+ {title}{children}
+
+ ;
+}
+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
+
+ {window.observed.dialogTransitions.push({dialog: "outer", open: value}); setOpen(value);}}>
+ {present && Open task}
+
+ {
+ if (explicit && !present) {event.preventDefault(); next.current?.focus();}
+ }}>
+ Record task
+
+ window.observed.dialogTransitions.push({dialog: "inner", open: value})}>
+ Nested task
+ Close nested
+
+ Close task
+
+
+
+ ;
+}
+createRoot(document.getElementById('root')).render(
+ new URLSearchParams(location.search).get('mode') === 'focus' ? :
+);
diff --git a/research/experiments/boundary-recovery/package-lock.json b/research/experiments/boundary-recovery/package-lock.json
new file mode 100644
index 0000000..187c03b
--- /dev/null
+++ b/research/experiments/boundary-recovery/package-lock.json
@@ -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
+ }
+ }
+ }
+ }
+}
diff --git a/research/experiments/boundary-recovery/package.json b/research/experiments/boundary-recovery/package.json
new file mode 100644
index 0000000..a92aa69
--- /dev/null
+++ b/research/experiments/boundary-recovery/package.json
@@ -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"
+ }
+}
diff --git a/research/experiments/boundary-recovery/provenance.json b/research/experiments/boundary-recovery/provenance.json
new file mode 100644
index 0000000..d695934
--- /dev/null
+++ b/research/experiments/boundary-recovery/provenance.json
@@ -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"
+ }
+}
diff --git a/research/experiments/boundary-recovery/result-immediate.json b/research/experiments/boundary-recovery/result-immediate.json
new file mode 100644
index 0000000..3f467f6
--- /dev/null
+++ b/research/experiments/boundary-recovery/result-immediate.json
@@ -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
+}
diff --git a/research/experiments/boundary-recovery/result-settled.json b/research/experiments/boundary-recovery/result-settled.json
new file mode 100644
index 0000000..5d25346
--- /dev/null
+++ b/research/experiments/boundary-recovery/result-settled.json
@@ -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
+}
diff --git a/research/reviews/BoundaryRecoveryExperiment.md b/research/reviews/BoundaryRecoveryExperiment.md
new file mode 100644
index 0000000..cc72055
--- /dev/null
+++ b/research/reviews/BoundaryRecoveryExperiment.md
@@ -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.
diff --git a/research/reviews/NavigationCoverage.md b/research/reviews/NavigationCoverage.md
index 70d7344..51efbfe 100644
--- a/research/reviews/NavigationCoverage.md
+++ b/research/reviews/NavigationCoverage.md
@@ -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.
diff --git a/workplans/FEP-WP-0003-contrasting-schools.md b/workplans/FEP-WP-0003-contrasting-schools.md
index 09c8f9d..1d9c210 100644
--- a/workplans/FEP-WP-0003-contrasting-schools.md
+++ b/workplans/FEP-WP-0003-contrasting-schools.md
@@ -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.