[PR #4922] [CLOSED] fix(javascript): recover Electron identity through esbuild require shims #6676

Closed
opened 2026-11-01 21:42:14 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4922
Author: @Y0UW1SH
Created: 10/17/2026
Status: ❌ Closed

Base: main ← Head: fix/esbuild-require-shim-3535


📝 Commits (10+)

  • a6bfda7 fix(javascript): recover Electron identity through esbuild require shims
  • 5b0b609 fix(javascript): prove shim initialization and retain prototype write uncertainty
  • a33074d fix(javascript): preserve require shim binding identity
  • 5fe55f2 chore: merge main and resolve shim analysis conflict
  • e160205 fix(javascript): preserve shim aliases and eval uncertainty
  • 1783e4b test: fix verifier prerequisites and asynchronous cleanup fixtures
  • 33f6f75 chore: reconcile upstream CI fixture fixes
  • e6beb7c fix(javascript): retain module identity through projected aliases
  • 6ddf321 fix(javascript): preserve alias and export capture integrity
  • 0428390 fix(javascript): preserve ordered alias effects

📊 Changes

107 files changed (+30473 additions, -1127 deletions)

View changed files

📝 docs/testing.md (+25 -0)
➕ src/domain/javascript/accessorAssignmentTargetReceivers.test.ts (+72 -0)
➕ src/domain/javascript/allocatedReceiverIdentity.test.ts (+288 -0)
➕ src/domain/javascript/arrayElementCaptureIntegrity.test.ts (+116 -0)
➕ src/domain/javascript/boundClassHelperCapture.test.ts (+180 -0)
➕ src/domain/javascript/callableClassValues.test.ts (+162 -0)
➕ src/domain/javascript/callableDefaultTargets.test.ts (+65 -0)
➕ src/domain/javascript/callableReferenceDefaults.test.ts (+142 -0)
➕ src/domain/javascript/capturedDefaultSlotIntegrity.test.ts (+197 -0)
➕ src/domain/javascript/constructedReceiverIdentity.test.ts (+846 -0)
➕ src/domain/javascript/constructorParentMutationIdentity.test.ts (+120 -0)
➕ src/domain/javascript/descriptorReturnedValueIdentity.test.ts (+112 -0)
➕ src/domain/javascript/destructuringDefinedSlots.test.ts (+175 -0)
➕ src/domain/javascript/destructuringReferenceOrder.test.ts (+366 -0)
📝 src/domain/javascript/electronModuleInterop.test.ts (+161 -3)
➕ src/domain/javascript/electronSavedExports.test.ts (+168 -0)
📝 src/domain/javascript/electronWrappedCallPaths.ts (+4 -2)
➕ src/domain/javascript/inheritedDefaultDataSlots.test.ts (+88 -0)
➕ src/domain/javascript/inheritedGetterEscapeIdentity.test.ts (+68 -0)
📝 src/domain/javascript/javascriptAstValues.ts (+103 -6)

...and 80 more files

📄 Description

Summary

Fixes #3535. Recover BrowserWindow, webPreferences and IPC findings through esbuild's guarded require helper while keeping analyzed application code inert. Builds on DeryFerd's closed, unmerged #3596 and preserves the original co-author credit.

Published commit: d195ebb4. Tested Git tree: 6ef791833f5785d23da6444b7d83ce31bf238200. Parent: 3deab802.

The full PR retains the shared loader, callable, storage, prototype and capture owners, including target-version uncertainty, native result provenance, synchronous invocation bounds and independent CommonJS/global bindings.

Latest corrections (2026-10-19 follow-up)

  • Merged upstream main (5199c672) and resolved the conflict with the
    upstream projection-widening rewrite (e4c3d723, fixes #4348/#5138). The
    shared callable-reference owner now exposes upstream's precision
    contract and feeds returnedReferenceWidenings, so the widening limitation
    and coverage fields are reported through the same channel as main.
  • Replaced initializer-history expansion with a bounded fixed point. The
    branch still carried per-path initializer histories (cycle blockers and
    memoized contexts) that upstream removed for exhausting the heap on React
    bundles. On this branch the same reassignment webs grew exponentially:
    8 swap writes took 830 ms and 12 timed out at 15 s; a 24-arm React-shaped
    switch mesh timed out. A source reached again under another projection now
    joins projections (differing keys widen to null, differing depths to any
    depth), pending call-result selections are bounded, and widening never
    drops represented targets while refusing exact captures. Every probed size
    now completes in about 2 ms with identical results. Upstream's projection,
    bounding and coverage tests pass; the full JavaScript domain suite got
    faster (224 s to 157 s, local).
  • Spread arguments stay opaque. Spreading even a literal array runs the
    array iterator, which earlier source can replace through
    Array.prototype[Symbol.iterator] or the array iterator prototype
    (Codex, 213d4c0c review). The expansion helper is removed; apply keeps
    index-read literal arrays.
  • Unknown recipients of known mutators are writes. Reflect.set(...args),
    Reflect.apply(Reflect.set, Reflect, args), Object.assign.apply(null, arguments) and the spread forms above were classified as harmless by both
    the loader-delegate owner and the ambient owner (no target, no write). They
    now record an unknown-identity write once the enclosing code can run; a
    function declaration that is never referenced is excluded, so uncalled
    generic helpers keep their findings.
  • Exact self-copies keep the shim (Codex, 3deab802 review): Q = Q
    cannot introduce a value the binding does not hold, so the shim owner
    removes such writes (initializer, reference initializer and assignment
    definition) before selecting the value at a read. Copies through another
    binding, a bound helper or with stay refused.
  • Reflect.get getter receivers (Codex, 3deab802 review): native Get
    runs the selected getter with its explicit receiver, or the target when
    omitted, mirroring the existing Reflect.set setter handling. A local
    receiver no longer suppresses a real endpoint; a global or unknown receiver
    still does.
  • Any-depth selections expose accessor callables. The port initially
    explored only accessor results for an escaped object; the whole object also
    exposes the accessor callable through its descriptor, which the returned
    receiver-exposure cases need.

Hardening

  • loaderDelegateDifferential.test.ts: 82 CommonJS cases run through
    Node's real CommonJS loader in a fresh worker thread (only the electron
    package stubbed) and through the production analyzer. Agreement is required except for two
    explicit lists the file owns: known precision limits (runtime keeps the
    original API, analyzer stays conservative) and known false findings
    (runtime replaces the loader, analyzer still reports it). An entry that
    starts agreeing fails until removed.
  • loaderDelegateFuzz.test.ts: seeded random compositions of the same
    mutation fragments; fails only on a false finding and logs agreed,
    conservative and skipped counts. REA_LOADER_FUZZ_CASES and
    REA_LOADER_FUZZ_SEED scale and reseed it. Local: seeds 1 and 2 at 1,500
    compositions each produced 0 false findings (1,277 and 1,230 agreed, 223
    and 269 conservative).
  • Documented in docs/testing.md under the JavaScript lanes.

Follow-up validation (local)

  • JavaScript domain suite: 6,054 passed, 4 failed across 152 files (local: Windows 11, Node 24.21.0, run while other heavy jobs were active). All four failures were 30 s timeouts of capacity cases; three pass when run alone (15 to 21 s each) and resolves repeated native bulk stores takes 42 s alone, see Remaining limits. Upstream's projection, bounding and coverage tests pass.
  • tsc --noEmit, npm run lint (0 errors, warnings only), module boundaries (2,241 files), architecture guards (1,558 files) and test discovery (1,729 files) pass. knip reports only two pre-existing unresolved worker aliases. oxfmt --check . is clean after restoring three upstream files that the merge commit's lint-staged hook had rewritten (a06775dd); the CI static-check failure on 429baa55 was that formatting.
  • The PR readiness check on 429baa55 failed on a GitHub API 503 and needs a rerun. CI for a06775dd was queued at push time; no CI pass is claimed here. No built CLI/MCP run and no real application were executed in this follow-up.

CI follow-up (after the first full CI run on a06775dd)

Context: this PR had never had a CI run before 2026-10-19 (PR readiness was
pending, so the CI workflow never started). The first run exposed four
kinds of failures; three are addressed below, one stays open.

  • Literal array spreads expand again. Keeping every spread opaque dropped
    the Worker URL resolution for the acceptance fixture
    Object.defineProperty(...[import.meta, "unrelated", ...]). The first
    version of the gate was a source-wide name scan; the review follow-up below
    replaces it with the ambient mutation facts. Spreads of bindings stay opaque.
  • Prototype enumeration taint narrowed to captures and writes. The early
    syntactic taint marked enumeration uncertain on any read of .prototype,
    __proto__, constructor or getPrototypeOf, so the harmless
    Object.setPrototypeOf({}, Object.prototype) in upstream's
    referenceSourceShadowedRequire fixture lost its require edge. It now
    taints only a captured selection (stored, returned, or passed where it may
    be retained), a getPrototypeOf result that is captured, a write through a
    physical or computed prototype chain, or a mutator whose target reaches one.
    All fixtures of that upstream test now produce the same static facts as
    main.
  • The CLI/MCP fixture matrix gets a 300 s budget: 60 fixtures through the
    compiled CLI and stdio MCP server cannot fit the default 30 s.
  • Ambient alias-branch and bulk-store hotspot fixed (34f3163a). Bulk
    copies from a static object literal are indexed by key at the
    iteration-source boundary instead of the unknown-key bucket, a copy whose
    literal has no own __proto__ key is never a possible prototype store,
    escape expansions are recorded once per resolver pass and replayed, and the
    global-root-store receiver and own-slot presence proofs are keyed by an
    ambient facts generation. In-process on Windows (tsc build, fresh process):
    300 bulk stores 61 s to 1.8 s cold, 300 getter stores 17.9 s to 1.9 s, the
    40-step member alias chain 116 s to 5.5 s; split and entry variants
    unchanged; results unchanged (bulk stores 0 windows, getter stores 1 window,
    14 hand-checked copy forms identical). This was a pre-existing regression of
    this branch against main (0.45 s there), independent of the
    returned-reference port.

Review follow-up on 8ea91c52 (facts instead of spellings; real loader)

Three findings on the CI follow-up commit were right: the iteration gate and
the reachability bound were spelling checks, and the differential oracle was
a hand-written module wrapper rather than Node's loader. All three now
consume owner facts.

  • The spread gate consumes ambient mutation facts.
    semanticArrayIterationMayBeReplacedBefore(site, facts, state) refuses the
    proof while the facts are incomplete, after any opaque effect site that
    precedes the spread, and when any ambient write effect that precedes it may
    match Array.prototype.values (a computed or template key, a
    whole-prototype write and an unknown-identity write all match). The
    source-wide name scan is gone. A known-mutator call with a literal spread is
    recorded in two marked readings, provisional (opaque recipient) and expanded
    (elements as positions); the refinement with the completed facts keeps
    exactly one per call. Reviewer case: Array[\prototype`][Symbol[`iterator`]]=...;
    Reflect.set(...[{},"x",0])now keeps the spread opaque (0 windows, runtime replacement);Array.prototype.extra=1; Reflect.set(...[module,'require',
    module.require])` expands (1 window, runtime original).
  • Prototype reflection on unattributed values is an unknown-identity
    write.
    When the ambient path resolver resolves the object a member write
    goes to and reaches the prototype of a call result that no owner attributes
    (no reference source, bound allocation, builtin origin or returned
    reference), or of a parameter or catch value, it records [null]. So
    Object.getPrototypeOf([].values()).next = fn, its alias, boxed,
    helper-return and helper-parameter forms all refuse the spread proof, while
    const o = {}; o.__proto__.x = 1 and Object.getPrototypeOf(Object.create(p))
    stay attributed and keep it. Read queries of a prototype are unchanged. An
    interim collector rule that made every __proto__/getPrototypeOf write
    opaque broke 17 prototype-identity cases and was dropped.
  • Reachability consumes resolved bindings and global member keys.
    semanticSiteMayExecute indexes each binding's resolved uses (through the
    reference-role owner) plus the property keys read off globalThis,
    window, self, global or a sloppy script-level this. A script-level
    function declaration is reachable through its name, through such a key (or
    an unknown key), through an export, or whenever an opaque effect site
    exists. globalThis['change']() and globalThis[name]() reach it in a
    classic script; a same-named key on a local object does not, in a script or
    a module. The identifier occurrence count is gone.
  • The oracle is Node's real CommonJS loader.
    tests/support/javascript/loaderDelegateOracle.ts writes each case as
    bundle.cjs beside a node_modules/electron stub and requires it from a
    fresh worker thread (a separate isolate: own globals, own Module, own
    cache), so require, module.require, Module.prototype.require and
    Module._load are genuine and one case's prototype mutations never reach
    the next. The record is a primitive string, since a case may replace array
    iteration. The corpus grew to 82 cases: shared loader writes
    (module.constructor._load, require('module')._load,
    Module.prototype.require), prototype reflection forms, string-keyed global
    reach and an exported helper. Object.prototype.extra=1; Reflect.set(...[{}, 'x', module.require]) left the precision list because the facts prove that
    spread intact; the exported helper joined it (reachable, never called here).
  • Validation (local, Windows 11, Node 24.21.0): JavaScript domain suite 152 files, 6,101 tests passed in 137 s (12 targeted files re-run twice, 1,530 tests); fuzz
    seed=20261019 cases=150 agreed=134 conservative=16 skipped=0 false=0; tsc --noEmit, oxfmt --check and oxlint (warnings only)
    clean. No built CLI/MCP run. CI for this head not yet reported.

Previous corrections (3deab802)

  • A proven custom at, pop, slice or reverse call could incorrectly inherit Array result aliases and hide a valid Electron finding. The interop producer now uses complete source-call/result coverage to supersede the nominal alternative. This proof is queried separately from cached invocation syntax; incomplete calls keep their possible aliases.
  • Reflect.set({__proto__:null},"require",module.require,module) now preserves the initial delegate. The copy proof consumes the shared descriptor/prototype walk to establish that an ordinary lookup cannot invoke a setter, while retaining the independent receiver, exact value, native-helper and preceding-effect checks. Target and prototype-value captures keep their original evaluation times.
  • Setter-absence dependencies name the owners each operation traverses. The foreign lookup depends on Object.prototype; Object.assign retains its existing module plus Object.prototype checks. Harmless earlier module stores therefore no longer block the new lookup proof.

Added 40 runtime-backed regression/control cases and four shared CLI/MCP fixture cases. Controls cover captured callables, actual native methods, bound/changed helpers, returned module/this values, independent Reflect receivers, getter/data descriptors, setter/prototype installation in later arguments and alias rebinding. Existing assertions, resource limits and timeouts were retained.

Validation at 3deab802

  • JavaScript domain/application suite: 6,281 passed across 160 files, 108.21s test duration (108.685s command wall).
  • Serial static gate: 6/6 tasks passed, 2m16.115s (136.358s command wall), one cached task. Includes typecheck, compile, lint/module boundaries, formatting, knip and metadata. Lint: 0 errors / 1,859 warnings.
  • 300/300 isolated Node comparisons pass: 180 current native-loader comparisons, 40 custom-method comparisons and 80 prior controls. They use real CommonJS wrapper/cache/native operations with only Electron exports stubbed. Twelve prior VM checks also pass.
  • 60/60 shared fixture sources match through the production parser/analyzer. Architecture guards pass across 1,529 source files; all 1,653 test files are discovered exactly once.
  • Independent review and differential runtime probes checked result completeness, publication phases, captured aliases, proxy/exotic rejection, failed Reflect.set results and setter-owner dependencies.
  • Both rebuilt public CLI/MCP tests failed, 8.76s test duration. A separate final built CLI diagnostic again returns capability_unavailable / unsupported_provider, provider process-ownership, operation prepare_owned_process: ps -axo pid=,ppid=,pgid=,uid=,stat=,command= fails with fatal library error, lookup self. Parsing cannot start; end-to-end adapter semantics remain unverified.
  • Known-limit diagnostics retain five older missing-finding mismatches and four passing returned/thrown-accessor controls. The additional coercion and repeated-copy mismatches below are not accuracy passes. No full deterministic-suite, coverage or CI pass is claimed.
  • Node 24.18.0, npm 11.16.0, Linux x86_64. Heavy jobs serialized with GOMAXPROCS=2 GOMEMLIMIT=512MiB UV_THREADPOOL_SIZE=2 NODE_OPTIONS=--max-old-space-size=1536; optional telemetry disabled.

Current performance evidence

Two serialized pairs compare the final tree with exact parent 213d4c0c on twelve source workloads, reversing process order. Each fresh runtime has a first pass and warm repeat. All input hashes match. Eight unaffected results match; both custom-result workloads and the single-copy lookup now produce corrected output. The fifty-copy lookup output remains unchanged and incorrect, as recorded under Remaining limits.

Measurement across two pairs Parent 213d4c0c This follow-up
First wall 1.563–1.646s 1.696–1.697s
First Node CPU 2.986–3.278s 3.331–3.361s
Warm wall 1.354–1.363s 1.257–1.343s
Warm Node CPU 2.818–2.847s 2.658–2.825s
Node peak RSS 318,612–326,480KiB 348,076–362,240KiB
Whole-command wall 3.403–3.458s 3.457–3.512s
Child-accounted process-family CPU 6.514–6.769s 6.715–6.824s

First wall/CPU and peak Node RSS increased; warm wall decreased. Peak Node RSS rose about 9–11%. These synthetic shared-host measurements do not establish a general speedup. Imports are outside analyzer timing and filesystem caches are warm. Agent tokens, cold-cache behavior, target-application/full-CLI costs and peak process-family RAM remain unmeasured; Node RSS is not process-family RSS.

Historical performance: parent 213d4c0c

Compared with exact parent 871cfcca on eight equivalent source workloads: stable/truncated arrays, inherited getters and native result selection, each at one and fifty repetitions. Two serialized pairs reverse process order; each fresh runtime has a first pass and warm repeat. All inputs match. Six unchanged workloads retain their result hashes; the two native-result workloads change as expected because replaced loaders are no longer reported as Electron.

Measurement across two pairs Parent 871cfcca 213d4c0c
First wall 1.038–1.256s 1.170–1.200s
First Node CPU 2.080–2.552s 2.408–2.410s
Warm wall 0.878–0.938s 0.904–1.064s
Warm Node CPU 1.713–1.771s 1.792–2.062s
Node peak RSS 297,996–341,028KiB 301,748–309,232KiB
Whole-command wall 2.379–2.917s 2.568–2.697s
Child-accounted process-family CPU 4.408–5.180s 4.832–5.136s

Aggregate results are mixed, with paired warm wall 3–14% higher. Native-result selection at fifty repetitions costs 68.9–84.1→142.2–160.1ms first / 48.8–56.0→119.7–138.8ms warm. Tracking the previously lost aliases adds work; this is not a general speedup. Timing begins after imports with existing filesystem caches. Agent tokens, cold-cache behavior, target-application/full-CLI cost and peak process-family RAM remain unmeasured; Node RSS is not process-family RSS.

Historical performance: concurrent parent 871cfcca

Twenty equivalent super method/getter workloads, two serialized before/after pairs against parent 894c6969. Each fresh runtime runs a first pass and warm repeat. All twenty source/result hashes match in all eight measured passes.

Measurement across two pairs Parent 894c6969 871cfcca
First wall 11.635–11.821s 11.533–12.497s
First Node CPU 17.618–17.630s 17.907–18.697s
Warm wall 10.172–10.604s 9.541–10.763s
Warm Node CPU 11.486–12.169s 10.922–12.237s
Node peak RSS 381,036–432,576KiB 382,488–389,784KiB
Whole-command wall 22.453–23.043s 22.544–22.773s
Child-accounted process-family CPU 29.970–30.650s 30.320–30.798s

Results are mixed: paired first wall ranges from roughly 1% lower to 6% higher; whole-command wall from 2% lower to 1% higher. No general speedup is established.

Inherited deep-super/getter performance regressions remain. Historical first/warm totals from 3156a7e4 to 73a4c834 grew approximately 0.7–0.9→9.4–9.6s / 0.5–0.6→8.5–8.8s; these are earlier revisions, not current-tree comparisons. Broader reuse still needs dependency/invalidation proof.

Timing starts after imports and uses existing filesystem caches. Cold-cache behavior, target-application performance, agent tokens, completed full-CLI costs and peak process-family memory remain unmeasured. Node RSS is not process-family RSS.

Historical performance: prior parent 894c6969

Compared the combined tree with parent 901a67ff on twenty equivalent super method/getter workloads. Two serialized before/after pairs each use a fresh runtime first pass and warm repeat. All twenty source/result hashes match in every pass.

Measurement across two pairs Parent 901a67ff 894c6969
First wall 9.985–10.202s 10.628–10.881s
First Node CPU 15.363–16.062s 16.140–16.959s
Warm wall 8.789–9.289s 8.513–9.007s
Warm Node CPU 10.037–10.336s 9.763–10.206s
Node peak RSS 368,296–381,916KiB 387,576–391,496KiB
Whole-command wall 19.417–19.728s 19.559–20.377s
Child-accounted process-family CPU 26.318–26.729s 27.063–27.290s

First-pass wall increased 4–9% and Node CPU increased 5–6%. Warm wall fell about 3%; whole-command wall/CPU and peak Node RSS increased. These synthetic shared-host measurements do not establish a general speedup.

Inherited performance regressions remain. Historical super measurements from 3156a7e4 to 73a4c834 showed first wall 0.719–0.891→9.352–9.550s, warm 0.490–0.573→8.521–8.831s, and whole-process wall 1.929–2.087→18.429–18.858s. Depth-20/read-20 instance/static getter first passes grew 67→2,831ms / 151→4,863ms. Earlier six-workload results from 8a688acb to 3156a7e4 grew 0.304→0.853s first / 0.239→0.677s warm. These describe historical revisions. Broader reuse still needs dependency/invalidation proof; the previous empty-store optimization showed no consistent benefit and was reverted.

Analyzer timing starts after imports and uses existing filesystem caches. Cold-cache behavior, target-application performance, agent tokens, completed full-CLI costs and peak process-family memory remain unmeasured. Node RSS is not process-family RSS. Process-family CPU above uses child resource accounting.

Remaining limits

  • Writes through helper parameters are not connected to call sites.
    function F(a){a.require=other} F(module) and
    function F(a,b,c){Reflect.set(a,b,c)} F(module,'require',other) replace
    the loader at runtime while analysis still reports the window. These are
    listed as known false findings in the differential corpus; they are open
    soundness gaps that predate this follow-up and need argument-to-parameter
    identity flow in the ambient path resolver. The prototype of a parameter is
    the exception: a write to Object.getPrototypeOf(a) or a.__proto__
    inside a helper records an unknown-identity write (conservative), so it
    refuses the spread proof; the .require writes themselves stay listed.

  • An unknown recipient inside a function that is referenced anywhere counts
    as a write even when every call passes fresh objects, for example
    function F(){Object.assign.apply(null,arguments)} F({}, {}). This is a
    precision limit, listed in the corpus.

  • Object.assign source getters, defineProperty descriptor getters, the
    implicit key-coercion receiver and sloppy arguments/caller slots remain
    open as recorded in the review threads.

  • The repeated bulk-store capacity test now completes in about 2 s locally;
    see the CI follow-up section.

  • Implicit key-coercion receivers still lose a valid fetch finding: the final VM comparison has one original call while analysis reports zero. A coercion hook that throws its callable and is later called without a receiver correctly invalidates the endpoint. No receiver-only narrowing is included; activation and outward/abrupt references need separate coverage.

  • Incomplete generic call/apply/bind result paths remain conservative. A preexisting overwritten call property holding Array.prototype.at can still produce a false Electron finding; the exact-parent differential reproduces it. An unused getter body with direct delegate effects can still suppress a real finding.

  • Repeated delegate-copy provenance remains incomplete. In the fifty-copy null-prototype lookup workload, isolated Node executes the original window once but current analysis reports zero, even with the initial delegate saved first. This is a recorded mismatch, not a passing accuracy case.

  • Native result provenance is still incomplete for standalone array mutation followed by a separate read, iterator results, callback-produced values, parameter-to-argument return flow and custom Array species. Explicit Reflect.get getter receivers, descriptor-reflection captures, factory-parameter closure flow and generic opaque escapes also have baseline gaps. These were reproduced against the parent and are not claimed fixed here.

  • Possible child/element aliases are intentionally broader than exact identity. Same-value copies through an ambiguous child selection can suppress a valid finding; the new reference candidates do not authorize a preservation proof.

  • The older-runtime regression uses separate VM realms with a deleted initial method to model absent target inventory. No actual Node 18 or historical Electron runtime was executed.

  • Repeated writes to arbitrary global slots retain possible aliases: the model lacks initial descriptor guarantees for those custom names. An apparent replacement with an unrelated object is not a strong proof that the old owner is unreachable.

  • Projected legacy B.prototype.__proto__=p assignments can still miss the changed prototype. Safely classifying them needs shared class-prototype descriptor facts to distinguish native setters from own/inherited shadows. Explicit Object/Reflect.setPrototypeOf changes are covered.

  • Async/await and generator returned-object flow has a preexisting invalidation gap. Six diagnostic runtime comparisons reproduce the baseline mismatch; they are not passing accuracy cases and no synchronous timing bound is applied to those returned expressions.

  • Object.assign source-getter finding: a local getter writing this.fetch can suppress the real global endpoint. A safe fix needs shared facts separating getter activation, copied values and thrown/outward references across copy phases. Subtracting accessor identities from generic escapes was unsafe.

  • Descriptor-getter finding: Object.defineProperty/defineProperties descriptor reads still use an overly broad escaped-callable receiver. They need distinct activation and outward-reference evidence, including abrupt completion, before narrowing.

  • Sloppy caller/arguments finding: Node reproduces the missed finding, but these own slots are engine extensions. The generic JavaScript model has no engine-provenance fact authorizing universal null-slot assumptions.

  • The prior known-limit diagnostic reproduced those five missing-finding cases (source getter, two descriptor forms, caller, arguments). Four returned/thrown-accessor safety controls agree with runtime. This diagnostic is not a passing nine-case accuracy claim.

  • A preexisting classic/unknown-script soundness gap remains: var Q=(e=>typeof require<"u"?require:e)(function(){}); external(globalThis); can retain Electron identity after unknown code replaces global Q. Both attempted corrections remain rejected.

  • Mixed early/late class getter reads conservatively union activations; the static self-field-alias variant remains unresolved. Early prototype-capture uncertainty is deliberately broad. Some nested fallback/callable paths also remain conservative. Other limits include nested skipped-RHS shim assignments, some sequence-return projections, shared-prototype Object.assign delegate copies, restoration after prior delegate effects, repeated bind with globalThis, prebound Reflect.set targets and unused-callable/private-slot paths.

The inherited-slot fix remains conservative after potentially effectful code and for modules, nested default allocations and constructor defaults. Wrapper metadata aliases/nested discard expressions and effectful descriptors can also lose otherwise valid provenance.

No target Joplin/Electron application was executed. CLI/MCP contracts, provider authority, process ownership, dependencies and analysis budgets remain intact. Other module families through the shim remain unsupported. Upstream 559af3d3 and date fixtures' known/unknown coverage are retained.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

The process above is REA's existing parsing worker.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Author readiness

Human owner: @Y0UW1SH

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4922 **Author:** [@Y0UW1SH](https://github.com/Y0UW1SH) **Created:** 10/17/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/esbuild-require-shim-3535` --- ### 📝 Commits (10+) - [`a6bfda7`](https://github.com/morluto/rea/commit/a6bfda7b612c8abe5d0825de2b4ecaf11f7503ff) fix(javascript): recover Electron identity through esbuild require shims - [`5b0b609`](https://github.com/morluto/rea/commit/5b0b60984d1a210e9637d2fc35701e0223962d27) fix(javascript): prove shim initialization and retain prototype write uncertainty - [`a33074d`](https://github.com/morluto/rea/commit/a33074dcc1b3c85676ead80cf60ea18c7e057be6) fix(javascript): preserve require shim binding identity - [`5fe55f2`](https://github.com/morluto/rea/commit/5fe55f2507ed0344c2f1d5316111e10ff6c2a565) chore: merge main and resolve shim analysis conflict - [`e160205`](https://github.com/morluto/rea/commit/e160205c9cb1fcf2483eb1b10b9fa2c8249a4b18) fix(javascript): preserve shim aliases and eval uncertainty - [`1783e4b`](https://github.com/morluto/rea/commit/1783e4b205106b768c9b9f0ed2e4feda3bac0caf) test: fix verifier prerequisites and asynchronous cleanup fixtures - [`33f6f75`](https://github.com/morluto/rea/commit/33f6f752bca5f7e69f2ba26ff61725397a0fb755) chore: reconcile upstream CI fixture fixes - [`e6beb7c`](https://github.com/morluto/rea/commit/e6beb7ca670c95696a645e1e236882a446f6225c) fix(javascript): retain module identity through projected aliases - [`6ddf321`](https://github.com/morluto/rea/commit/6ddf3215e22da76e6cf287bab8a3eb872cd13c11) fix(javascript): preserve alias and export capture integrity - [`0428390`](https://github.com/morluto/rea/commit/0428390a39d919a2cc17c4305cd7b7c6ef0e11ec) fix(javascript): preserve ordered alias effects ### 📊 Changes **107 files changed** (+30473 additions, -1127 deletions) <details> <summary>View changed files</summary> 📝 `docs/testing.md` (+25 -0) ➕ `src/domain/javascript/accessorAssignmentTargetReceivers.test.ts` (+72 -0) ➕ `src/domain/javascript/allocatedReceiverIdentity.test.ts` (+288 -0) ➕ `src/domain/javascript/arrayElementCaptureIntegrity.test.ts` (+116 -0) ➕ `src/domain/javascript/boundClassHelperCapture.test.ts` (+180 -0) ➕ `src/domain/javascript/callableClassValues.test.ts` (+162 -0) ➕ `src/domain/javascript/callableDefaultTargets.test.ts` (+65 -0) ➕ `src/domain/javascript/callableReferenceDefaults.test.ts` (+142 -0) ➕ `src/domain/javascript/capturedDefaultSlotIntegrity.test.ts` (+197 -0) ➕ `src/domain/javascript/constructedReceiverIdentity.test.ts` (+846 -0) ➕ `src/domain/javascript/constructorParentMutationIdentity.test.ts` (+120 -0) ➕ `src/domain/javascript/descriptorReturnedValueIdentity.test.ts` (+112 -0) ➕ `src/domain/javascript/destructuringDefinedSlots.test.ts` (+175 -0) ➕ `src/domain/javascript/destructuringReferenceOrder.test.ts` (+366 -0) 📝 `src/domain/javascript/electronModuleInterop.test.ts` (+161 -3) ➕ `src/domain/javascript/electronSavedExports.test.ts` (+168 -0) 📝 `src/domain/javascript/electronWrappedCallPaths.ts` (+4 -2) ➕ `src/domain/javascript/inheritedDefaultDataSlots.test.ts` (+88 -0) ➕ `src/domain/javascript/inheritedGetterEscapeIdentity.test.ts` (+68 -0) 📝 `src/domain/javascript/javascriptAstValues.ts` (+103 -6) _...and 80 more files_ </details> ### 📄 Description ## Summary Fixes #3535. Recover BrowserWindow, webPreferences and IPC findings through esbuild's guarded require helper while keeping analyzed application code inert. Builds on DeryFerd's closed, unmerged #3596 and preserves the original co-author credit. Published commit: [`d195ebb4`](https://github.com/morluto/rea/commit/d195ebb4a021c62e85f956db45b561eb51f06eef). Tested Git tree: `6ef791833f5785d23da6444b7d83ce31bf238200`. Parent: `3deab802`. The full PR retains the shared loader, callable, storage, prototype and capture owners, including target-version uncertainty, native result provenance, synchronous invocation bounds and independent CommonJS/global bindings. ## Latest corrections (2026-10-19 follow-up) - **Merged upstream `main` (5199c672)** and resolved the conflict with the upstream projection-widening rewrite (e4c3d723, fixes #4348/#5138). The shared callable-reference owner now exposes upstream's `precision` contract and feeds `returnedReferenceWidenings`, so the widening limitation and coverage fields are reported through the same channel as `main`. - **Replaced initializer-history expansion with a bounded fixed point.** The branch still carried per-path initializer histories (cycle blockers and memoized contexts) that upstream removed for exhausting the heap on React bundles. On this branch the same reassignment webs grew exponentially: 8 swap writes took 830 ms and 12 timed out at 15 s; a 24-arm React-shaped switch mesh timed out. A source reached again under another projection now joins projections (differing keys widen to `null`, differing depths to any depth), pending call-result selections are bounded, and widening never drops represented targets while refusing exact captures. Every probed size now completes in about 2 ms with identical results. Upstream's projection, bounding and coverage tests pass; the full JavaScript domain suite got faster (224 s to 157 s, local). - **Spread arguments stay opaque.** Spreading even a literal array runs the array iterator, which earlier source can replace through `Array.prototype[Symbol.iterator]` or the array iterator prototype (Codex, 213d4c0c review). The expansion helper is removed; `apply` keeps index-read literal arrays. - **Unknown recipients of known mutators are writes.** `Reflect.set(...args)`, `Reflect.apply(Reflect.set, Reflect, args)`, `Object.assign.apply(null, arguments)` and the spread forms above were classified as harmless by both the loader-delegate owner and the ambient owner (no target, no write). They now record an unknown-identity write once the enclosing code can run; a function declaration that is never referenced is excluded, so uncalled generic helpers keep their findings. - **Exact self-copies keep the shim** (Codex, 3deab802 review): `Q = Q` cannot introduce a value the binding does not hold, so the shim owner removes such writes (initializer, reference initializer and assignment definition) before selecting the value at a read. Copies through another binding, a bound helper or `with` stay refused. - **Reflect.get getter receivers** (Codex, 3deab802 review): native [[Get]] runs the selected getter with its explicit receiver, or the target when omitted, mirroring the existing Reflect.set setter handling. A local receiver no longer suppresses a real endpoint; a global or unknown receiver still does. - **Any-depth selections expose accessor callables.** The port initially explored only accessor results for an escaped object; the whole object also exposes the accessor callable through its descriptor, which the returned receiver-exposure cases need. ## Hardening - `loaderDelegateDifferential.test.ts`: 82 CommonJS cases run through Node's real CommonJS loader in a fresh worker thread (only the `electron` package stubbed) and through the production analyzer. Agreement is required except for two explicit lists the file owns: known precision limits (runtime keeps the original API, analyzer stays conservative) and known false findings (runtime replaces the loader, analyzer still reports it). An entry that starts agreeing fails until removed. - `loaderDelegateFuzz.test.ts`: seeded random compositions of the same mutation fragments; fails only on a false finding and logs agreed, conservative and skipped counts. `REA_LOADER_FUZZ_CASES` and `REA_LOADER_FUZZ_SEED` scale and reseed it. Local: seeds 1 and 2 at 1,500 compositions each produced 0 false findings (1,277 and 1,230 agreed, 223 and 269 conservative). - Documented in `docs/testing.md` under the JavaScript lanes. ## Follow-up validation (local) - JavaScript domain suite: **6,054 passed, 4 failed** across 152 files (local: Windows 11, Node 24.21.0, run while other heavy jobs were active). All four failures were 30 s timeouts of capacity cases; three pass when run alone (15 to 21 s each) and `resolves repeated native bulk stores` takes 42 s alone, see Remaining limits. Upstream's projection, bounding and coverage tests pass. - `tsc --noEmit`, `npm run lint` (0 errors, warnings only), module boundaries (2,241 files), architecture guards (1,558 files) and test discovery (1,729 files) pass. `knip` reports only two pre-existing unresolved worker aliases. `oxfmt --check .` is clean after restoring three upstream files that the merge commit's lint-staged hook had rewritten (`a06775dd`); the CI static-check failure on `429baa55` was that formatting. - The PR readiness check on `429baa55` failed on a GitHub API 503 and needs a rerun. CI for `a06775dd` was queued at push time; no CI pass is claimed here. No built CLI/MCP run and no real application were executed in this follow-up. ## CI follow-up (after the first full CI run on a06775dd) Context: this PR had never had a CI run before 2026-10-19 (PR readiness was pending, so the CI workflow never started). The first run exposed four kinds of failures; three are addressed below, one stays open. - **Literal array spreads expand again.** Keeping every spread opaque dropped the Worker URL resolution for the acceptance fixture `Object.defineProperty(...[import.meta, "unrelated", ...])`. The first version of the gate was a source-wide name scan; the review follow-up below replaces it with the ambient mutation facts. Spreads of bindings stay opaque. - **Prototype enumeration taint narrowed to captures and writes.** The early syntactic taint marked enumeration uncertain on any read of `.prototype`, `__proto__`, `constructor` or `getPrototypeOf`, so the harmless `Object.setPrototypeOf({}, Object.prototype)` in upstream's `referenceSourceShadowedRequire` fixture lost its `require` edge. It now taints only a captured selection (stored, returned, or passed where it may be retained), a `getPrototypeOf` result that is captured, a write through a physical or computed prototype chain, or a mutator whose target reaches one. All fixtures of that upstream test now produce the same static facts as `main`. - **The CLI/MCP fixture matrix gets a 300 s budget**: 60 fixtures through the compiled CLI and stdio MCP server cannot fit the default 30 s. - **Ambient alias-branch and bulk-store hotspot fixed (`34f3163a`).** Bulk copies from a static object literal are indexed by key at the iteration-source boundary instead of the unknown-key bucket, a copy whose literal has no own `__proto__` key is never a possible prototype store, escape expansions are recorded once per resolver pass and replayed, and the global-root-store receiver and own-slot presence proofs are keyed by an ambient facts generation. In-process on Windows (tsc build, fresh process): 300 bulk stores 61 s to 1.8 s cold, 300 getter stores 17.9 s to 1.9 s, the 40-step member alias chain 116 s to 5.5 s; split and entry variants unchanged; results unchanged (bulk stores 0 windows, getter stores 1 window, 14 hand-checked copy forms identical). This was a pre-existing regression of this branch against `main` (0.45 s there), independent of the returned-reference port. ## Review follow-up on 8ea91c52 (facts instead of spellings; real loader) Three findings on the CI follow-up commit were right: the iteration gate and the reachability bound were spelling checks, and the differential oracle was a hand-written module wrapper rather than Node's loader. All three now consume owner facts. - **The spread gate consumes ambient mutation facts.** `semanticArrayIterationMayBeReplacedBefore(site, facts, state)` refuses the proof while the facts are incomplete, after any opaque effect site that precedes the spread, and when any ambient write effect that precedes it may match `Array.prototype.values` (a computed or template key, a whole-prototype write and an unknown-identity write all match). The source-wide name scan is gone. A known-mutator call with a literal spread is recorded in two marked readings, provisional (opaque recipient) and expanded (elements as positions); the refinement with the completed facts keeps exactly one per call. Reviewer case: `Array[\`prototype\`][Symbol[\`iterator\`]]=...; Reflect.set(...[{},"x",0])` now keeps the spread opaque (0 windows, runtime replacement); `Array.prototype.extra=1; Reflect.set(...[module,'require', module.require])` expands (1 window, runtime original). - **Prototype reflection on unattributed values is an unknown-identity write.** When the ambient path resolver resolves the object a member write goes to and reaches the prototype of a call result that no owner attributes (no reference source, bound allocation, builtin origin or returned reference), or of a parameter or catch value, it records `[null]`. So `Object.getPrototypeOf([].values()).next = fn`, its alias, boxed, helper-return and helper-parameter forms all refuse the spread proof, while `const o = {}; o.__proto__.x = 1` and `Object.getPrototypeOf(Object.create(p))` stay attributed and keep it. Read queries of a prototype are unchanged. An interim collector rule that made every `__proto__`/`getPrototypeOf` write opaque broke 17 prototype-identity cases and was dropped. - **Reachability consumes resolved bindings and global member keys.** `semanticSiteMayExecute` indexes each binding's resolved uses (through the reference-role owner) plus the property keys read off `globalThis`, `window`, `self`, `global` or a sloppy script-level `this`. A script-level function declaration is reachable through its name, through such a key (or an unknown key), through an export, or whenever an opaque effect site exists. `globalThis['change']()` and `globalThis[name]()` reach it in a classic script; a same-named key on a local object does not, in a script or a module. The identifier occurrence count is gone. - **The oracle is Node's real CommonJS loader.** `tests/support/javascript/loaderDelegateOracle.ts` writes each case as `bundle.cjs` beside a `node_modules/electron` stub and requires it from a fresh worker thread (a separate isolate: own globals, own `Module`, own cache), so `require`, `module.require`, `Module.prototype.require` and `Module._load` are genuine and one case's prototype mutations never reach the next. The record is a primitive string, since a case may replace array iteration. The corpus grew to 82 cases: shared loader writes (`module.constructor._load`, `require('module')._load`, `Module.prototype.require`), prototype reflection forms, string-keyed global reach and an exported helper. `Object.prototype.extra=1; Reflect.set(...[{}, 'x', module.require])` left the precision list because the facts prove that spread intact; the exported helper joined it (reachable, never called here). - Validation (local, Windows 11, Node 24.21.0): JavaScript domain suite 152 files, 6,101 tests passed in 137 s (12 targeted files re-run twice, 1,530 tests); fuzz `seed=20261019 cases=150 agreed=134 conservative=16 skipped=0 false=0`; `tsc --noEmit`, `oxfmt --check` and `oxlint` (warnings only) clean. No built CLI/MCP run. CI for this head not yet reported. ## Previous corrections (3deab802) - A proven custom `at`, `pop`, `slice` or `reverse` call could incorrectly inherit Array result aliases and hide a valid Electron finding. The interop producer now uses complete source-call/result coverage to supersede the nominal alternative. This proof is queried separately from cached invocation syntax; incomplete calls keep their possible aliases. - `Reflect.set({__proto__:null},"require",module.require,module)` now preserves the initial delegate. The copy proof consumes the shared descriptor/prototype walk to establish that an ordinary lookup cannot invoke a setter, while retaining the independent receiver, exact value, native-helper and preceding-effect checks. Target and prototype-value captures keep their original evaluation times. - Setter-absence dependencies name the owners each operation traverses. The foreign lookup depends on Object.prototype; Object.assign retains its existing module plus Object.prototype checks. Harmless earlier module stores therefore no longer block the new lookup proof. Added **40 runtime-backed regression/control cases** and **four shared CLI/MCP fixture cases**. Controls cover captured callables, actual native methods, bound/changed helpers, returned module/this values, independent Reflect receivers, getter/data descriptors, setter/prototype installation in later arguments and alias rebinding. Existing assertions, resource limits and timeouts were retained. ## Validation at 3deab802 - JavaScript domain/application suite: **6,281 passed across 160 files**, **108.21s** test duration (**108.685s** command wall). - Serial static gate: **6/6 tasks passed**, **2m16.115s** (**136.358s** command wall), one cached task. Includes typecheck, compile, lint/module boundaries, formatting, knip and metadata. Lint: **0 errors / 1,859 warnings**. - **300/300 isolated Node comparisons pass:** 180 current native-loader comparisons, 40 custom-method comparisons and 80 prior controls. They use real CommonJS wrapper/cache/native operations with only Electron exports stubbed. Twelve prior VM checks also pass. - **60/60 shared fixture sources** match through the production parser/analyzer. Architecture guards pass across **1,529 source files**; all **1,653 test files** are discovered exactly once. - Independent review and differential runtime probes checked result completeness, publication phases, captured aliases, proxy/exotic rejection, failed Reflect.set results and setter-owner dependencies. - Both rebuilt public CLI/MCP tests **failed**, **8.76s** test duration. A separate final built CLI diagnostic again returns `capability_unavailable` / `unsupported_provider`, provider `process-ownership`, operation `prepare_owned_process`: `ps -axo pid=,ppid=,pgid=,uid=,stat=,command=` fails with `fatal library error, lookup self`. Parsing cannot start; end-to-end adapter semantics remain unverified. - Known-limit diagnostics retain five older missing-finding mismatches and four passing returned/thrown-accessor controls. The additional coercion and repeated-copy mismatches below are not accuracy passes. No full deterministic-suite, coverage or CI pass is claimed. - Node **24.18.0**, npm **11.16.0**, Linux x86_64. Heavy jobs serialized with `GOMAXPROCS=2 GOMEMLIMIT=512MiB UV_THREADPOOL_SIZE=2 NODE_OPTIONS=--max-old-space-size=1536`; optional telemetry disabled. ## Current performance evidence Two serialized pairs compare the final tree with exact parent **213d4c0c** on twelve source workloads, reversing process order. Each fresh runtime has a first pass and warm repeat. All input hashes match. Eight unaffected results match; both custom-result workloads and the single-copy lookup now produce corrected output. The fifty-copy lookup output remains unchanged and incorrect, as recorded under Remaining limits. | Measurement across two pairs | Parent 213d4c0c | This follow-up | | --- | ---: | ---: | | First wall | 1.563–1.646s | 1.696–1.697s | | First Node CPU | 2.986–3.278s | 3.331–3.361s | | Warm wall | 1.354–1.363s | 1.257–1.343s | | Warm Node CPU | 2.818–2.847s | 2.658–2.825s | | Node peak RSS | 318,612–326,480KiB | 348,076–362,240KiB | | Whole-command wall | 3.403–3.458s | 3.457–3.512s | | Child-accounted process-family CPU | 6.514–6.769s | 6.715–6.824s | First wall/CPU and peak Node RSS increased; warm wall decreased. Peak Node RSS rose about **9–11%**. These synthetic shared-host measurements do not establish a general speedup. Imports are outside analyzer timing and filesystem caches are warm. Agent tokens, cold-cache behavior, target-application/full-CLI costs and peak process-family RAM remain unmeasured; Node RSS is not process-family RSS. ## Historical performance: parent 213d4c0c Compared with exact parent **871cfcca** on eight equivalent source workloads: stable/truncated arrays, inherited getters and native result selection, each at one and fifty repetitions. Two serialized pairs reverse process order; each fresh runtime has a first pass and warm repeat. All inputs match. Six unchanged workloads retain their result hashes; the two native-result workloads change as expected because replaced loaders are no longer reported as Electron. | Measurement across two pairs | Parent 871cfcca | 213d4c0c | | --- | ---: | ---: | | First wall | 1.038–1.256s | 1.170–1.200s | | First Node CPU | 2.080–2.552s | 2.408–2.410s | | Warm wall | 0.878–0.938s | 0.904–1.064s | | Warm Node CPU | 1.713–1.771s | 1.792–2.062s | | Node peak RSS | 297,996–341,028KiB | 301,748–309,232KiB | | Whole-command wall | 2.379–2.917s | 2.568–2.697s | | Child-accounted process-family CPU | 4.408–5.180s | 4.832–5.136s | Aggregate results are mixed, with paired warm wall **3–14% higher**. Native-result selection at fifty repetitions costs **68.9–84.1→142.2–160.1ms first / 48.8–56.0→119.7–138.8ms warm**. Tracking the previously lost aliases adds work; this is not a general speedup. Timing begins after imports with existing filesystem caches. Agent tokens, cold-cache behavior, target-application/full-CLI cost and peak process-family RAM remain unmeasured; Node RSS is not process-family RSS. ## Historical performance: concurrent parent 871cfcca Twenty equivalent super method/getter workloads, two serialized before/after pairs against parent **894c6969**. Each fresh runtime runs a first pass and warm repeat. All twenty source/result hashes match in all eight measured passes. | Measurement across two pairs | Parent 894c6969 | 871cfcca | | --- | ---: | ---: | | First wall | 11.635–11.821s | 11.533–12.497s | | First Node CPU | 17.618–17.630s | 17.907–18.697s | | Warm wall | 10.172–10.604s | 9.541–10.763s | | Warm Node CPU | 11.486–12.169s | 10.922–12.237s | | Node peak RSS | 381,036–432,576KiB | 382,488–389,784KiB | | Whole-command wall | 22.453–23.043s | 22.544–22.773s | | Child-accounted process-family CPU | 29.970–30.650s | 30.320–30.798s | Results are mixed: paired first wall ranges from roughly **1% lower to 6% higher**; whole-command wall from **2% lower to 1% higher**. No general speedup is established. Inherited deep-super/getter performance regressions remain. Historical first/warm totals from `3156a7e4` to `73a4c834` grew approximately **0.7–0.9→9.4–9.6s / 0.5–0.6→8.5–8.8s**; these are earlier revisions, not current-tree comparisons. Broader reuse still needs dependency/invalidation proof. Timing starts after imports and uses existing filesystem caches. Cold-cache behavior, target-application performance, agent tokens, completed full-CLI costs and peak process-family memory remain unmeasured. Node RSS is not process-family RSS. ## Historical performance: prior parent 894c6969 Compared the combined tree with parent **901a67ff** on twenty equivalent super method/getter workloads. Two serialized before/after pairs each use a fresh runtime first pass and warm repeat. All twenty source/result hashes match in every pass. | Measurement across two pairs | Parent 901a67ff | 894c6969 | | --- | ---: | ---: | | First wall | 9.985–10.202s | 10.628–10.881s | | First Node CPU | 15.363–16.062s | 16.140–16.959s | | Warm wall | 8.789–9.289s | 8.513–9.007s | | Warm Node CPU | 10.037–10.336s | 9.763–10.206s | | Node peak RSS | 368,296–381,916KiB | 387,576–391,496KiB | | Whole-command wall | 19.417–19.728s | 19.559–20.377s | | Child-accounted process-family CPU | 26.318–26.729s | 27.063–27.290s | First-pass wall increased **4–9%** and Node CPU increased **5–6%**. Warm wall fell about **3%**; whole-command wall/CPU and peak Node RSS increased. These synthetic shared-host measurements do not establish a general speedup. **Inherited performance regressions remain.** Historical super measurements from 3156a7e4 to 73a4c834 showed first wall **0.719–0.891→9.352–9.550s**, warm **0.490–0.573→8.521–8.831s**, and whole-process wall **1.929–2.087→18.429–18.858s**. Depth-20/read-20 instance/static getter first passes grew **67→2,831ms / 151→4,863ms**. Earlier six-workload results from 8a688acb to 3156a7e4 grew **0.304→0.853s first / 0.239→0.677s warm**. These describe historical revisions. Broader reuse still needs dependency/invalidation proof; the previous empty-store optimization showed no consistent benefit and was reverted. Analyzer timing starts after imports and uses existing filesystem caches. Cold-cache behavior, target-application performance, agent tokens, completed full-CLI costs and peak process-family memory remain unmeasured. Node RSS is not process-family RSS. Process-family CPU above uses child resource accounting. ## Remaining limits - **Writes through helper parameters are not connected to call sites.** `function F(a){a.require=other} F(module)` and `function F(a,b,c){Reflect.set(a,b,c)} F(module,'require',other)` replace the loader at runtime while analysis still reports the window. These are listed as known false findings in the differential corpus; they are open soundness gaps that predate this follow-up and need argument-to-parameter identity flow in the ambient path resolver. The prototype of a parameter is the exception: a write to `Object.getPrototypeOf(a)` or `a.__proto__` inside a helper records an unknown-identity write (conservative), so it refuses the spread proof; the `.require` writes themselves stay listed. - An unknown recipient inside a function that is referenced anywhere counts as a write even when every call passes fresh objects, for example `function F(){Object.assign.apply(null,arguments)} F({}, {})`. This is a precision limit, listed in the corpus. - `Object.assign` source getters, `defineProperty` descriptor getters, the implicit key-coercion receiver and sloppy `arguments`/`caller` slots remain open as recorded in the review threads. - The repeated bulk-store capacity test now completes in about 2 s locally; see the CI follow-up section. - [Implicit key-coercion receivers](https://github.com/morluto/rea/pull/4922#discussion_r4315388116) still lose a valid fetch finding: the final VM comparison has one original call while analysis reports zero. A coercion hook that throws its callable and is later called without a receiver correctly invalidates the endpoint. No receiver-only narrowing is included; activation and outward/abrupt references need separate coverage. - Incomplete generic call/apply/bind result paths remain conservative. A preexisting overwritten `call` property holding Array.prototype.at can still produce a false Electron finding; the exact-parent differential reproduces it. An unused getter body with direct delegate effects can still suppress a real finding. - Repeated delegate-copy provenance remains incomplete. In the fifty-copy null-prototype lookup workload, isolated Node executes the original window once but current analysis reports zero, even with the initial delegate saved first. This is a recorded mismatch, not a passing accuracy case. - Native result provenance is still incomplete for standalone array mutation followed by a separate read, iterator results, callback-produced values, parameter-to-argument return flow and custom Array species. Explicit Reflect.get getter receivers, descriptor-reflection captures, factory-parameter closure flow and generic opaque escapes also have baseline gaps. These were reproduced against the parent and are not claimed fixed here. - Possible child/element aliases are intentionally broader than exact identity. Same-value copies through an ambiguous child selection can suppress a valid finding; the new reference candidates do not authorize a preservation proof. - The older-runtime regression uses separate VM realms with a deleted initial method to model absent target inventory. No actual Node 18 or historical Electron runtime was executed. - Repeated writes to arbitrary global slots retain possible aliases: the model lacks initial descriptor guarantees for those custom names. An apparent replacement with an unrelated object is not a strong proof that the old owner is unreachable. - Projected legacy `B.prototype.__proto__=p` assignments can still miss the changed prototype. Safely classifying them needs shared class-prototype descriptor facts to distinguish native setters from own/inherited shadows. Explicit Object/Reflect.setPrototypeOf changes are covered. - Async/await and generator returned-object flow has a preexisting invalidation gap. Six diagnostic runtime comparisons reproduce the baseline mismatch; they are not passing accuracy cases and no synchronous timing bound is applied to those returned expressions. - [Object.assign source-getter finding](https://github.com/morluto/rea/pull/4922#discussion_r4310124357): a local getter writing `this.fetch` can suppress the real global endpoint. A safe fix needs shared facts separating getter activation, copied values and thrown/outward references across copy phases. Subtracting accessor identities from generic escapes was unsafe. - [Descriptor-getter finding](https://github.com/morluto/rea/pull/4922#discussion_r4310285863): Object.defineProperty/defineProperties descriptor reads still use an overly broad escaped-callable receiver. They need distinct activation and outward-reference evidence, including abrupt completion, before narrowing. - [Sloppy caller/arguments finding](https://github.com/morluto/rea/pull/4922#discussion_r4310285866): Node reproduces the missed finding, but these own slots are engine extensions. The generic JavaScript model has no engine-provenance fact authorizing universal null-slot assumptions. - The prior known-limit diagnostic reproduced those **five missing-finding cases** (source getter, two descriptor forms, caller, arguments). Four returned/thrown-accessor safety controls agree with runtime. This diagnostic is **not** a passing nine-case accuracy claim. - A preexisting classic/unknown-script soundness gap remains: `var Q=(e=>typeof require<"u"?require:e)(function(){}); external(globalThis);` can retain Electron identity after unknown code replaces global Q. Both attempted corrections remain rejected. - Mixed early/late class getter reads conservatively union activations; the static self-field-alias variant remains unresolved. Early prototype-capture uncertainty is deliberately broad. Some nested fallback/callable paths also remain conservative. Other limits include nested skipped-RHS shim assignments, some sequence-return projections, shared-prototype Object.assign delegate copies, restoration after prior delegate effects, repeated bind with globalThis, prebound Reflect.set targets and unused-callable/private-slot paths. The inherited-slot fix remains conservative after potentially effectful code and for modules, nested default allocations and constructor defaults. Wrapper metadata aliases/nested discard expressions and effectful descriptors can also lose otherwise valid provenance. No target Joplin/Electron application was executed. CLI/MCP contracts, provider authority, process ownership, dependencies and analysis budgets remain intact. Other module families through the shim remain unsupported. Upstream `559af3d3` and date fixtures' known/unknown coverage are retained. Expected effects: - [x] Read existing files or captured data. - [x] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. The process above is REA's existing parsing worker. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Author readiness Human owner: @Y0UW1SH - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:42:14 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6676
No description provided.