Skip to content

Commit f7a0bf3

Browse files
feat: React holdout migrates GREEN — missing-export tolerance closes gap 2
Generic capability on the reopened CRA adapter: a dangling named import in a DEPENDENCY ES module resolves to undefined (webpack 4 semantics), analyzer-proven absent (follows re-export chains), dependency-scoped (app-source dangling imports refused), and only where provably safe under undefined (value reads at module-eval scope refused so real errors still surface). One genuinely-dangling import across the 28 react-virtualized marker files (onScroll.js:74, dead); the other 27 untouched. The unseen cypress-realworld-app now migrates GREEN: 10182 modules, 18 files emitted, byte-identical x2 (7051b848). 11 tests; 2145/2145. Follow-ups: witness journeys, re-freeze, PASSING receipt.
1 parent ddfdfa2 commit f7a0bf3

5 files changed

Lines changed: 789 additions & 6 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{
2+
"schemaVersion": "versionless.react-cypress-rwa-green-build-profile.v1",
3+
"role": "holdout",
4+
"fixture": "react-cypress-rwa",
5+
"unit": "lrapr-t017b/g1-missing-export-tolerance",
6+
"result": "migrated-green",
7+
"advances": {
8+
"unit": "lrapr-t017/h1-cypress-rwa-holdout-rerun",
9+
"priorResult": "migrated-red",
10+
"priorGap": "missing-export tolerance for a self-inconsistent dependency ES module",
11+
"priorDemand": {
12+
"code": "MISSING_EXPORT",
13+
"module": "node_modules/react-virtualized/dist/es/WindowScroller/WindowScroller.js",
14+
"importer": "node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js",
15+
"line": 74,
16+
"column": 10,
17+
"symbol": "bpfrpt_proptype_WindowScroller"
18+
},
19+
"note": "This is an authorized adapter-reopen hardening loop (owner-directed 2026-08-13). The 5de7df56 adapter freeze was lifted for this unit; the fingerprint fence is not enforced. Prior RED records stay immutable and are advanced by reference."
20+
},
21+
"adapterReopen": {
22+
"authorized": true,
23+
"authority": "owner-directed 2026-08-13",
24+
"subtreeChanged": "packages/frameworks/react",
25+
"capabilityAdded": "missing-export tolerance for a self-inconsistent dependency ES module",
26+
"fingerprintFenceEnforced": false
27+
},
28+
"source": {
29+
"repository": "https://github.com/cypress-io/cypress-realworld-app",
30+
"ref": "refs/tags/v1.0.18",
31+
"revision": "f6b5cf3a1799998dab71181eeed59460f8ada5f4",
32+
"archiveSha256": "bd9319272dabc1e7263a0186e0aaae011f6219e6e106363d2c89426f9357b315",
33+
"frontendRoot": ".",
34+
"license": "MIT",
35+
"react": "17.0.2",
36+
"reactScripts": "4.0.3",
37+
"webpack": "4.44.2",
38+
"typescript": "4.3.4"
39+
},
40+
"migratedLane": {
41+
"result": "green",
42+
"bundler": "rolldown 1.0.3 under vite 8.0.16",
43+
"adapter": "createCraViteAdapter applied through fixtures/react-cypress-rwa/vite.config.ts, nothing holdout-specific in the config",
44+
"attempts": 2,
45+
"deterministic": true,
46+
"modulesTransformed": 10182,
47+
"transformPhase": "complete",
48+
"renderChunks": "complete",
49+
"emit": "complete",
50+
"outputDigest": {
51+
"scheme": "sha256(sha256(sorted(relpath sha256)) over build-vite)",
52+
"value": "7051b8489abcaea1bd18ae99a082acf0c8d88596f1dfd82d6b4bac222e37dc18",
53+
"stableAcrossAttempts": true
54+
},
55+
"outputFiles": 18,
56+
"outputInventory": [
57+
"assets/core-js-DSYDOQMP.js",
58+
"assets/core-js-DSYDOQMP.js.map",
59+
"assets/css-shim-3b0ed064-DLqqkXmS.js",
60+
"assets/css-shim-3b0ed064-DLqqkXmS.js.map",
61+
"assets/dom-CC_hZazZ.js",
62+
"assets/dom-CC_hZazZ.js.map",
63+
"assets/index-DMUS1oIR.js",
64+
"assets/index-DMUS1oIR.js.map",
65+
"assets/index-DQFHSTHs.css",
66+
"assets/shadow-css-360bb6da-CLjHZAjT.js",
67+
"assets/shadow-css-360bb6da-CLjHZAjT.js.map",
68+
"favicon.ico",
69+
"img/rwa-readme-screenshot.png",
70+
"index.html",
71+
"logo192.png",
72+
"logo512.png",
73+
"manifest.json",
74+
"robots.txt"
75+
],
76+
"entryChunkBytes": 4503844
77+
},
78+
"observedButNotFatal": [
79+
{
80+
"code": "INVALID_ANNOTATION",
81+
"module": "node_modules/@aws-amplify/ui-components/dist/esm-es5/index-83f2275b.js",
82+
"line": 1778,
83+
"detail": "A '/*@__PURE__*/' comment sits in a position rolldown cannot interpret; the annotation is ignored, not an error.",
84+
"severity": "warning"
85+
},
86+
{
87+
"code": "EVAL",
88+
"module": "node_modules/vm-browserify/index.js",
89+
"line": 110,
90+
"detail": "Direct eval() in the vm shim; a warning, unchanged from what webpack shipped.",
91+
"severity": "warning"
92+
},
93+
{
94+
"code": "chunk-size",
95+
"detail": "The single application chunk exceeds 500 kB after minification; a size advisory, not a failure.",
96+
"severity": "warning"
97+
}
98+
],
99+
"applicationFilesChanged": {
100+
"count": 0,
101+
"handEditedSourceFiles": []
102+
},
103+
"parity": {
104+
"comparable": false,
105+
"nonClaims": [
106+
"Build-level parity across both lanes is not claimed here; this record establishes that the migrated lane builds green, transform through emit.",
107+
"No runtime, boot, or behavioural parity is claimed. Witness journeys are a follow-up unit.",
108+
"No browser evidence exists for either lane in this record.",
109+
"Nothing is claimed about the four external auth provider modes."
110+
]
111+
}
112+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"schemaVersion": "versionless.react-cypress-rwa-missing-export-tolerance.v1",
3+
"unit": "lrapr-t017b/g1-missing-export-tolerance",
4+
"capability": "missing-export tolerance for a self-inconsistent dependency ES module",
5+
"question": "What did webpack 4 do with a dependency ES module that imports a named binding a sibling module never exports, and how is that reproduced generically without suppressing real errors?",
6+
"webpackSemantics": "webpack 4's ESM linker resolved an import of an export the target module does not provide to undefined rather than refusing the build; on the create-react-app 4 line it warned at most. The baseline lane (webpack 4.44.2) built green with the dangling binding present as undefined.",
7+
"rolldownSemantics": "Rolldown (the bundler Vite 8 builds with) implements the specification's binding resolution strictly: a named import of an absent export is a hard MISSING_EXPORT at the chunk-rendering stage, and the build stops.",
8+
"scopeOfTheShape": {
9+
"package": "react-virtualized@9.22.3",
10+
"build": "dist/es",
11+
"generator": "babel-plugin-flow-react-proptypes",
12+
"pattern": "The generator emits `import { bpfrpt_proptype_<Type> } from \"<sibling>\"` for flow proptype markers. Where the sibling module carries no runtime marker for that type, the import dangles.",
13+
"filesCarryingABpfrptImport": 28,
14+
"genuinelyDanglingImports": 1,
15+
"analyzerProof": "Across all 28 files that import a bpfrpt_proptype_* marker, each named relative import was resolved to its target and the target's actual export surface read (following named re-export chains). Exactly one import is genuinely absent from its target; the other 27 resolve through their types.js definitions or re-export chains and are left untouched."
16+
},
17+
"theOneDanglingImport": {
18+
"importer": "node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js",
19+
"line": 74,
20+
"construct": "import { bpfrpt_proptype_WindowScroller } from \"../WindowScroller.js\";",
21+
"symbol": "bpfrpt_proptype_WindowScroller",
22+
"target": "node_modules/react-virtualized/dist/es/WindowScroller/WindowScroller.js",
23+
"targetExports": ["IS_SCROLLING_TIMEOUT", "default"],
24+
"targetHasSymbol": false,
25+
"localReferenceCount": 0,
26+
"safeUnderUndefined": "yes — a dead import (zero references), so module evaluation never reads the binding",
27+
"reachedFrom": "src/components/TransactionInfiniteList.tsx (production application code imports react-virtualized)"
28+
},
29+
"rewrite": {
30+
"form": "The declaration is rewritten to keep the target module in the graph as a side-effect import and to bind each neutralized name to undefined.",
31+
"before": "import { bpfrpt_proptype_WindowScroller } from \"../WindowScroller.js\";",
32+
"after": "import \"../WindowScroller.js\"; const bpfrpt_proptype_WindowScroller = void 0;",
33+
"moduleEvaluationOrderPreserved": true
34+
},
35+
"genericityGuards": {
36+
"dependencyModulesOnly": "Only modules under a dependency directory are considered; an application-source dangling import is a real first-party defect and is left for the bundler to report.",
37+
"analyzerProvenAbsence": "The target module is resolved and its export surface read; a name the target does export (directly, by named re-export, or behind an unresolved export *) is left exactly as written. An unresolvable or bare-package target is never rewritten.",
38+
"provablySafeUnderUndefined": "The binding is neutralized only when every reference to it is one the module's own evaluation does not dereference: a use inside a function body (deferred), an export/import alias that forwards the binding by name, a type-only position, or no use at all. A value read at module-evaluation scope is refused, so a genuine missing-export error still surfaces.",
39+
"noApplicationBranch": "No capability path branches on the application's name, revision, or exact source; nothing in the capability names react-virtualized or the bpfrpt marker."
40+
},
41+
"verification": {
42+
"positive": "A dependency ES module whose dangling proptype import is dead, or read only inside a function body, is rewritten to undefined and the build links.",
43+
"negativeAppSource": "An application-source dangling import is refused (the plugin does not act outside node_modules).",
44+
"negativeValuePosition": "A dangling import dereferenced in a value position at module-evaluation scope is refused and left for the bundler to error on.",
45+
"negativePresentExport": "A present export is left byte-identical.",
46+
"buildProbe": "Under a minimal fixture, an unadapted build rejects with the MISSING_EXPORT rolldown cannot link; the adapted build links and the neutralized binding is undefined at runtime."
47+
},
48+
"result": "The migrated holdout build advances from the RED at onScroll.js:74 to GREEN: 10182 modules transformed, chunks rendered, 18 files emitted, deterministic across two builds (output digest 7051b8489abcaea1bd18ae99a082acf0c8d88596f1dfd82d6b4bac222e37dc18). No strictly-later gap remains at build time."
49+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# cypress-realworld-app — the holdout migrates green under the hardened adapter
2+
3+
**Unit** `lrapr-t017b/g1-missing-export-tolerance` · **Role** holdout · **Result** migrated lane **green**, transform through emit
4+
5+
This closes the strictly-later gap T017 named to the byte. T017 re-ran the holdout against the
6+
re-frozen adapter and stopped RED at one demand: a dangling named import in a self-inconsistent
7+
dependency ES module. This unit is an **authorized adapter reopen** (owner-directed 2026-08-13):
8+
the `5de7df56…` freeze is lifted for this hardening loop, the fingerprint fence is not enforced,
9+
and one generic capability is added to `packages/frameworks/react`. The prior RED records stay
10+
immutable and are advanced by reference.
11+
12+
## The gap that was open
13+
14+
T017 reached the rendering-chunks stage and failed with:
15+
16+
```
17+
[MISSING_EXPORT] "bpfrpt_proptype_WindowScroller" is not exported by
18+
"node_modules/react-virtualized/dist/es/WindowScroller/WindowScroller.js".
19+
╭─[ node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js:74:10 ]
20+
74 │ import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";
21+
```
22+
23+
`react-virtualized` 9.22.3's `dist/es` build is produced with `babel-plugin-flow-react-proptypes`,
24+
which emits `import { bpfrpt_proptype_<Type> }` statements for flow proptype markers. Where the
25+
sibling module carries no runtime marker, the import dangles. webpack 4 resolved a dangling named
26+
import to `undefined`, so the baseline built; rolldown treats it as a hard error.
27+
28+
## The capability added
29+
30+
**Missing-export tolerance for a self-inconsistent dependency ES module.** When a dependency
31+
(node_modules) ES module imports a named binding the target module provably does not export, the
32+
binding is resolved to `undefined` — exactly webpack 4's outcome — under three guards, each
33+
enforced rather than asserted:
34+
35+
- **Dependency modules only.** An application-source dangling import is a real first-party defect
36+
and is left for the bundler to report.
37+
- **Analyzer-proven absence.** The target is resolved and its actual export surface read; a name
38+
the target does export (directly, by named re-export, or behind an unresolved `export *`) is
39+
left exactly as written, and an unresolvable or bare-package target is never rewritten.
40+
- **Provably safe under `undefined`.** The binding is neutralized only when every reference to it
41+
is one the module's own evaluation does not dereference — a use inside a function body, an
42+
`export`/`import` alias that forwards the binding by name, a type-only position, or no use at
43+
all. A value read at module-evaluation scope is refused, so a genuine missing-export error still
44+
surfaces.
45+
46+
The rewrite keeps the target module in the graph as a side-effect import so evaluation order is
47+
unchanged, and binds each neutralized name to `undefined`:
48+
49+
```
50+
import "../WindowScroller.js"; const bpfrpt_proptype_WindowScroller = void 0;
51+
```
52+
53+
The capability names no application, package, or marker. Nothing branches on the holdout's
54+
identity, revision, or source.
55+
56+
## How narrow the shape actually is
57+
58+
An analyzer sweep across all **28** files in that ES build that import a `bpfrpt_proptype_*` marker
59+
resolved each named relative import to its target and read the target's real export surface,
60+
following re-export chains. Exactly **one** import is genuinely absent from its target —
61+
`onScroll.js:74`, a **dead** import with zero references. The other 27 resolve through their
62+
`types.js` definitions or re-export chains and are left untouched. The one dangling case is dead,
63+
so it is safe under `undefined` by construction: module evaluation never reads it.
64+
65+
## Result — migrated lane green
66+
67+
The frozen-plus-capability `createCraViteAdapter` composition, applied through
68+
`fixtures/react-cypress-rwa/vite.config.ts` on Vite 8.0.16 / rolldown 1.0.3 with nothing
69+
holdout-specific in the config, now builds through to emit:
70+
71+
```
72+
✓ 10182 modules transformed.
73+
rendering chunks...
74+
✓ built in 2.65s
75+
```
76+
77+
Built **twice**, byte-identical: output digest
78+
`7051b8489abcaea1bd18ae99a082acf0c8d88596f1dfd82d6b4bac222e37dc18` over 18 emitted files
79+
(the 4.5 MB application chunk and its sourcemap, the split css/dom/core-js chunks, the CSS bundle,
80+
the generated `index.html`, and the replicated public assets). A measurement, not a flake.
81+
82+
Three advisories print and none is fatal: an `INVALID_ANNOTATION` for a `/*@__PURE__*/` comment
83+
position in `@aws-amplify/ui-components`, an `EVAL` advisory for `vm-browserify`'s direct `eval`,
84+
and a chunk-size advisory for the single large application chunk.
85+
86+
## Discipline
87+
88+
Zero application source files were hand-edited. `src/aws-exports.js` and `aws-exports-es5.js` are
89+
copied by the application's own `predev:cognito:ci`; `index.html` is generated by the migration's
90+
`craEntryDocument` from the immutable `public/index.html`. The tolerance added is exactly
91+
webpack-4-dangling-DEPENDENCY-ESM-import→undefined — not a blanket missing-export suppression:
92+
application-source and value-position-used cases still fail. Network was used for nothing; the
93+
dependency closure was resident from the ingest under consent `VL-LEGACY-CORPUS-2026-08-10`, and
94+
every build ran offline.
95+
96+
## Gate
97+
98+
`pnpm exec tsc --noEmit` clean · `pnpm exec vp lint` 0 errors · `pnpm exec vp test --project node`
99+
2145/2145 (11 new tests for the capability: positive dead-import and deferred-use rewrites, the
100+
mixed-import split, and the app-source / value-position / present-export / unresolved-star / bare
101+
refusals, plus a build probe proving rolldown links the adapted build and refuses the unadapted
102+
one).
103+
104+
## What is not claimed, and what comes next
105+
106+
Build-level parity across both lanes is not claimed here — this record establishes that the
107+
migrated lane builds green, transform through emit. No runtime, boot, or behavioural parity is
108+
claimed, and no browser evidence exists for either lane. Witness journeys, an adapter re-freeze at
109+
a fresh boundary, and a PASSING holdout receipt (superseding the RED record by reference) are the
110+
follow-up units.

0 commit comments

Comments
 (0)