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:
parent
50c68575f1
commit
3eecd74b0d
18 changed files with 2067 additions and 0 deletions
10
TREE.md
10
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
63
research/experiments/boundary-recovery/README.md
Normal file
63
research/experiments/boundary-recovery/README.md
Normal 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.
|
||||
333
research/experiments/boundary-recovery/development-results.json
Normal file
333
research/experiments/boundary-recovery/development-results.json
Normal 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
|
||||
}
|
||||
]
|
||||
124
research/experiments/boundary-recovery/experiment.mjs
Normal file
124
research/experiments/boundary-recovery/experiment.mjs
Normal 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));
|
||||
}
|
||||
81
research/experiments/boundary-recovery/fixture.jsx
Normal file
81
research/experiments/boundary-recovery/fixture.jsx
Normal 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 />
|
||||
);
|
||||
989
research/experiments/boundary-recovery/package-lock.json
generated
Normal file
989
research/experiments/boundary-recovery/package-lock.json
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
research/experiments/boundary-recovery/package.json
Normal file
13
research/experiments/boundary-recovery/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
57
research/experiments/boundary-recovery/provenance.json
Normal file
57
research/experiments/boundary-recovery/provenance.json
Normal 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"
|
||||
}
|
||||
}
|
||||
134
research/experiments/boundary-recovery/result-immediate.json
Normal file
134
research/experiments/boundary-recovery/result-immediate.json
Normal 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
|
||||
}
|
||||
139
research/experiments/boundary-recovery/result-settled.json
Normal file
139
research/experiments/boundary-recovery/result-settled.json
Normal 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
|
||||
}
|
||||
60
research/reviews/BoundaryRecoveryExperiment.md
Normal file
60
research/reviews/BoundaryRecoveryExperiment.md
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue