Architecture Decision Records
Durable architecture and implementation decisions for GocciaScript. New ADRs use the lightweight format from .agents/skills/grill-with-docs/ADR-FORMAT.md: a short title and enough context to explain what changed and why.
Executive Summary
- One decision per file — Each ADR captures the original decision context, date, area, and supporting links.
- Append-only history — Do not rewrite existing ADRs to match later implementation; add a new ADR that supersedes or refines the old one.
- Historical migration — The former single-file decision entries were moved here without changing their decision text.
Records
- 0001 — Number dual representation
- 0002 — Singleton special values
- 0003 — Opcode layout by hotness
- 0004 — Compiler-side desugaring
- 0005 — Register-based bytecode
- 0006 — Delphi mode performance parity
- 0007 — Constant-time virtual dispatch
- 0008 — Zero-cost FPC generics
- 0009 — Reject TFPDataHashTable
- 0010 — TOrderedStringMap for scope bindings
- 0011 — Scope creation via CreateChild
- 0012 — Eliminate global mutable state
- 0013 — Reject string interning
- 0014 — Bytecode and interpreter feature parity
- 0015 — Control flow records for break and return
- 0016 — IEEE 754 number representation
- 0017 — VMT dispatch for AST evaluation
- 0018 — TStringBuffer over TStringBuilder
- 0019 — Custom hash maps over TDictionary
- 0020 — Unified heap object hierarchy
- 0021 — GocciaScript bytecode VM unification
- 0022 — Opt-in automatic semicolon insertion
- 0023 — Bytecode VM profiling
- 0024 — Platform metadata via Goccia.build
- 0025 — Source map generation
- 0026 — BigInt primitive support
- 0027 — Heap trampoline call stack limit
- 0028 — BigInt typed arrays
- 0029 — Opt-in var declarations
- 0030 — Opt-in function declarations
- 0031 — Fetch-only async I/O
- 0032 — Per-engine realm isolation
- 0033 — Generator and async generator support
- 0034 — Layered Temporal time zone providers
- 0035 — Lexer hot path simplification
- 0036 — Direct binary precedence parsing
- 0037 — Parser hot path simplification
- 0038 — Opt-in strict-types enforcement
- 0039 — Split engine and runtime entry points
- 0040 — Compiler constant optimization
- 0041 — Silent-by-default script loaders
- 0042 — test262 LoaderBare harness
- 0043 — Opt-in traditional for loops
- 0044 — Purpose-built RegExp VM
- 0045 — ECMA-402 Intl implementation
- 0046 — Remove compat-all flag
- 0047 — Opt-in loose equality
- 0048 — Opt-in non-strict compatibility
- 0049 — Non-strict this coercion
- 0050 — Script-scoped non-strict assignment and delete
- 0051 — Strict-only test262 handling
- 0052 — Opt-in while loops
- 0053 — Default imports and exports
- 0054 — Reject UPX packing
- 0055 — Class-based runtime extensions
- 0056 — Explicit function declaration nodes
- 0057 — Canonical compat-asi flag
- 0058 — Centralized parser policy
- 0059 — Opt-in labels
- 0060 — Opt-in for-in loops
- 0061 — Annex B labelled functions
- 0062 — Explicit arguments object compatibility
- 0063 — Real source-phase imports
- 0064 — Gate ModuleSource provider
- 0065 — Shape-lite inline cache design
- 0066 — Lazy shape computation
- 0067 — Cross-realm shape dictionary mode
- 0068 — GocciaSandboxRunner with explicit seed baselines
- 0069 — Nested sandbox virtual filesystems
- 0070 — Shared string-to-number conversion
- 0071 — Reject symlinks in sandbox seed imports
- 0072 — Import Bytes with immutable backing
- 0073 — Opt-in ShadowRealm
- 0074 — Deferred bytecode call-stack frames
- 0075 — ShadowRealm full test262 conformance
- 0076 — Same-runner benchmark comparison
- 0077 — SameValueZero-keyed ordered store for Map and Set
- 0078 — Thread-local cleanup registry for managed threadvars
- 0079 — Keep speculatively-scanned tokens across parenthesized-group probes
- 0080 — FormatDouble first-hit precision scan
- 0081 — Reject shared value caches as a runtime optimization
- 0082 — Unify embedded-data caches onto a lock-free publication primitive
- 0083 — Migrate explicit per-thread cache clears into the thread-cleanup registry
- 0084 — Realm-owned prototype method hosts and object-reference threadvar audit
- 0085 — Defer broad Annex B support before 1.0
- 0086 — Parser errors for unsupported default syntax
- 0087 — AWFY cross-engine benchmark methodology
- 0088 — Reject broader property inline caches without AWFY transfer
- 0089 — Defer fixed-arity call specialization without end-to-end transfer
- 0090 — Web Tooling Goccia benchmark lane
- 0091 — Unified performance barometer with a frozen JetStream subset
- 0092 — Convert sandbox filesystem failures at the runtime seam
- 0093 — Engine-owned host environment
- 0094 — Virtual modules use the ordinary module loader
- 0095 — Custom bidirectional FFI ABI engine
- 0096 — Keep snapshot persistence behind a test-host adapter
- 0097 — Engine-owned capability audit seam
- 0098 — Portable exact numeric text conversion
- 0099 — Canonical UTF-16 text with explicit encoded-byte boundaries
- 0100 — Native binary64 execution with narrow semantic operations
- 0101 — Closed numeric scalar self-call frames
- 0102 — Explicit bounded FFI call descriptors
- 0103 — Layer untrusted execution boundaries
- 0104 — WHATWG EventTarget base for AbortSignal
- 0105 — Argument collections root their elements
- 0106 — Scope the sandbox-hardening programme to finding bugs over jailing them
- 0107 — Out-of-process sandbox execution protocol
- 0108 — Specifier-only module resolution errors
- 0109 — Engine-integrity faults are never guest-catchable
- 0110 — The growth gate collects before refusing, and store paths root their temporaries
- 0111 — Opt-in node_modules resolution
- 0112 — Native AsyncLocalStorage over continuation snapshots
- 0113 — Deterministic virtual timer queue
- 0114 — Unmanaged execution-context records
- 0115 — Immutable Blob history snapshots
- 0116 — Bounded string prefixes with reserved materialization capacity
- 0117 — A JavaScript-visible AST module exposes statement structure, not the AST
- 0118 — A parse result is measured in the file, not in what the parser was handed
- 0119 — A sandbox run is materialized by the host, on the host's command line
- 0120 — Native Test262 runner with isolated worker runtimes
- 0121 — Reserved for the capability-gated provider imports proposal in #1054, which was withdrawn unmerged and superseded by 0122; no record uses this number
- 0122 — One capability model: explicit allow/deny grants, trusted config, one runner
- 0123 — A nested engine run gets its own microtask scope
- 0124 — An unhandled promise rejection fails the run
- 0125 — A host can observe unhandled rejections through an engine hook
- 0126 — A bytecode call clears only what a new frame could read stale, binds thread state per outermost entry, and identifies its callee by exact class
- 0127 — The collector does not mark the registers of closed numeric frames
- 0128 — The RegExp VM has two resource limits with their own messages, and a greedy single-character loop keeps one backtrack entry
- 0129 — The collector also triggers on the heap manager's in-use total, and refusal stays on tracked bytes
- 0130 — The bytecode VM's stacks are charged to the memory budget, and a refusal is a catchable RangeError at the next instruction boundary
- 0131 — A bytecode caller frame's position is worked out when a stack is captured, from the instruction pointers the VM already saves
- 0132 — The call-stack records and the copy a stack growth makes are charged with the VM stacks
- 0133 — A module roots the values it holds directly
- 0134 — Direct eval compiles to a closure of its call site, and a sloppy eval's new vars live in a hidden variable object of the calling function
- 0135 — A call and a failed property read are located where V8 locates them
- 0136 — A module's function declarations are compiled while it is linked, and read its imports by name
- 0137 — Bootstrap shims load on first use through the engine's executor, and a bytecode engine builds no interpreter
- 0138 — Bytecode is the only way GocciaScript executes, and the engine owns its VM
- 0139 — A reload links its new version into the cached module record
- 0140 — An entry with a top-level import or export runs as a Module unless a source type is named
- 0141 — The tracked-bytes pressure trigger backs off above a live set that sits inside the reserve
- 0142 — A run waits until no extension has pending work, and an entry still pending then fails the run
- 0143 — A parse's arena owns its tree, a descriptor built for [[GetOwnProperty]] is its caller's, and a collector frees what it still manages
- 0144 — Read-only paths are enforced by the virtual filesystem, and refused writes are EROFS
- 0145 — The heap trigger follows growth since the last collection, and gated storage growth samples the heap
- 0146 — An embedder can answer an engine's fetch requests through its own transport
- 0147 — Marking nests at most 64 objects deep and spills the rest to an explicit mark stack
- 0148 — A paused run's heap can be snapshotted at an idle point and restored in a fresh engine, behind an embedder option with replay as the fallback
- 0149 — A fetch request's URL and a redirect's Location go through the URL parser
- 0150 — A heap snapshot restores module records whole, compiling each source module without linking it, and writes data modules as values
- 0151 — A deterministic sandbox run stamps its files with the deterministic clock
- 0152 — A heap restore commits built-in differences after every fallible step
- 0153 — Precompiled modules use the module linker
- 0154 — The VM dispatch loop uses no goto
- 0155 — Goccia.build reports a GC-stress build
- 0156 — fetch honours the redirect mode, and "manual" answers with the redirect response
- 0157 — Sandbox search is native and pinned by shared test vectors
- 0158 — The compiler infers operator results, proven consts and const function returns
- 0159 — A frame whose function never reads its arguments as passed gets no argument window
- 0160 — Concise arrow bodies make proper tail calls, and closed numeric frames give way to them
- 0161 — goccia:ast reports type-level declarations by kind, with no parse switch
- 0162 — The parse tree reaches expressions on request, through one child enumeration on the node
- 0163 — goccia:ast reports the committed token stream, as typed ranges, on request
- 0164 — A compile profile reports lex, parse and compile time and the compiler's work per source function
- 0165 — The dispatch loop keeps its hot arms first and aligned
- 0166 — Bundler names and output destinations are deterministic
- 0167 — Profile stack paths incrementally and aggregate directory captures on one thread
- 0168 — goccia:ast reports the specifiers and module path of type-only module statements, and nothing else about them
- 0169 — Win32 builds add audited SafeSEH metadata after linking
- 0170 — fetch reuses the connection within an origin and shares one OpenSSL context
- 0171 — The wait that drives one promise to settlement runs extensions' host work