Commit Graph
88 Commits
Author SHA1 Message Date
Chris Tate 7ae931c40e feat: preserve class and child handles through unknown 2026-09-15 19:07:32 -05:00
Chris Tate 5a782a6150 feat: add static async iteration parity (#328) 2026-09-15 00:03:07 -05:00
Chris Tate 2ac8743dbb feat(compiler): add Node module resolution introspection (#322) 2026-09-14 18:15:44 -05:00
Chris TateandPaolo Anzani 2bfc37b138 fix(compiler): support branded primitive types (#320)
Co-authored-by: Paolo Anzani <201222836+paoloanzn@users.noreply.github.com>
2026-09-14 13:46:36 -05:00
Chris Tate 073c840564 Enforce TypeScript 7 and Node type-link parity (#315)
* Enforce TypeScript 7 and Node type-link parity

* fix(compiler): normalize Windows resolver paths

* fix(compiler): canonicalize Windows realpaths
2026-09-14 09:19:08 -05:00
Chris Tate a8a5204574 Implement typed async generators (#310) 2026-09-14 00:38:06 -05:00
Chris Tate 9fb0620f06 Implement explicit resource management (#309) 2026-09-13 23:34:17 -05:00
Chris Tate 94fd361b54 Implement sparse native arrays and missing-value parity (#297)
Represent ordinary JavaScript arrays with hole, value, and explicit-undefined states; add sparse side storage and carry missing values through the frontend, native backends, runtime, and Node-facing consumers.
2026-09-12 22:39:07 -05:00
Chris Tate 0bf47eb87e feat: support static ESM import metadata 2026-09-02 18:18:09 -05:00
Chris Tateandyowainwright 1071b87d77 Lower Math.PI and Math.E statically (#279)
Co-authored-by: yowainwright <1074042+yowainwright@users.noreply.github.com>
2026-09-01 00:47:23 -05:00
Chris Tate 6310dfb960 Reuse reachability lowering output (#178)
* Reuse reachability lowering output

- Retain reachable IR and assemble the emitted module without lowering bodies a second time
- Preserve reached-only artifact filtering, coverage remainder behavior, and add lowering phase timing

* Preserve record order in retained lowering

* Preserve record order in retained helpers

* Preserve generic lowering record order

* Recheck retained generic rest order

* Record retained lowering parity fixtures

* Preserve retained lowering failure parity
2026-08-20 14:01:39 -05:00
Jimmy Miller b98b7c4a49 fix(compiler): guard zero-arg dynamic filter calls 2026-08-17 10:34:43 -04:00
Jimmy Miller 625a8ee6c0 fix(compiler): fence dynamic filter void predicates 2026-08-14 10:27:39 -04:00
Jimmy Miller 76dde4bf87 test(compiler): refresh filter void snapshot 2026-08-14 10:27:38 -04:00
Jimmy Miller 6f4592295f test(compiler): cover island filter void predicate 2026-08-14 10:27:38 -04:00
Jimmy Miller 4fe24e91d4 fix(compiler): clarify void filter predicate fence 2026-08-14 10:27:38 -04:00
Jimmy Miller 0bd90765cf feat(compiler): accept truthy (non-bool) filter predicates
`.filter(fn)` required the callback to return exactly bool. JS applies
ToBoolean to whatever the predicate answers, so `xs.filter((s) => s)` — the
idiomatic drop-the-falsy — was refused for no semantic reason.

The filter loop now wraps the call in the same toBool an `if` statement would
apply. A bool answer is unchanged and emits identical IR. A union answer
routes through its interned per-arm truthy helper, requested at the call site
where a real node exists for the diagnostic. A unit-only answer is constantly
falsy, and the call still runs for its effects.

The kinds with no native ToBoolean keep the fence: void has no value at all,
and dyn/jsval/caught truthiness needs the embedded engine.

Verified against Node: filtering strings, numbers, and a
`(string | undefined)[]` all print identically in both.

Diagnostics 100, filter corpus 9 across both
backends.
2026-08-14 10:27:38 -04:00
Jimmy Miller 990c7ce048 fix(compiler): preserve runtime-optional soundness 2026-08-13 15:47:45 -04:00
Chris Tate 4d96d2de28 feat(fs): support three-argument writeFile (#112)
* feat(fs): support three-argument writeFile

- Lower static UTF-8 encoding and mode options for fs/promises.writeFile.

- Add mode-aware runtime and backend support with promise rejection behavior.

- Cover supported options and failures in the differential corpus.

* fix fs write option evaluation and mode validation

* fix fs prototype-backed write options

* fix: preserve ignored write option semantics
2026-08-10 10:19:51 -05:00
Chris Tate b94c88e31c feat: support static non-UTF-8 TextDecoder labels (#111)
* feat: support static non-UTF-8 TextDecoder labels

- Recognize static WHATWG labels and aliases during builtin lowering.
- Add feature-gated portable decoders generated against the pinned Node runtime.
- Cover legacy encodings, malformed input, and unsupported dynamic labels.

* fix: match Node legacy decoder recovery

* fix TextDecoder edge-case parity

* fix(runtime): match Node malformed text decoding

* fix TextDecoder recovery edge cases
2026-08-09 23:15:51 -05:00
Chris Tate a9b6dba424 feat(string): support static split limits (#109)
* feat(string): support static split limits

- Lower split limits for string and literal RegExp separators across C and LLVM.
- Match JavaScript ToUint32 limit semantics in the runtime.
- Add differential coverage and refresh diagnostics and surface metadata.

* fix(string): default undefined split limits
2026-08-09 18:09:00 -05:00
Chris Tate f1066d7825 Support variable Buffer.toString encodings (#108)
* Support variable Buffer.toString encodings

- Lower runtime-valued BufferEncoding arguments through checked C and LLVM paths.
- Normalize aliases and casing while preserving Node unknown-encoding errors.
- Add differential coverage and remove the obsolete diagnostic fence.

* fix(buffer): preserve unknown encoding messages

* fix(buffer): preserve optional encoding defaults

* fix(runtime): handle empty Buffer decoding ranges
2026-08-09 17:23:13 -05:00
Chris Tateandsensei-woo e5c875bd97 Support Date instance lowering (#101)
* Support Date instance lowering

- Lower read-only Date values and constructors through the C and LLVM backends.

- Add portable calendar helpers, differential coverage, and documented surface contracts.

- Fence unsupported mutation, identity, locale, and component-constructor forms.

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>

* Fix Date exception and timezone semantics

* fix: harden Date lowering edge cases

* fix Date lowering edge cases

* Fix Date union fences and offset clipping

---------

Co-authored-by: sensei-woo <sensei-woo@users.noreply.github.com>
2026-08-08 23:17:58 -05:00
Chris Tate 782d3b8451 fix(compiler): support implicit top-level await modules (#94)
* fix(compiler): support implicit top-level await modules

- Match Node 24 syntax detection by treating top-level await as an ES module marker.

- Add differential corpus coverage for TypeScript files without explicit imports or exports.

* fix Node ESM classification for top-level await

* fix ESM require diagnostics and classification caching

* fix require binding and package type precedence

* fix: align top-level await module detection with Node
2026-08-08 11:35:01 -05:00
Chris Tate 52459a86d4 feat(compiler): lower stored text codec calls (#85)
* feat(compiler): lower stored text codec calls

- Lower same-scope const TextEncoder and TextDecoder call receivers without runtime instances.
- Preserve fences for escaping, captured, and imported codec values with actionable diagnostics.
- Add differential, diagnostic, and preflight baseline coverage for the supported idiom.

* fix(compiler): fence deferred text codec calls

* fix text codec switch-clause TDZ handling
2026-08-02 21:24:33 -05:00
Chris Tate 334febe43d fix(fetch): address remaining review findings 2026-07-30 22:48:36 -05:00
Chris Tate 8790cefbab fix(fetch): address review feedback 2026-07-30 11:35:00 -05:00
Chris Tate dafd7ac647 fix: close static fetch parity gaps 2026-07-30 11:35:00 -05:00
Chris Tate f11e51256c feat: make fetch static-compatible
- Add native RequestInit, AbortSignal, and streaming request and response body support.
- Lower the static fetch surface through both C and LLVM without the embedded engine.
- Add differential, Node-typing, sanitizer, and regression coverage.
2026-07-30 11:34:59 -05:00
Chris Tate e88ddc1678 Support top-level await
- Lower asynchronous ESM initialization with Node-compatible dependency ordering and caching.
- Finish top-level promises consistently across the C and LLVM runtimes.
- Add differential coverage for imports, rejection, and unsettled evaluation.
2026-07-28 11:19:04 -05:00
Chris Tate bc2cd2b7d1 Support Number.toFixed statically
- Lower Number.prototype.toFixed through the compiler IR and native backends.
- Match JavaScript rounding, range errors, and numeric edge cases in the runtime.
- Add differential coverage and update surface metadata and diagnostics.
2026-07-26 18:43:53 -05:00
Chris Tate 2c0f01ad93 HTTP Agent and dynamic-handle compatibility
- Add Agent connection accounting and HTTP/socket compatibility surfaces
- Support dynamic Buffer at and slice access
- Pin behavior with differential corpus coverage
2026-07-24 18:52:19 -05:00
Chris Tate de37b09716 Trap-claim carve-outs restore two over-narrowed shapes; extending a poisoned base refuses at build time
- The written-binding decline yields when the declared type has no mapping or the write's RHS roots at an ambient-undefined name
- A class extending a collection-poisoned base flushes the base's diagnostics eagerly: the heritage evaluates at module init, so deferring the compiler's own fence manufactured a silent wrong output
2026-07-24 17:18:25 -05:00
Chris Tate e947502af6 The long tail of engine-held values: iteration, promises crossing, and the driver through its plugin pipeline
- for-of, destructuring, and spread over engine-held dyn values ride the engine's own iterator protocol with V8's exact non-iterable spellings; for-of over dyn lowers generally
- Static promises cross into the engine as thenables; Promise.all over mixed and non-literal entries runs the engine's own combinator; engine-array fulfillments exit by reference
- The re-minted silent-wrong-answer path is closed: island-literal members whose values poison fence at the read with the captured diagnostic
- The checked-dynamic tree replaces the old nickname across comments and internal docs
2026-07-24 17:17:00 -05:00
Chris Tate a7cd284944 Trap claims survive writes storage cannot serve; extending a rejected base reports at compile time
- trapDeclRootOf's written-binding decline now requires a mappable declared type: ordinary storage only buys anything when it compiles — an unmappable recursive call shape traded the compiling no-storage trap for a guaranteed SC2009 on a value that never exists
- bindingEverWritten skips statement-position writes whose RHS chain roots at an ambient-undefined name: that statement IS the RHS root's throw (lowerExprStatement's first claim) and never touches the target's storage, so it cannot justify the decline
- A poisoned base a top-level class extends flushes its deferred diagnostics eagerly (collectClassShape): the heritage evaluates at module init and the base's fence is the compiler's own — Node defines the base fine — so the deferred trap compiled a binary that refused at startup where Node runs; leaf and derived-only poisoned classes keep the JS deferral
- Fixtures: corpus 2632 (unmappable written trap binding) and 2633 (ambient-rooted rewrite), diagnostics js-extends-poisoned-base (the eager report names the base's real blocker), errors.test pins for both deferral edges that must NOT report eagerly; order-parity baselines recorded for the new entries
2026-07-24 15:03:30 -05:00
Chris Tate 07c9a4128c node:stream/consumers compiles statically; createRequire erases to static requires
- text, json, and buffer settle real promises over the readable machinery with Node's settle timing and rejection set; arrayBuffer and blob fence with their representation reasons
- createRequire over this file's URL is compile-time plumbing: builtin specs become namespace imports, relative JSON bakes as validated documents, npm packages ride the require-condition graph, unresolvable names throw Node's catchable MODULE_NOT_FOUND
- module.builtinModules bakes as the compat target's exact list

# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	packages/compiler/src/frontend/lowering/lowerer.ts
2026-07-24 12:13:08 -05:00
Chris Tate 794b35a26e Subclasses override emit in the forwarding shape, monomorphized per event
- The forwarding shape — emit(event, ...args) whose rest appears only as super.emit(event, ...args) — lowers per dispatching event as a real hierarchy method, vtable-participating
- error events keep the throw-when-unhandled path through overrides; super.emit resolves to the nearest override ancestor, JS's chain
- Receivers above every override route through interned instanceOf dispatch; other emitter members and stream-rooted classes keep their fences with reasons stated

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-24 12:10:30 -05:00
Chris Tate 034195f588 Profile-declared integer boundary slots prove or refuse at compile time
- An interval-and-wholeness inference over the lowered IR proves integrality and range for every value reaching a declared i64/u64 slot, with loop-header widening precise enough that the bounded counter loop proves its exact range
- Refusals carry the slot path, the failed obligation with evidence, and the author's concrete fix as SC4021, SC4022, and SC4023
- Declared slots are both obligation and assumption: call sites check arguments, callee bodies assume their classes; integer returns emit exact machine-integer wrappers and inbound params trap through the structured host-contract family
- Math.trunc and Math.ceil compile statically

# Conflicts:
#	packages/compiler/src/library/profile.ts
2026-07-24 12:07:29 -05:00
Chris Tate 092220ec51 Collect-phase probes recover from poison; no-storage bindings claim statement declarators only
- The ambient-trap probe resolves as a side-effect-free probe: deferred collection diagnostics stay deferred, and the merged-namespace fence lands as a diagnostic instead of crashing the collect walk
- deadUnmappableBinding and the nullish family claim statement-position declarators only; for-init heads and catch bindings stay locals
- Trap classification declines written bindings; the seven 0.0.10 pass-to-fail fences restore byte-identical to 0.0.9 behavior
2026-07-24 12:06:37 -05:00
Chris Tate 2c8e31b1ff Rendered snapshots for the integer-boundary refusal codes; dynamic-surface fixture re-based
- library-int suite snapshots SC4021/SC4022/SC4023 rendered end to end (slot path, evidence, span, fix) — the diagnostics-corpus discipline for codes the executable lane cannot reach
- dynamic-surface diagnostics fixture swaps Math.ceil for Math.sqrt (trunc/ceil compile statically now) and its snapshot regenerates
2026-07-24 11:52:12 -05:00
Chris Tate 1c3324fb63 Fix both 0.0.10 crash sites: collect probes recover from poison, for-init declarators stay locals
- collectGlobals' trap/nullish/dead classification probes catch PoisonError per the per-probe recovery discipline: the cross-block merged-namespace SC1090 fence reached through the ambient-root walk recorded its diagnostic and then crashed the CLI mid-discovery (5 merged-namespace corpus tests, ice bucket)
- On a poisoned probe, JS files defer the recorded diagnostics to runtime fences (deferJsCollectionDiags) and the binding falls through to ordinary registration; the statement lowering re-fences at its own site
- The no-storage binding families (nullish, dead-unmappable) only claim statement-position declarators: an uninitialized for-init let (`for (let x; false;) {}`) was claimed as dead-unmappable, violating lowerVarDeclList's contract that a for-init declarator always lowers ("lowerer bug: for-init declarator resolved to a global", 2 corpus tests)
- Fixtures: corpus 2608 (for-init heads with the masking variants), diagnostics merged-namespace-collect-probe (the collection-reached fence stays an honest SC1090)
2026-07-24 11:38:39 -05:00
Chris Tate 6b6445ab90 Retire the "DOM" nickname in comments, internal docs, and message prose: the checked-dynamic tree / dyn values
- The ratified rename rider (world-unification §7 lane 5): "DOM" collided with the browser DOM and, post-lane-1, the structure is no longer a document tree. Comments and doc prose now say "the checked-dynamic tree" for the noun and "dyn" adjectivally; runtime symbol names (scr_dyn_*), the error.domClone libCall, and the real web DOMException family are unchanged.
- The few user-facing strings carrying the nickname move with it (the structuredClone cyclic message, the dc subscriber/publish fences, two emitter ICE texts); diagnostics snapshots regenerated (96 pass). One private compiler constant (DOM_PROTO_METHOD_NAMES -> DYN_PROTO_METHOD_NAMES) rides along.
- Mechanical sweep, 142 files, comments/prose only — no behavior change.
2026-07-24 11:14:58 -05:00
Chris Tate 071e5644fd Named capture groups compile end to end
- The pattern fence lifts: the vendored engine already executes named groups and named backreferences
- Match results project .groups at compile time as the checker's own record shape, with Node's key order, undefined-when-none, and null-read error texts
- Replace and replaceAll resolve $<name> templates with Node's exact edge behaviors; matchAll rows carry groups through for-of, drains, and destructuring
- Destructuring over index-signature records reads through the overflow map generally
2026-07-24 10:50:22 -05:00
Chris Tate 65bab3d8cd Named capture groups compile end to end
- lowerRegexLiteral drops the (?<name> fence: patterns and \k<name> backreferences run natively in libregexp, and .groups on exec/match/matchAll results desugars into a compile-time record projection over the honest slice — the checker's own Record<string,string> hybrid with overflow entries in group declaration order — whenever the regex is statically known (literals, const bindings, new RegExp("..."), matchAll rows via for-of bindings, stored drains, spread rows, element access)
- Node's exact shapes: a group-less regex answers .groups === undefined, nullable receivers throw Node's TypeError on the unit arms, ES2025 duplicate names project the participating alternative, and destructuring rides — flat, aliased, and the nested { groups: { year } } pattern — via the groups wrap plus a general overflow read for undeclared keys of index-signature shapes
- scr_put_substitution resolves $<name> against lre_get_groupnames with Node's edge answers: nonexistent and nonparticipating names substitute empty, $< stays literal when the pattern has no named groups, an unterminated $<name stays literal
- s.match(re) also claims a nullable string receiver (the Dict<string> member spelling) through the checked narrow; an untraceable .groups read keeps the SC2020 fence with a hint naming the traceable shapes
- Differential corpus 2604-2607 (both backends, SAN), runtime C substitution/backreference cases, refreshed diagnostics snapshots and surface manifest; corpus 1614's deliberate deferred fence moves to a Proxy wrap now that hybrid record reads compile
2026-07-24 10:41:37 -05:00
Chris Tate 4abd8e4fff node:module compiles: createRequire's static erasure and the baked builtinModules
- createRequire(import.meta.url) (or __filename / import.meta.filename) in a const binding is compile-time plumbing that emits nothing; each require call through it resolves per site against the binding's own file, and the inline createRequire(import.meta.url)("spec") spelling erases identically
- The erasure per target: a builtin spec makes the binding a namespace import in const clothing (namespace, destructured, and inline-member spellings all key the existing module tables, with the fallback-typed 'as typeof import(...)' cast idiom stripped); a relative .json document validates at compile time and lowers to json.parse over its baked text (JSON.parse's checked-dynamic unknown stance — exactly Node's value); a bare name nothing installed resolves compiles to Node's catchable MODULE_NOT_FOUND throw (the optional-dependency try/require pattern); an installed npm package embeds under --dynamic through the island's NEW require-condition entry (NpmGraphBuilder.addRequire — a dual package answers its CJS arm, exactly Node's createRequire) and reports the requires-dynamic diagnostic in a static build
- Dynamic specifiers, '#' project imports, relative non-.json targets, --npm-static opt-ins, and non-this-file bases all fence by name; the island-handle rescue skips the plumbing decls so --dynamic builds erase them too
- module.builtinModules bakes as Node v24's exact list (a fresh string[] per read, pinned to the compat target so emitted programs stay host-independent); isBuiltin and syncBuiltinESMExports fence per site with pointed hints, and the createRequire hint, its diagnostics snapshot, the surface manifest, and the npm-static offender wording follow
- Corpus 2606 pins json/builtin/builtinModules/MODULE_NOT_FOUND differentially on both backends and under ASan; the npm create-require fixture case pins cjszoo's module.exports, dual's require-condition CJS arm, and the missing-package rejection against Node
2026-07-24 10:32:20 -05:00
Chris Tate e47ad23c0c Subclasses may override emit in the forwarding shape, monomorphized per event
- class C extends EventEmitter may re-declare emit(event: string, ...args: unknown[]): boolean when the rest parameter only forwards through super.emit(event, ...args) in the method's own body; the body lowers once per event name as the real hierarchy method emit:<event> (vtable participation, (this, ...tuple) => bool ABI), with event bound to the name and the forward lowered to the runtime emit — or to the nearest override ancestor's specialization, JS's super chain.
- Emit sites dispatch by the whole-program rule: receivers at-or-below an override call the specialization directly (virtually when a deeper override exists); receivers above every override route through an interned helper that instanceOf-tests the topmost override subtrees and falls through to the runtime emit, so plain EventEmitter bindings holding override instances still dispatch like Node.
- super.<member> over an emitter-rooted base now lowers through the emitter spoke with static dispatch (an override at-or-below the lexical class never answers a super call); registering 'newListener'/'removeListener' listeners fences while a comparable class overrides emit (the runtime fires meta events internally, past the override), and every other emitter member keeps the surface fence with the forwarding-shape rule spelled out for emit.
- Corpus 2604-2609 pin both backends against Node: the logging override with upcast dispatch, two-level override chains with literal super.emit, once/off/prepend ordering and handled errors under an override, the unhandled-'error' throw through an override, the filtering override, and an emitter-subclass job queue; the diagnostics snapshot pins the three fences.
2026-07-24 09:37:57 -05:00
Chris Tate f2a629ae06 The --dynamic import() subgraph rides the shared cycle admission
- Extract preflight's benign-cycle admission (the cheap per-edge rule plus the declaration-only init window analysis, with Tarjan SCC and cluster-verdict memos) into makeCycleAdmission; preflight's static walk and appendDynamicImportModules now judge back edges identically, so benign cycles behind dynamic import() compile as Node cache hits instead of blanket SC1016.
- appendDynamicImportModules runs once in lowerToIr before any pass constructs: the old per-constructor run minted cycle refusals into the discovery pass, whose diagnostics are discarded by design, so inadmissible import() cycles silently compiled; refusals now land on the emit pass with the same narrowed reason (binding and line, or the offending top-level statement) the static walk mints.
- SC1016 gains a registry hint naming the admitted shape and the fix; diagnostics snapshots, order-parity baselines, and the surface manifest repin with it.
- Corpus 2604 (mutual class cycle across two modules), 2605 (three-module function ring with two back edges and a function-body-only value use), 2606 (import() into an admitted mutual-recursion cycle) — all byte-identical to Node through both backends; diagnostics fixture dyn-import-cycle-tdz pins the dynamic-path TDZ refusal.
2026-07-24 09:32:53 -05:00
Chris Tate c8c8b8634f Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- declare-erased roots make every chain rooted there a catchable ReferenceError at first touch, exactly Node's erasure semantics
- Never-read unmappable bindings emit nothing; nullish-cast bindings answer Node's exact TypeError on member access
- Assertion-supplied generic signatures register like annotations and monomorphize per call
- Stdlib generic members take the lib fence naming the member

# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-stmts.ts
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-24 08:06:21 -05:00
Chris Tate c48a21b9ef Object.create, array entries and keys iterators, and variadic-spread Object.assign
- Object.create(null) is a real null-prototype dictionary: inspect, toString, and deepStrictEqual answer Node's forms; the dynamic tier routes to the engine's own Object.create for live delegation
- Object prototypes fence by name in static builds — an own-copy stance is observably wrong under Node's empty-own-set answers and live proto mutations
- entries() and keys() walk any static array live in for-of heads and iterator-helper chains
- Object.assign(target, ...sources) packs spreads with JS's evaluation order before any copy, with V8's position-dependent error texts

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
#	packages/runtime/src/scr_json.c
2026-07-24 08:03:58 -05:00
Chris Tate 42c2725e5d Collapse string|object-family unions into the checked-dynamic representation, retiring their SC2009 fences
- mapType: a union with an 'object'/'unknown'/'{}'-flavored (dyn) arm beside only dyn-subsumable siblings — scalars, units, dynFrom-convertible records/arrays — maps to DYN wholesale; class/Map/Set/func/promise/regex arms keep their typed homes, and the SC2009 classifier now names the sibling that cannot ride the collapse instead of blaming the dyn arm.
- A dyn ELEMENT makes the whole array the DOM value (unknown[], object[], the plugins-slot (string | object)[]): the DOM array literal builds from TS annotations, .length on collapsed-array receivers is a DOM keyed read, element writes ride dyn.keySet (ARR index set/extend with undefined-hole padding), and for-of over dyn gets a pointed operation fence instead of the stale component story.
- Appendix-A repro 4 stops SC2009-fencing: an island plugins array wraps at the collapsed slot (dynFromJsval) and the element read reaches the loud keyed-read ladder until the routing lane arms it (dyncheck pin jsval-collapsed-union-slot).
- Corpus 2584/2585 differential both backends (guard ladder, option tables, unknown[] first-class; SAN clean); json-dyn snapshot repinned where the unknown[] declaration fence retires; conformance movers verified locally: discriminateObjectTypesOnly, unknownPropertiesAreAssignableToObjectUnion, unknownLikeUnionObjectFlagsNotPropagated.
- SEMANTICS 398 documents the collapse rule, the exact subsumable-arm domain, and the width/identity consequences (deep-copy entry, full-width JSON, per-site fences for non-subsumable values).
2026-07-24 02:54:47 -05:00