Commit Graph
866 Commits
Author SHA1 Message Date
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 c1be329607 Static promises cross into the island as real engine thenables; Promise.all over mixed island/static entries runs the engine's own combinator
- jsvalIn (and the island-literal member path behind it) accepts STATIC promises whose fulfillment is in the reverse bridge's payload domain — void/number/boolean/string/'any', plus the NEW Promise<any[]> payload (SCR_ISLP_JSVAL_ARR: a native array of engine cells re-enters as a fresh engine array over the SAME engine values). scr_jsval_from_promise emits from jsMarshal in both backends; the IR validator admits the arm.
- Promise.all with MIXED entries where some entry is an island value (the withPlugins shape: Promise.all([loadBuiltinPlugins(), loadPlugins(plugins)])) lowers to the ENGINE's own Promise.all — island entries pass through, static promises bridge as thenables, and the combined result stays an island value the static side awaits through the existing island→static bridge. All-static tuples keep the typed fence hint.
- Corpus 2605 (unawaited static promise in an 'any' slot answers typeof "object" and awaits engine-side; the mixed-all + .flat() chain) is byte-identical to Node under both backends; SAN clean. Composite fulfillments (Promise<{...}[]>) stay fenced with the promise type named — the payload-domain residue.
2026-07-24 11:32:57 -05:00
Chris Tate 10696c70f8 Profile-declared i64/u64 integer slots: surface, wiring, wrappers, host-contract trap
- LIB_PARAM_CLASSES/LIB_RETURN_CLASSES grow i64/u64 (the prove-or-refuse pair); u8/u32/i32 stay param-only with the message updated to point at the landed machinery
- sidecar.integer_slots declares contract slots by sidecar path (Msg.count, Point.x, helpers.f.params[0], helpers.f.return); the projection spells each declared slot i64 (the frozen format-1 vocabulary — u64 is the stricter compile-time obligation over the same wire spelling), refuses non-number or unresolvable paths, and emits integer_slots as the resolved-decision attestation in declaration order
- compileLibrary runs the integer-boundary inference over export-map and sidecar-declared slots and refuses SC4021 (representability) / SC4022 (wholeness) / SC4023 (range), each carrying slot path, failed obligation with evidence, and the author's fix; u8/u32/i32 plumbing classes seed parameter shapes for free precision
- Both emitters grow i64/u64 wrappers: inbound params range-check through scr_library_i64_in/u64_in (the SC4012 host-contract trap — silent rounding past 2^53 is a coercion the author never wrote), returns convert exactly under the discharged proof
- Profile-loader suite pins the new surface; the pre-ask-4 pins updated
2026-07-24 11:29:30 -05:00
Chris Tate fe40a0f904 Refuse unknown profile root keys: a fences array pasted at the root was silently inert
- The ask-5 keys (fences/teachings/remediations) at the profile root refuse SC4001 with a pointer to their determinism.* home; every other unknown root key refuses with the standard unknown-field wording.
- The root goes strict everywhere, not just for the three ask-5 names: profiles pin per compiler release, so an unknown root key is a typo or a misplaced section (a misspelled sidecar would silently drop the contract), never forward-compat surface. All fixture and harness profiles load unchanged under the strict root.
- The check runs after the profile_format gate so a future format still refuses as the wrong format, and the interior of determinism keeps its reserved-and-ignored surface beyond the three ask-5 keys, both pinned by tests.
2026-07-24 11:26:34 -05:00
Chris Tate 165ca1f125 Project the attestation's ambient ground into the surface manifest so full fences imply deterministic
- The determinism attestation demoted on Date/perf_hooks/process libCalls that had no manifest entry, so the ask-5 §4 invariant (compiles under full fences => deterministic) could not be stated and the spec's own worked-example prefix stdlib.date. refused as unknown; AMBIENT_SURFACE_FNS now projects those families as permanent manifest ids (stdlib.date.*, node-builtin.perf_hooks.performance.now, node-builtin.process.*) with reach-witnessing detectors.
- BUILTIN_MODULE_FN_ALIASES folds the special-cased alternate spellings into their member detectors: the fs Buffer/fd/options/withFileTypes/watch-listener variants, the JS-ladder mkdtempSyncChk and lchmodSyncChk, crypto's composed randomBytesToString, and os.userInfo's userHomedir/userShell scalars.
- A parity test holds the two scans together over the exhaustive IrLibFn registry: every spelling LIB_NONDETERMINISTIC_PREFIXES demotes on must be deniable through a manifest-id fence, with the library-mode-refused and validate-then-fence residue pinned by name with reasons.
- The §4 invariant fixture and K14's worked fences now cover every attestation-known family, and a new K14 case pins the teeth: Date.now / process.env reads under the full-fence profile refuse SC4008 naming the entry instead of attesting deterministic: false post hoc.
- Sampling probes cover the new static entries (Date.now, process.pid, performance.now); surface-manifest.json regenerated (18 new entries, 336 total).
2026-07-24 11:25:42 -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 afa63317f8 Record order-parity baselines for the new corpus fixtures; the dataview fence names both .buffer consumers
- order-parity.json gains 2604-2607 entries (SCRIPTC_UPDATE_BASELINES re-record)
- The '.buffer' SC1090 message now names new DataView(x.buffer, ...) and Buffer.from(x.buffer, ...) as the composed positions and states the subarray-view alternative; snapshot regenerated
2026-07-24 11:13:38 -05:00
Chris Tate 9e21ef5cc3 Record order-parity baselines for the cycle and named-group fixtures 2026-07-24 11:12:25 -05:00
Chris Tate b98d726382 for-of, destructuring, and spread over island values held in 'unknown' drain the engine's own iterator; engine arrays exit into 'any[]' slots
- The iteration pack's JSVAL arm is ARMED (scr_dyn_iter_pack, scr_dyn_arr_push_spread, scr_dyn_pack_push_spread_iter route through the new iter_drain ops row): the ENGINE's iterator protocol drains into a fresh DOM array — engine arrays, generators, Maps, Sets, and Symbol.iterator implementations step exactly as Node runs them, elements wrapping back scalar-normalized.
- for-of over checked-dynamic values LOWERS now (both backends): the source packs once through dyn.iterPack and a hidden index loop binds each element as a dyn value (new dyn.arrLen/dyn.arrAt accessors) — an eager snapshot, the matchAll stance. Pattern heads, pre-declared and var heads, and labeled break/continue all ride the desugar. Non-iterables throw V8's for-of CallPrinter spellings, threaded into the engine-side guard.
- The validated exit gains the jsval-element-array arm (scr_jsval_exit_jsval_arr, both backends): an 'any' value exits into 'any[]'- and 'any[] | undefined'-declared slots Array.isArray-gated, elements BY REFERENCE (identity crosses, the spine is a snapshot) — the withPlugins loadPlugins(plugins) boundary, the driver frontier's first named poison.
- Corpus 2604 and npm case jsval-iterate (package-minted Set/Map/generator/Symbol.iterator) are byte-identical to Node under both backends; dyncheck's jsval-iterate pin flips from the retired island fence to the armed answers; SAN clean over the drain/wrap/exit churn.
2026-07-24 11:04:10 -05:00
Chris Tate 7fe38cc085 Island-literal member fences follow the member's shape: data members fence at the READ, not as callables
- A member value an island ('any'-typed) object literal cannot lower or marshal used to become a call-time fence CLOSURE regardless of shape — a data-shaped member (the withPlugins plugins: slot) then answered typeof "function" and .length 0 silently, re-minting the fence box entry 394 retired, and downstream errors blamed the wrong thing (plugins.flatMap is not a function).
- Data-shaped members now define through the engine's getter machinery instead: READING the member throws the captured diagnostic verbatim — the honest granularity, since using the value is exactly what cannot be answered.
- Function-shaped members (syntactic lambdas/methods, checker-callable values — the doc-builder aggregate and commander wrapper shapes) keep the ratified call-time closure, as do conditional-spread literals where getters cannot combine.
2026-07-24 11:03:23 -05:00
Chris Tate ced8f52207 The integer-boundary inference over the lowered IR, with the reference corpus as IR-level unit tests
- library/int-infer.ts: interval+wholeness+maybeNaN+spelling domain, JS-exact transfer functions (ToInt32/ToUint32 bitwise contract, JS remainder sign, Math rounding family discharges wholeness), branch refinement on both edges (ordered comparisons exclude NaN; wholeness sharpens strict bounds), loop-header-only threshold widening with body-edge recovery
- Structured-IR fixpoint: scoped walk with join points at if/switch/try merges, per-loop header fixpoint plus a collect pass, break/continue env folding, transitive mutated-global summaries so direct calls havoc precisely
- Interprocedural v1: intraprocedural with declared-slot summaries (declared params seed their class shape; declared returns are assumed at call sites, proven at their own returns)
- numLit carries the source spelling exactly when a decimal integer literal does not round-trip f64 (the representability witness)
- All 16 reference-corpus cases pass at the IR level, the bounded counter loop proving exactly [0, 9]
2026-07-24 11:03:14 -05:00
Chris Tate a44f5b3994 Renumber the named-group fixtures past the cycle fixtures 2026-07-24 10:50:49 -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 6c3de6befe Benign import cycles compile under the dynamic tier too; refusals narrow to the binding
- The dynamic-import subgraph judges back edges with the same admission engine as the static graph; its refusals now reach the build instead of being discarded with the discovery pass
- TDZ-shape reads and non-inert top levels refuse naming the binding or statement and its location, not the whole cycle
- Evaluation order stays Node's postorder with back edges as cache hits, pinned by the parity baselines
2026-07-24 10:50:07 -05:00
Chris Tate f28c271784 Math.trunc and Math.ceil compile statically
- New libCalls math.trunc/math.ceil: C trunc()/ceil() are the JS operations, llvm.trunc.f64/llvm.ceil.f64 on the LLVM lane
- Joined STATIC_MATH_FNS (checked before the island table) — they are ask-4's wholeness-discharge operators, so library builds need them compiled
- Surface manifest regenerated: stdlib.math.trunc/ceil move to static
2026-07-24 10:45:01 -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 08de805fb5 Record order-parity baselines for the emit-override fixtures
- Purely additive: the eight new entries (corpus 2604-2610 and the ee-emit-override diagnostics program) join baselines/order-parity.json; no existing baseline moves.
2026-07-24 10:34:13 -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 6f2de332db Readable push encodings: defaultEncoding decodes string pushes, explicit literals override, unknown spellings take Node's ERR_UNKNOWN_ENCODING
- The Readable defaultEncoding option lowers to a push-side decode default (Buffer.from(chunk, enc)); push(chunk, enc) with any literal encoding decodes per call and overrides the default, utf8 included
- Unknown literal encodings become Node's runtime TypeError — at construction for the encoding/defaultEncoding options (nodeThrowExpr, the ladder pattern), at the call for push
- on/once/off admit Node's ignored extra arguments when they are pure reads (the suite's stray-third-argument shape); effectful extras keep the fence
- readable.pushStrEnc/readable.pushEncoding land in both backends; corpus 2607-stream-push-encodings pins the decode, override, and both throw sites differentially
2026-07-24 10:30:47 -05:00
Chris Tate 567af934f1 Release 0.0.10 v0.0.10 2026-07-24 10:03:40 -05:00
Chris Tate 2785bb09f9 Stream ticks ride the user nextTick queue: FIFO with process.nextTick, Node's order
- Every deferred stream emission posts a raw marker on the user tick queue (scr_next_tick_raw) and each marker dispatches exactly one stream tick, so resume_/emitReadable_/endReadableNT interleave with user nextTicks in enqueue order — in Node they are the same queue
- Cures the setEncoding-with-buffered-bytes shape: a nextTick registered after on('data') now observes the flowed chunks, and stream emissions run before microtasks at each checkpoint instead of after
- The loop station stays as the drain for anything a marker never reached and as the uncaught-throw cleanup of undelivered ticks
- Corpus 2606-stream-ticks-are-nextticks pins the interleavings (buffered decode, split multi-byte char, tick/data/tick ordering, end-after-tick) differentially on both backends
2026-07-24 09:47:43 -05:00
Chris Tate c1a800dbad The JS lane's unannotated emit override lowers in the forwarding shape
- An unannotated event parameter (checker any) in a JS source binds as the string it always is — event names are compile-time literals program-wide — so CJS emitter subclasses wrapping emit compile like their TS twins; TS sources still spell the annotation.
- Corpus 2610 pins the CJS override with filtering, instance state, and checked-dynamic listeners against Node on both backends.
2026-07-24 09:41:21 -05:00
Chris Tate 6e9467821f Stream emitters model Node's kShapeMode: pre-created event keys govern eventNames order
- Stream constructors reserve their class's known _events names (Readable close/error/data/end/readable, Writable close/error/prefinish/finish/drain, Duplex the union) — empty reserved buckets are invisible to every read but hold the first-registration rank, so eventNames lists them before earlier user events, exactly Node's shape optimization
- Shape mode reproduces Node's removal split: removeListener keeps an emptied name's rank (events[type] = undefined), the named removeAllListeners deletes a populated key (and resets wholesale when the count drains, kShapeMode quirk included), the no-argument wipe resets everything and leaves shape mode
- Corpus 2605-stream-event-names pins the order, rank survival, drain, and wipe behaviors differentially on both backends
2026-07-24 09:40:01 -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 135aec0383 Buffer slice/subarray become true aliasing views; typed arrays gain element fill and Buffer.from(x.buffer)
- scr_bytes_subarray: a same-elem view retaining the owner (the DataView rule, chain depth 1); subarray() and Buffer's slice() lower there, plain typed arrays' slice() keeps the copy — mutations through views are visible both ways now, byte-exactly with Node
- Buffer.from(x.buffer, byteOffset?, length?) lowers as the dataViewNew view construction (the syntactic .buffer peel); the fallback lib gains the ArrayBuffer overload
- fillElem: TypedArray.prototype.fill on non-u8 receivers, per-element coercion with slice-clamped indices; u8 keeps the Buffer fill family
- Both backends emit the new intrinsics; corpus 2604-bytes-views pins aliasing, offset composition, swap-through-slice, clamping, and fill coercion differentially
2026-07-24 09:28:37 -05:00
Chris Tate 417ece6724 node:stream/consumers compiles statically: text, json, and buffer settle real promises
- scr_sc_text/json/buffer accumulate every chunk through a native 'data' listener (string chunks as their utf8 bytes, the Blob rule) and settle at the terminal point through the finished watcher — Node's own eos timing, verified byte-exact including resolution landing after 'close'
- Rejections match Node's set: the stream's error, ERR_STREAM_PREMATURE_CLOSE on an early close, json's SyntaxError on malformed text, and the async-iterable TypeError for a stream with no readable side
- lower-stream's stream/consumers arm lowers text/json/buffer over one lowered stream argument (json answers a checked-dynamic promise — the JSON.parse unknown stance); arrayBuffer and blob fence module-qualified with pointed hints, since neither value has a representation
- Both backends emit sc.text/sc.json/sc.buffer; the trio joins the may-throw seeds, the loop-liveness set, and moduleUsesStream's link gate; the SC1010 hint, its snapshots, and the surface manifest gain stream/consumers
- Corpus 2604/2605 pin the consumers differentially on both backends and under ASan + RC audit: split multibyte sequences across chunks, encoded string-chunk streams, settle ordering, empty streams, and all four rejection arms
2026-07-24 09:27:44 -05:00
Chris Tate c8f2bb2cbe Profiles deny surfaces by manifest id: fences, generalized teachings, and the determinism invariant
- A fences array denies manifest-identified surfaces reached by the compiled library graph: SC4008 names the id and surface with the profile's teaching as an attributed note; unreached fences leave emission byte-identical
- Fence evaluation walks the dead-stripped module IR — the same ground as the deterministic attestation, making the full-fences invariant structural
- Teachings generalize to any refusal code or manifest id; unknown or unpoliceable fence selectors refuse loudly rather than sit inert
- Teaching and remediation strings take the ratified byte constraints and the 512-byte cap everywhere
2026-07-24 09:18:32 -05:00
Chris Tate 72f01a3cea Fence ambient surfaces by manifest id: SC4008 denials, teachings on any refusal
- Profile fences (determinism.fences) resolve at load against this release's surface manifest: exactly one of id/prefix, unknown selectors and unpoliceable static entries refuse SC4001 loudly (ratified strictness), folded constants exempt under a prefix only
- A fenced static surface reached by the compiled library graph refuses SC4008 at the reaching construct, over the same dead-stripped module IR the deterministic attestation reads; unreached fences emit byte-identical code
- The SC4004/SC4005 teaching rider generalizes: one decoration pass attaches profile text to any library refusal by code, manifest id, or fence coverage, rendered as a visibly-attributed note line the renderer prints after the hint
- Teaching/remediation strings now refuse every control byte below 0x20 except newline and cap at 512 UTF-8 bytes; fence remediations feed profileRemediation through covered entry codes
- K14 fence conformance on both emissions, fence-family SC4001 exact-path tests, and the ask-5 §4 pin: the full-fence worked-example profile compiles and attests deterministic: true
2026-07-24 09:11:50 -05:00
Chris Tate 4564681774 The stream-ladder fixture tears its socket down deterministically
- The accepted server-side socket destroys on connection: relying on the peer's destroy to drain the server's connection count races the event loop under slow environments
2026-07-24 09:04:54 -05:00
Chris Tate 88ff8eaa60 Library links stay timer-free with the real fs.exists; the regex size arm re-bases
- fs.exists' async fire compiles out under SCR_LIB and the compile-time scan refuses the surface with the event-loop family
- The regex-free size ceiling re-bases +8KB for the engine-handle arms and validation-ladder strings
2026-07-24 08:33:32 -05:00
Chris Tate 8cb44e4345 Record order-parity baselines and the island destructuring fence for the merged train
- Baselines for the renumbered fixtures, purely additive
- iterPack loud-fences engine-held sources instead of claiming not-iterable on values Node iterates
2026-07-24 08:15:35 -05:00
Chris Tate b392ff6af0 Concurrent test-suite flavors stop sharing scratch directories
- Library, npm-static, provenance, and diagnostics suites key their cache dirs by suite flavor, so concurrent plain and sanitized runs never race ar/ranlib
- The differential oracle publish is atomic: write-tmp then rename, so a concurrent reader can't see a truncated file
- Proven with three consecutive concurrent both-flavor rounds, zero failures
2026-07-24 08:09:34 -05:00
Chris Tate a16ee028ee Keyed writes land on dynamic receivers; array destructuring walks dyn sources
- Evolving-any receivers holding checked-dynamic values take keyed writes with ToPropertyKey-exact key spelling; dyn-keyed reads gain the same key stringification
- Array destructuring over dyn sources packs via the spread walk with V8's exact non-iterable TypeError wordings
- The dyn keyed-write miss error appends the primitive's rendering, matching Node

# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-exprs.ts
#	packages/compiler/src/ir/validate.ts
2026-07-24 08:09:34 -05:00
Chris Tate e4bd46c826 fs, net, dgram, tls, and stream misuse throws Node's validation ladders
- Per-API validators in the checked-dynamic lane run Node's argument checks in Node's order, then the real operation or a rendered fence thrown after validation
- fs.exists is genuinely async including the synchronous-false wart; mkdtempSync and lchmod are real
- Destructured sub-namespace bindings resolve as their module; function-armed unions cross into the checked-dynamic world
- dgram state errors carry their ERR_SOCKET codes

# Conflicts:
#	packages/compiler/test/ts7/baselines/order-parity.json
2026-07-24 08:07:01 -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 6a4376c9ee The string-or-object union family collapses into the checked-dynamic world
- Unions carrying an object/unknown-flavored arm beside dyn-subsumable siblings map to dyn wholesale; typeof and isArray narrowing dispatch natively
- Class instances, Maps, Sets, functions, promises, and regexes stay excluded so typed semantics never degrade; the component fence names the blocking sibling
- A dyn element makes the whole array the DOM value; element reads and writes ride the routed machinery

# Conflicts:
#	tests/harness/dyncheck.test.ts
2026-07-24 08:00:33 -05:00
Chris Tate 613d205a25 Routed operations over engine-held dyn values: keyed access, calls, method dispatch, and nullish tests
- Keyed reads and writes, dynCall, and method dispatch on engine-held values route through the engine, which runs its own prototypes JS-exact
- One generic host shim carries boxed dyn functions into engine calls over the uniform thunk signature
- Nullish coalescing and optional chains unfence for every dyn value, engine-held or not
- Mixed island-vs-DOM deepStrictEqual comparisons loud-fence instead of minting a fabricated failure
- The formatter driver's support slice runs past its flatMap wall
2026-07-24 07:59:12 -05:00
Chris Tate f4ebb76698 The checked-dynamic world gains an engine-handle kind
- SCR_DYN_JSVAL wraps a retained engine cell; scalars normalize to native kinds at the boundary, so only objects, arrays, and functions ever wrap
- typeof, truthiness, strict equality, String(), and Array.isArray/instanceof-Error narrowing route through the engine; every unarmed walker loud-fences by name
- The identity round trip: an engine value crossing into dyn and back is the same engine value, by reference
- The silent-wrong-answer fence box is unreachable from engine members; the LLVM jsMarshal dyn case now mirrors C
2026-07-24 07:58:10 -05:00
Chris Tate 1f9badf573 Key harness scratch dirs by suite flavor so concurrent plain/san runs never share build dirs
- library-mode, library-contract (cache dir plus the tmpdir npm root), and library-asyncfree now carry a plain/san path segment: both flavors run the same ordinary builds, and the nightly's concurrent both-flavor recipe raced ar/ranlib in the shared dirs ("can't open file ... lib.lib.a"); the per-build -san tag stays, it distinguishes explicitly-sanitized builds within one suite
- the same flavor split lands on npm-static's fixed npm-static-workspace dir (the two flavors even build with different sanitize flags there), provenance's shared outDir, and diagnostics' $TMPDIR/scriptc-diag scratch (some entries emit backend artifacts before failing)
- dec-oracle publishes in the differential/llvm/linux/windows lanes are now atomic (write tmp, rename): the content-keyed path is shared across flavors and lanes, and a truncate-and-rewrite could expose a torn oracle to a concurrent reader
- every other suite already keys its build dirs by a content hash that includes the flavor, and the CAS build cache folds the sanitized flags into its key, so those stay as they are
- proof: three consecutive concurrent both-flavor library-mode runs from a wiped cache all pass 45/45 with zero ar/ranlib failures, and a concurrent both-flavor run of all four library suites passes 125/125 in each flavor
2026-07-24 06:08:45 -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
Chris Tate abffa3e251 Bring in the SCR_DYN_JSVAL engine-handle kind (world-unification lane 1) — the union collapse needs dyn to hold engine values 2026-07-24 02:54:26 -05:00
Chris Tate abbfcaec1a Corpus fixtures pin the ambient-trap, inert-value, and nullish-binding families
- 2578 catches the ReferenceError from ambient-rooted optional chains, generic call signatures, method chains, instantiation expressions, trap declarations, and assignment RHS roots
- 2579 pins the uncaught module-level trap: stdout up to the throw, exit 1, dead code past the unwind
- 2580 pins dead unmappable bindings, write-only bindings, and the assertion-shaped generic arrow (called on two instantiations)
- 2581 pins nullish generic bindings: the TypeError messages for null and undefined units, propagation, and nullish-to-nullish writes
- order-parity baselines re-recorded for the four new entries; byte-identical on both backends
2026-07-24 02:29:56 -05:00
Chris Tate e81e4f5ab5 Ambient roots trap whole chains; dead and nullish generic bindings lower to their runtime truth
- ambientUndefVarRootOf walks calls, `new`, optional chains, assertions, instantiation expressions, and tagged templates, and accepts `declare function` and trap-binding roots: Node erases the declaration, so the root read IS the crash — lowerCall, member reads, assignments, and variable declarations lower the whole statement to the catchable ReferenceError, arguments never evaluate, and the never-initialized binding registers as a trap whose references can never run
- never-read unmappable bindings with value-only initializers and writes emit nothing: Node builds the value and drops it, so `var xs2: typeof Array;` and the write-only `var f2: { <T, U>(x: T, y: U): T }` compile instead of fencing on a type the program never consumes
- null!/as-any bindings whose types keep generic signatures (or intern empty record shapes) register no storage; generic-method calls and member reads through them compile to Node's exact TypeError, nullish-to-nullish flows to nothing, heritage-clause uses decline the family so mixin resolution keeps its fence, and checked-dynamic (`any`) bindings keep their dyn story untouched
- assertion-shaped generic arrows (`< <T>(x: T) => T >((x) => x)`) register like annotated bindings and monomorphize per call; stdlib generic members (Promise.then, Object.defineProperty) take the SC2020 lib fence, and Function.prototype.apply/call/bind on compiled function values name the direct-call spelling
- diagnostics fixtures that used `declare` to conjure unconstructible values now conjure them by cast so each fence family keeps its pin; snapshots re-recorded, the deferred checker-panic diagnostic now flushes at its first reference as designed
2026-07-24 02:29:48 -05:00
Chris Tate 346a73b7f1 Record order-parity baselines for the tls and stream ladder fixtures 2026-07-24 02:29:09 -05:00
Chris Tate b49134cdcb tls option bags and the stream misuse arms throw Node's validation ladders
- The tls server/secure-context walker validates every present typed option before the pem walk and its fences: the ciphers/passphrase/ecdhCurve string contracts, the engine trio's string-or-null-or-undefined clause, handshakeTimeout/sessionTimeout number contracts with validateInt32's exact range text, ticketKeys' view contract and exactly-48-bytes value ladder (Received renders the byte length, Node's shape), and minVersion/maxVersion's ERR_TLS_INVALID_PROTOCOL_VERSION with %j rendering. Literals carrying a validated key ride whole to the walker (JS sources), and createSecureContext gains the runtime-record form (tls.createSecureContextDyn) — a validated { cert, key } bag builds the real context.
- tls.connect validates checkServerIdentity's function contract with Node's presence semantics (a PRESENT key replaces the builtin verifier even holding undefined — the defaults spread); tls.getCACertificates walks validateString plus the documented name set before its fence.
- stream.finished/stream/promises.finished over a provably-non-stream value throw Node's isNodeStream ERR_INVALID_ARG_TYPE; socket write's two-argument encoding form implements the stream_base typecheck (write(string, 'buffer') is the synchronous "Second argument must be a buffer") with utf8 spellings as the plain write; Readable.toWeb's type option answers Node's one-of ladder.
- Unions with boxable FUNCTION arms now cross into the DOM (canConvertToDyn + both backends' union-to-dyn walkers) — the invalid-input probes iterate [1, null, () => {}, true]; scr_dyn_specific_type renders functions as Node's `function ${name}` (anonymous keeps the trailing space).
- Corpus 2581 (tls) and 2582 (stream, both backends) pin the ladders byte-identically vs Node.
2026-07-24 02:27:38 -05:00
Chris Tate 10c4613f1f net and dgram misuse throws Node's validation ladders instead of fencing
- net.createServer with a provably-non-object argument, connect option bags (literal, computed-key, and runtime-record spellings) walking Node's Socket-constructor order — the objectMode trio's ERR_INVALID_ARG_VALUE, validatePort's ERR_SOCKET_BAD_PORT, host's string and autoSelectFamily's boolean contracts — and the autoSelectFamilyAttemptTimeout range ladder (validated and then inert, the single-dial simplification's sibling); setDefaultAutoSelectFamilyAttemptTimeout validates from 1 and keeps Node's sub-10ms floor.
- listen's and dgram.createSocket's options.signal answer validateAbortSignal's ERR_INVALID_ARG_TYPE for provably-non-AbortSignal values; plausible signals keep the fence (abort-driven close has no lowering yet).
- dgram.Socket.send replicates Node's signature shuffle over DOM arguments: slice type/bounds contracts (ERR_BUFFER_OUT_OF_BOUNDS over Buffers, subarrays, DataViews, and strings), the buffer-list per-element contract with the list as the Received tail, the unconnected port ladder with the specific-type tail and trailing period, the null/undefined-only address gate, and the connected-state ERR_SOCKET_DGRAM_IS_CONNECTED arms — a fully-validated unconnected single-payload send RUNS; callback/list/connected forms meet the post-validation fence. The dgram state errors now carry Node's ERR_SOCKET_* codes, and dgram bind/connect/close callbacks accept checked-dynamic wrappers through the dynCheck boundary.
- Corpus 2579 (net, both backends) and 2580 (dgram) pin the ladders byte-identically vs Node; order-parity baselines re-recorded.
2026-07-24 02:02:52 -05:00
Chris Tate cbb567f901 Fixtures for the jsval→DOM crossing: rows 1-3 differential, the fence ladder pinned
- corpus 2578 (TS) and 2579 (JS, the JSDoc @param {object} repro shape) pin typeof/truthiness/String()/===, scalar normalization, representation-free narrowing, and the identity round trip against Node on both backends
- npm case jsval-into-dyn drives the prettier-plugins shape over a real package surface (the new plugstub fixture package: any-declared exports carrying engine functions)
- dyncheck jsval-* tests pin every loud fence (keyed read/write, member calls, 'in', destructuring, JSON.stringify, structuredClone, Object.keys, deepStrictEqual, inspect) plus the RC-clean catch-and-continue unwind under the SAN lane
- the errors.test.ts SC1101 pin flips to the now Node-exact behavior (the retired "one unbridgeable mix": the wrapped argument compiles and the unbound-this read throws Node's own TypeError); ts7 order-parity baselines record the three new fixtures
2026-07-24 01:59:28 -05:00
Chris Tate 096a562df6 jsval→dyn edges convert instead of fencing: the dynFromJsval crossing in both backends
- coerceToExpected's expected-dyn/actual-jsval edge, the lib-boundary jsval-into-dyn-slot fence, DOM object-literal members, and index-signature value slots all wrap through the new dynFromJsval IR node — retiring the fence-closure silent-wrong-answer box for jsval members (typeof answered "function" and .length answered 0 there; the box remains only for non-jsval unconvertibles like unboxable-signature functions and func-carrying records)
- both backends emit the wrap (scr_dyn_from_jsval, borrowed operand, +1 out, infallible), and the LLVM jsMarshal gains the missing dyn case (scr_jsval_from_dyn) so dyn→island crossings — the identity unwrap included — stop detouring through the JSON serializer
- the emitted display (sc_ds), keyed-read (sc_dyn_key_get), and iter-N walkers grow JSVAL arms in both backends: engine-routed String(), the loud keyed-read fence with the key named, and the iteration fence instead of a false not-iterable claim
- dynTest/dynHasKey route the representation-free narrowing tests through the engine (typeof object/function, Array.isArray, instanceof Error) and fence 'in' loudly; validator rules pin dynFromJsval to exactly a jsval operand and a dyn result
2026-07-24 01:59:01 -05:00