# Decision Log

Chronological record of key architectural and implementation decisions, newest first. Each entry has a date, an area tag, a summary, and links to the PR and/or detailed documentation.

## How to add an entry

```markdown
**YYYY-MM-DD** · `area` — Summary of the decision and why. [#PR](https://github.com/frostney/GocciaScript/pull/N). [detail link](file.md#anchor).
```

- **Date** — When the decision was made
- **Area** — One of: `build`, `bytecode`, `data-structures`, `engine`, `fpc-platform`, `interpreter`, `parser`, `runtime`, `strings`, `testing`
- **Summary** — 1–3 sentences explaining the decision and why
- **PR / Detail** — Link to the pull request and/or the `docs/` section where the full rationale lives

> **Entries are immutable.** Each entry records what was decided at that point in time. Do not retroactively update entries to match the current implementation. If the implementation changes, add a new entry. Links may be updated if targets are renamed.

---

**2026-05-20** · `parser` — Function declarations use explicit statement nodes. Replaced the temporary `IsFunctionDeclaration` flag on `TGocciaVariableDeclaration` with `TGocciaFunctionDeclaration` and `TGocciaExportFunctionDeclaration`, while keeping function expressions on `TGocciaFunctionExpression`. This keeps declaration paths readable and maintainable, and gives parser, interpreter, bytecode, export, hoisting, and redeclaration logic exact AST semantics instead of inferring declaration intent from variable-declaration shape.

**2026-05-18** · `runtime` — Runtime host features are now class-based extensions installed onto `TGocciaRuntimeCore`, rather than a broad `TGocciaRuntimeGlobals` selector. `GocciaScriptLoader` / `GocciaREPL` apply `TGocciaLoaderRuntimeProfile`; `GocciaTestRunner` layers `TGocciaTestingLibraryRuntimeExtension`; `GocciaBenchmarkRunner` layers `TGocciaBenchmarkRuntimeExtension`; FFI remains an explicit `--unsafe-ffi` extension. Feature-specific APIs such as fetch host restrictions and JSON5/TOML/YAML globals injection live on concrete extensions and are reached through runtime dispatch, keeping the base runtime extension generic and making smaller host surfaces easier to link. [architecture.md](architecture.md#main-layers). [embedding.md](embedding.md#runtime-extensions).

**2026-05-18** · `build` — Do not add UPX packing to the default release pipeline. A local binary-size spike showed large compressed file-size reductions after the existing production build and CI artifact strip, but macOS required UPX packing to be forced and the packed executable was killed at runtime; the remaining cross-platform upside does not outweigh code-signing, antivirus/reputation, and support friction for public release artifacts. Continue relying on FPC production flags plus release stripping, and pursue source-level/runtime-surface reductions instead. [build-system.md](build-system.md).

**2026-05-18** · `engine` — Default imports and exports are supported across interpreter and bytecode script-module execution, including `import name from`, default-plus-named or namespace imports, `export default` expressions, and named default class/function declarations. "Named exports only" is no longer a language restriction; it remains the project convention for internal examples and new project code because named exports keep call sites explicit and refactors easier to review. [language.md § Modules](language.md#modules). [language-tables.md](language-tables.md).

**2026-05-17** · `engine` · [#651](https://github.com/frostney/GocciaScript/pull/651) — Added opt-in `while` and `do...while` JavaScript compatibility behind `--compat-while-loops` / `"compat-while-loops"` in interpreter and bytecode modes. The flag is separate from `--compat-traditional-for-loop`, stays off by default with the existing parser warning/no-op posture, and supports condition testing, `break`, `continue`, `return`, timeout checks, instruction limits, and module/config propagation when enabled. [language.md](language.md#while-and-dowhile).

**2026-05-17** · `testing` · [#645](https://github.com/frostney/GocciaScript/pull/645) — The test262 runner now injects a `"use strict"` directive for `onlyStrict` tests while still passing `--compat-non-strict-mode` to Script tests. This lets strict-only coverage exercise strict `arguments`, `with`, assignment, `delete`, and `this` semantics without losing the parser/runtime support needed for non-strict Script tests. [test262.md](test262.md#strict-mode).

**2026-05-15** · `engine` · [#645](https://github.com/frostney/GocciaScript/pull/645) — Tightened `--compat-non-strict-mode` to Script-source semantics and completed the assignment/delete behavior required by #586. Failed ordinary object/global writes now silently preserve the assigned expression value only in non-strict Script mode, identifier delete can remove configurable global object properties, modules ignore the compatibility flag for strict `this`/`arguments`/parser/compiler decisions, bytecode format v31 adds loose set/global-delete opcodes, and the test262 runner now passes the flag only to non-`onlyStrict` Script tests. [language.md](language.md#non-strict-assignment-semantics). [language.md](language.md#delete-semantics). [bytecode-vm.md](bytecode-vm.md#compatibility-scope-helpers). [test262.md](test262.md#strict-mode).

**2026-05-15** · `engine` · [#645](https://github.com/frostney/GocciaScript/pull/645) — Completed the non-strict compatibility surface from #586 by making regular function calls coerce nullish `this` to `globalThis` behind `--compat-non-strict-mode` in interpreter and bytecode modes. Bytecode function templates now serialize their strict-this mode, the bytecode format is v30, the CLI/config tests cover the behavior, and the arguments-object helper lives in the value namespace as `Goccia.Values.ArgumentsObjectValue`. [language.md](language.md#this-binding-strict-mode-semantics). [bytecode-vm.md](bytecode-vm.md#compatibility-scope-helpers). [value-system.md](value-system.md).

**2026-05-15** · `engine` · [#645](https://github.com/frostney/GocciaScript/pull/645) — Added opt-in ES non-strict compatibility via `--compat-non-strict-mode` / `"compat-non-strict-mode"` for `arguments` objects, `with` statement execution, and legacy `delete` return values in both interpreter and bytecode modes. When enabled, ordinary functions, methods, accessors, and generators receive an unmapped `arguments` object while arrows keep lexical lookup; `arguments` stays an ordinary identifier that can be shadowed. `with` uses object environment lookup with `Symbol.unscopables`, identifier calls preserve the object as `this`, bytecode captures hidden with-object locals so closures created inside `with` keep resolving correctly, and `delete` returns `false` for non-deletable bindings/properties instead of throwing. [language.md](language.md#arguments-object). [language.md](language.md#with-statement). [language.md](language.md#delete-semantics). [interpreter.md](interpreter.md#scope-chain-design). [bytecode-vm.md](bytecode-vm.md#compatibility-scope-helpers).

**2026-05-11** · `engine` — Added opt-in loose equality compatibility (`--compat-loose-equality` / `"compat-loose-equality"`). `==` and `!=` remain warning-producing no-ops by default, but when enabled they use the shared ES2026 `IsLooselyEqual` implementation in interpreter and bytecode modes, including string/number/boolean coercion, BigInt mixed comparisons, and object `ToPrimitive`; the bytecode format is bumped to v27 for `OP_LOOSE_EQ` and `OP_LOOSE_NEQ`. [language.md](language.md#loose-equality--and-).

**2026-05-10** · `engine` — Removed the `--compat-all` meta-flag. It was originally added to give test262 a single switch that turns on every `--compat-*` option, but in practice it (a) hides which compatibility surface a given run actually depends on, (b) silently widens its own meaning whenever a new `--compat-*` flag lands, and (c) needs the same fan-out logic kept in sync in three frontends (`GocciaScriptLoaderBare.dpr`, `Goccia.CLI.Application.pas`, `GocciaBundler.dpr`). Consumers now enumerate the specific flags they need; `scripts/run_test262_suite.ts` lists `--compat-var --compat-function --compat-traditional-for-loop` explicitly. The individual compat flags themselves (`--compat-var`, `--compat-function`, `--compat-traditional-for-loop`) and their `goccia.json` keys are unchanged.

**2026-05-08** · `runtime` — ECMA-402 Intl namespace. Full implementation of the Internationalization API: Intl.Locale, Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat, Intl.PluralRules, Intl.RelativeTimeFormat, Intl.ListFormat, Intl.DisplayNames, Intl.Segmenter, Intl.DurationFormat, plus Intl.getCanonicalLocales and Intl.supportedValuesOf. Data strategy mirrors Temporal timezone: system ICU library (macOS libicucore, Linux libicui18n, Windows icu.dll shared with Temporal) as primary, embedded CLDR resource as fallback. Shared units in `source/shared/` (ICU.pas, BCP47.pas, IntlTypes.pas, IntlICU.pas, IntlCLDRData.pas, IntlLocaleResolver.pas) provide platform-independent Intl infrastructure. Existing locale stubs on String/Number/Date prototypes wired to Intl formatters. [built-ins-intl.md](built-ins-intl.md). [build-system.md § Generated Intl Data](build-system.md#generated-intl-data).

**2026-05-08** · `engine` — Replace TRegExpr with a purpose-built backtracking bytecode VM regex engine. TRegExpr used native call recursion for backtracking, causing SIGSEGV on inputs ~42K+ chars when combined with the evaluator's stack depth (#515). Three preprocessing passes papered over feature gaps: `(?s)` modifier scope leak, no named groups (two-pass rewrite), and inadequate Unicode (`\p{...}` expanded to ASCII approximations). New architecture: `Goccia.RegExp.Compiler.pas` (recursive-descent parser + bytecode emitter) and `Goccia.RegExp.VM.pas` (iterative dispatch loop with heap-allocated backtrack stack and always-on failure memoization). The compiler parses ES2026 regex grammar directly, handling named groups, Unicode property escapes, inline modifier groups, and backreferences natively — no preprocessing passes. The memoization cache records `(PC, InputPos)` failure states to prune exponential backtracking (e.g., `(a+)+b`). Configurable step limit (default 10M) throws `Error` instead of crashing. Removes the FPC `regexpr` package from the cross-compilation toolchain. Reuses `TextSemantics.pas` UTF-8 functions (`TryReadUTF8CodePoint`, `AdvanceUTF8StringIndex`, `CodePointToUTF8`, etc.) rather than reimplementing. Public API (`ExecuteRegExp` signature, `TGocciaRegExpMatchResult` record) unchanged; `Goccia.RegExp.Runtime.pas` and `Goccia.Builtins.GlobalRegExp.pas` unmodified.

**2026-05-05** · `parser` — Opt-in traditional `for(init; test; update)` loops (`--compat-traditional-for-loop`). Added behind a new compatibility flag for ECMAScript compatibility when porting legacy code, mirroring the existing `--compat-var` and `--compat-function` posture. The flag is off by default and ORed in by `--compat-all` so test262 (which always passes `--compat-all`) executes traditional `for(;;)` bodies that previously parser-warn-and-skipped — surfacing real engine gaps in unrelated areas (Atomics #541, Intl #542, BigInt postfix increment #540, etc.). `let`/`const` declarations in for-init create a per-iteration lexical environment per ES2026 §14.7.4.4, so closures captured during iteration N pin to that iteration's binding (the textbook `fns.push(() => i)` case yields `[0, 1, 2]`, not `[3, 3, 3]`). `var` declarations require both `--compat-var` and the new flag and share a single hoisted binding visible after the loop. The bytecode compiler reuses the counted-loop pattern from `CompileCountedForOf` for `for(let i = N; i <op> M; i++ | i--)` shapes (rejecting var/const, bodies that mutate the loop var, type annotations, and non-integer-literal cond RHS). `while` and `do...while` remain excluded — they have the same stub status but were intentionally split into a separate iteration. [language-tables.md](language-tables.md).

**2026-05-04** · `testing` · [#513](https://github.com/frostney/GocciaScript/pull/513) — test262 conformance harness reframed around the standard tc39 convention. Previously the wrapper ran inside `GocciaTestRunner` and had to selectively hide / capture / restore the test-library globals (`expect`, `describe`, `test`, `runTests`, etc.) it registered, with failure capture leaning on an `undefined` sentinel that collided with thrown `undefined` and chunked-runner crashes that masked thousands of conformance failures as wrapper failures (#491 history). Replaced with: per-test `GocciaScriptLoaderBare` subprocess, stock tc39/test262 harness files read directly from the pinned checkout's `harness/` directory (with a small set of bundled adaptations under `scripts/test262_harness/` for stock files that depend on language features Goccia excludes by design or that work around specific engine bugs — see [test262.md § Bundled harness adaptations](test262.md#bundled-harness-adaptations)), exit-code + stdout-marker wire protocol identical to `test262-harness`/`eshost`/test262.fyi, and a thin TypeScript orchestrator (`scripts/run_test262_suite.ts`). No eligibility filter — every discovered test runs; per-test subprocess + `--timeout` + `--max-memory` bound the blast radius. Wrapper-template drift is now structurally impossible because the "template" is `harness + body` string concatenation. Wrapper-infra failures are classified separately and gated to zero in CI. Surfaced eleven engine bugs (all milestoned 0.8.0, all labeled `engine` per the architecture split where `engine` covers `TGocciaEngine` — language semantics + ECMAScript built-ins — and `runtime` is reserved for `TGocciaRuntime` host extensions like console/fetch/JSON5): [#514](https://github.com/frostney/GocciaScript/issues/514) (Iterator.concat SIGSEGV), [#515](https://github.com/frostney/GocciaScript/issues/515) (RegExp.test SIGSEGV), [#516](https://github.com/frostney/GocciaScript/issues/516) (`Reflect.construct` rejects function decls/exprs), [#517](https://github.com/frostney/GocciaScript/issues/517) (script-mode unattached call `this`), [#518](https://github.com/frostney/GocciaScript/issues/518) (bytecode VM Range-check on top-level `Promise.then` drain), [#519](https://github.com/frostney/GocciaScript/issues/519) (`Error.prototype.constructor` missing), [#520](https://github.com/frostney/GocciaScript/issues/520) (module arrow `this` lexical inheritance), [#521](https://github.com/frostney/GocciaScript/issues/521) (`var`/`function` shadowing built-in globals), [#522](https://github.com/frostney/GocciaScript/issues/522) (`String(obj)` doesn't invoke `toString`), [#523](https://github.com/frostney/GocciaScript/issues/523) (`yield*` accesses `.next` on null), [#524](https://github.com/frostney/GocciaScript/issues/524) (for-of re-fetches `iterator.next` each iteration). Each bundled-harness adaptation under `scripts/test262_harness/` references its tracking issue and is to be removed when the underlying engine bug is fixed. [test262.md](test262.md).

**2026-05-04** · `runtime` · [#510](https://github.com/frostney/GocciaScript/pull/510) — Script loaders are silent-by-default about the script's last evaluated value, mirroring `node script.js` / `bun script.js` / `deno run script.js` / `qjs script.js`. Printing is opt-in via `--print` (or `"print": true` in `goccia.json` for `GocciaScriptLoader`), which emits the bare value on its own line including `undefined` — the same semantics as `node -p` / `bun --print` / `deno eval -p`. Replaces the previous "always print `Result: <value>` banner" behavior, which was the outlier among mainstream JS runtimes and surfaced as a false-positive vector in substring-graded environments such as test262.fyi (#509). `GocciaScriptLoader`'s human-readable timing banner ("Running script (interpreted): ..." + Lex/Parse/Execute line) is unchanged; only the final value line moved behind the flag. JSON output (`--output=json`) still always carries the result in the structured `result` field regardless of `--print`. [build-system.md § Compile and Run](build-system.md#compile-and-run).

**2026-05-03** · `bytecode` — Bytecode compilation now has an internal compiler-side constant optimizer for pure primitive folding, primitive `const` propagation, and dead branch/tail removal. The work deliberately stays format-neutral: no new VM opcodes or `.gbc` changes, and coverage mode preserves branch shape so missing branches remain reportable. [bytecode-vm.md § Compiler Optimizer](bytecode-vm.md#compiler-optimizer).

**2026-05-02** · `runtime` · [#493](https://github.com/frostney/GocciaScript/pull/493) — Engine and runtime entry points are split explicitly. `TGocciaEngine` owns language execution, core built-ins, source strings/source lists, and Script-vs-Module entry semantics through `SourceType`; `TGocciaRuntime` owns host/runtime globals, special-purpose opt-ins such as test assertions/benchmarks/FFI, file-backed convenience helpers, and the default filesystem module content provider. `GocciaScriptLoaderBare` remains a core-engine-only frontend that can read file/stdin into source text without attaching runtime globals, and the CLI CI suite now covers that contract. [architecture.md § Main Layers](architecture.md#main-layers). [embedding.md § Engine API](embedding.md#engine-api).

**2026-04-30** · `runtime` — Strict-types runtime enforcement is now an explicit `--strict-types` CLI / `"strict-types"` config flag (default off) that works in both interpreter and bytecode mode. Previously the bytecode VM implicitly enforced type annotations and the interpreter silently ignored them, with a read-only `Goccia.strictTypes` JS global advertising the mode. The new model: enforcement is opt-in regardless of execution mode, the bytecode compiler gates `OP_CHECK_TYPE` emission on the flag, the interpreter records type hints on lexical bindings (`TLexicalBinding.TypeHint`) and enforces them in `AssignBinding` / variable declaration / parameter binding via the shared `Goccia.Types.Enforcement` unit, and the `Goccia.strictTypes` global is removed in favour of directory-level `goccia.json` config (mirroring `--asi` / `--compat-var`). [language.md § Types as Comments](language.md#types-as-comments-stage-1).

**2026-04-29** · `parser` — Parser hot paths now avoid repeated cursor-helper and chained `Match(...)` dispatch in `Advance`, `Check`, `Match`, remaining expression operators, call/member continuations, and primary expressions. Same-corpus production timing with `GocciaScriptLoader --output=json` showed 35.1-49.1% parser-phase improvements versus clean `HEAD`, so this supersedes the narrower binary-only parser optimization while keeping recursive descent and avoiding parser unit splits. [spikes/parser-hot-dispatch-performance.md](spikes/parser-hot-dispatch-performance.md).

**2026-04-29** · `parser` — Binary-expression precedence parsing now uses direct `Peek.TokenType` loops on the measured hot precedence layers instead of routing through the generic open-array `ParseBinaryExpression` helper. Production timing showed 3.6-18.9% parser-phase improvements across larger synthetic parser maps and 6.3-14.3% improvements across existing benchmark-file parse medians; rejected alternatives included explicit `Advance inline`, eager source-text removal, and repeated-`Check(...)` loops. [spikes/parser-binary-expression-performance.md](spikes/parser-binary-expression-performance.md).

**2026-04-29** · `parser` — Lexer hot paths favor direct scanning and measured tiny-method `inline` hints over abstraction in the numeric scanner. Comment skipping, block comments, regex literal slicing, and template line-terminator handling were simplified and benchmarked with `GocciaBenchmarkRunner` lex timing; the numeric separator helper was rejected because it shortened the code but regressed decimal and radix-heavy cases. [spikes/lexer-performance-simplification.md](spikes/lexer-performance-simplification.md).

**2026-04-28** · `runtime` · [#440](https://github.com/frostney/GocciaScript/pull/440) — Temporal named time zones use layered providers in this order: `GOCCIA_TZDIR`, Unix TZif files, Windows ICU, then embedded TZif resource fallback. The embedded IANA payload is generated as `Generated.TimeZoneData.res` with a small linker unit instead of a large Pascal array, keeping portability while leaving lookup and parsing logic in hand-authored units. [Temporal Time Zone Data](built-ins-temporal.md#time-zone-data). [Generated Timezone Data](build-system.md#generated-timezone-data).

**2026-04-27** · `runtime` — Generator and async generator support. Generator method shorthand (`*method()` and `async *method()`) is now part of the default method syntax, while `function*` and `async function*` remain behind `--compat-function` because they use the compatibility-gated `function` keyword. The interpreter uses resumable generator continuations and the bytecode format reserves a single `OP_YIELD` opcode; `yield*` is represented as delegation logic rather than a second opcode. [language.md § Generators](language.md#generators).

**2026-04-25** · `runtime` · [#403](https://github.com/frostney/GocciaScript/pull/403) — Per-engine realm for built-in prototype isolation. `TGocciaRealm` (`Goccia.Realm.pas`) owns the mutable intrinsic state — `Array.prototype`, `Object.prototype`, `Map.prototype`, every error prototype, every Temporal prototype, etc. Each `TGocciaEngine` constructs its own realm and frees it in `Destroy`, which unpins every prototype the realm owns; the next engine on the same worker thread sees pristine intrinsics. Replaces the unit-level `threadvar` caches that previously survived engine teardown and the `prototypeIsolation.js` harness that tried to undo mutations from JS (and could not reverse non-configurable property additions). Two slot kinds: `TGocciaRealmSlotId` for `TGCManagedObject` prototypes (pinned in `SetSlot`, unpinned at tear-down), and `TGocciaRealmOwnedSlotId` for plain-`TObject` helpers like `TGocciaSharedPrototype` (Free-d at tear-down before pin release so destructors can still unpin owned objects). [core-patterns.md § Realm Ownership & Slot Registration](core-patterns.md#realm-ownership--slot-registration). [embedding.md § Engine Lifecycle & Realm Isolation](embedding.md#engine-lifecycle--realm-isolation).

**2026-04-24** · `runtime` — Fetch-only asynchronous I/O. `fetch()` now returns a pending Promise before network completion and runs blocking HTTP work on fetch-specific background workers. Completions are settled back on the owning runtime thread, then normal Promise reactions drain through the existing microtask queue; the microtask queue remains a Promise-reaction queue, not an I/O queue. `await fetch(...)` deliberately keeps the current synchronous await model by pumping fetch completions while it waits; a general event loop and true async continuations remain future work. [built-ins.md § fetch](built-ins.md#fetch-gocciabuiltinsglobalfetchpas).

**2026-04-23** · `parser` — Opt-in `function` declarations (`--compat-function`). Added `function` declaration and expression support behind a `--compat-function` CLI flag for ECMAScript compatibility when porting legacy code. Function declarations desugar to var-scoped bindings backed by `TGocciaFunctionExpression`, which produces call-site `this` binding (not lexical). Declarations are hoisted per ES spec: both name and value are available before the declaration is reached, using the same `DefineVariableBinding` infrastructure as `--compat-var`. Async functions are supported; generators (`function*`) and `arguments` remain excluded. No new AST node types — uses `IsFunctionDeclaration` flag on `TGocciaVariableDeclaration`. [language.md § function Keyword](language.md#function-keyword).

**2026-04-20** · `runtime` · [#368](https://github.com/frostney/GocciaScript/pull/368) — Opt-in `var` declarations (`--compat-var`). Added `var` support behind a `--compat-var` CLI flag for ECMAScript compatibility when porting legacy code. Var bindings are stored in a separate `FVarBindings` map on function/module/global scopes (distinct from the lexical binding map used by `let`/`const`), matching the ES spec's separation between VariableEnvironment and LexicalEnvironment. This avoids forcing var semantics (function-scoped, redeclarable, no TDZ) through lexical machinery designed around block-scoping, TDZ, and no-redeclaration guarantees. The bytecode compiler uses a parallel `DeclareVarLocal` approach with depth-0 locals that survive `EndScope` unwinding. Naming follows spec terminology: `DefineLexicalBinding` / `DefineVariableBinding` / `GetBinding` / `AssignBinding`. [language.md § var Declarations](language.md#var-declarations).

**2026-04-20** · `runtime` · [#365](https://github.com/frostney/GocciaScript/pull/365) — BigInt64Array/BigUint64Array (ES2020). Adds the two BigInt-typed array constructors, completing typed array coverage. Elements are BigInt values; setting non-BigInt throws TypeError. All prototype methods (sort, indexOf, includes, fill, find, map, filter, reduce, etc.) handle BigInt kinds correctly. [built-ins-binary-data.md](built-ins-binary-data.md).

**2026-04-20** · `bytecode` · [#356](https://github.com/frostney/GocciaScript/pull/356) — Call stack depth limit with heap-resident trampoline. Added a configurable call depth limit (`--stack-size=N`, default 3 500) that throws `RangeError` when exceeded. The bytecode VM dispatches bytecode-to-bytecode calls iteratively via an explicit frame stack (`FFrameStack`) on the heap rather than recursing on the Pascal call stack. V8/JSC check the native stack pointer against a guard address and are bounded by the OS thread stack size; this implementation uses a heap-allocated frame array and a counter, which are independent concepts — the depth limit is a runtime safety guard, the trampoline is an implementation detail of call dispatch, and neither is related to JIT compilation. The interpreter mode retains Pascal recursion with the depth check as a guard. [bytecode-vm.md](bytecode-vm.md). [embedding.md § Call Stack Depth Limit](embedding.md#call-stack-depth-limit).

**2026-04-18** · `runtime` — BigInt primitive type (ES2020). Previously excluded from scope because GocciaScript's use cases did not demand arbitrary-precision integers. Reconsidered because (1) test262 tests for the Temporal API require BigInt as a prerequisite — nanosecond-precision epochs (`Temporal.Instant.epochNanoseconds`) return BigInt values, and (2) several Temporal internals are simplified with BigInt primitives available rather than workarounds. The implementation is self-contained (`TBigInteger` record in `BigInteger.pas`, `TGocciaBigIntValue` wrapping it) with no impact on existing Number paths. [built-ins.md § BigInt](built-ins.md#bigint-gocciabuiltinsglobalbigintpas). [language-tables.md](language-tables.md).

**2026-04-12** · `bytecode` · [#289](https://github.com/frostney/GocciaScript/pull/289) — Source Maps (v3). Source map generation for bytecode compilation and preprocessors (JSX transformer). Maps compiled bytecode instructions back to original source locations for debugging. [bytecode-vm.md § Design Rationale](bytecode-vm.md#design-rationale).

**2026-04-11** · `runtime` · [#276](https://github.com/frostney/GocciaScript/pull/276) — `Goccia.build` platform metadata. Deno.build-compatible shape exposing `os`, `arch`, and `target` for runtime platform detection. [built-ins.md § Global Constants](built-ins.md#global-constants-functions-and-error-constructors-gocciabuiltinsglobalspas).

**2026-04-09** · `bytecode` · [#237](https://github.com/frostney/GocciaScript/pull/237) — Bytecode VM profiling. Opcode histograms, pair frequency, function self-time, allocation tracking, and flame graph export via `--profile` flag. [profiling.md](profiling.md).

**2026-04-08** · `parser` · [#220](https://github.com/frostney/GocciaScript/pull/220) — Opt-in automatic semicolon insertion. Added ASI behind a `--asi` CLI flag. Previously against the project philosophy (GocciaScript requires explicit semicolons), added for compatibility with code written for standard ECMAScript. Off by default; does not change the language subset, only relaxes the semicolon requirement. [language.md § Automatic Semicolon Insertion](language.md#automatic-semicolon-insertion).

**2026-03-23 → 2026-04-10** · `bytecode` · [#107](https://github.com/frostney/GocciaScript/pull/107) [#109](https://github.com/frostney/GocciaScript/pull/109) [#110](https://github.com/frostney/GocciaScript/pull/110) [#136](https://github.com/frostney/GocciaScript/pull/136) [#137](https://github.com/frostney/GocciaScript/pull/137) [#254](https://github.com/frostney/GocciaScript/pull/254) — VM unification. Fold the separate Souffle VM into a GocciaScript-specific bytecode VM: eliminate interpreter bridges for modules (#107) and async/await (#110), add native-value-backed Map/Set (#109), refactor built-in object model registration (#136), fold the VM directly (#137), and extract the GC into a standalone `Goccia.GarbageCollector` unit (#254). [bytecode-vm.md § Design Rationale](bytecode-vm.md#design-rationale). [garbage-collector.md](garbage-collector.md).

**2026-03-25** · `bytecode` — Unify heap object hierarchy. `TGocciaValue` now inherits from `TGocciaCompiledHeapObject`, removing the bridge between interpreter and bytecode value models. [bytecode-vm.md § Design Rationale](bytecode-vm.md#design-rationale).

**2026-03-18** · `data-structures` — Custom hash maps over TDictionary. Purpose-built `TOrderedStringMap`, `THashMap`, and `TOrderedMap` replace `TDictionary` on hot paths with 4–6× faster inserts at typical sizes. [#66](https://github.com/frostney/GocciaScript/pull/66). [spikes/fpc-hashmap-performance.md](spikes/fpc-hashmap-performance.md).

**2026-03-11** · `strings` — TStringBuffer over TStringBuilder. Both `TUnicodeStringBuilder` and `TAnsiStringBuilder` trigger a 750× slowdown from FPC's heap manager without preallocation. `TStringBuffer` (advanced record with doubling growth) is ~2× faster even with preallocation. [#65](https://github.com/frostney/GocciaScript/pull/65). [spikes/fpc-string-performance.md](spikes/fpc-string-performance.md).

**2026-03-08** · `interpreter` — VMT dispatch on AST nodes. Expression and statement evaluation uses virtual dispatch instead of `if ... is` type check chains. Eliminates `TObject.InheritsFrom` overhead (18.4% of interpreted instructions in callgrind profiling). [#51](https://github.com/frostney/GocciaScript/pull/51). [core-patterns.md § Virtual Dispatch Value System](core-patterns.md#virtual-dispatch-value-system).

**2026-03-05** · `runtime` — Switch number representation from enum to IEEE 754. Removed `TGocciaNumberSpecialValue` enum and `FSpecialValue` field from `TGocciaNumberLiteralValue`. Numbers now store a single `Double` using standard IEEE 754 bit patterns for NaN, Infinity, and -0. Enabled by masking all FPU exceptions via `SetExceptionMask` so that operations like `0.0 / 0.0` produce IEEE 754 NaN instead of raising a Pascal exception. The `IsNaN`/`IsInfinity`/`IsNegativeZero` property accessors delegate to `Math.IsNaN`/`Math.IsInfinite` and an endian-neutral sign-bit check. The engine saves and restores the previous exception mask on creation/destruction. [#39](https://github.com/frostney/GocciaScript/pull/39). [value-system.md § Number Representation](value-system.md#number-representation).

**2026-03-08** · `interpreter` — `TGocciaControlFlow` for break/return. `break` and `return` use result records (`cfkBreak`, `cfkReturn`) instead of Pascal exceptions, eliminating `FPC_SETJMP` overhead from the interpreter's hot path. [#45](https://github.com/frostney/GocciaScript/pull/45). [interpreter.md § Error Handling Strategy](interpreter.md#error-handling-strategy).

**2026-03-05** · `bytecode` — Feature parity between bytecode and interpreter. The bytecode backend passes the full JavaScript test suite. [#39](https://github.com/frostney/GocciaScript/pull/39). [bytecode-vm.md](bytecode-vm.md).

**2026-02-20** · `strings` — No string interning. Dictionary-based string interning was benchmarked at −4% across 172 benchmarks. FPC's COW semantics make allocation effectively free; the hash + lookup cost exceeds it. [core-patterns.md § String Interning](core-patterns.md#string-interning--attempted-and-rejected).

**2026-02-16** · `interpreter` — Eliminate global mutable state. All runtime state flows through `TGocciaEvaluationContext`, the scope chain, and value objects. The `GlobalEvaluationContext` mutable global was removed. [interpreter.md § Pure Evaluator Functions](interpreter.md#pure-evaluator-functions).

**2026-02-16** · `interpreter` — `CreateChild` factory for scopes. Direct `TGocciaScope.Create` replaced with `ParentScope.CreateChild(kind)` to ensure proper parent linkage and callback propagation. [interpreter.md § Scope Chain Design](interpreter.md#scope-chain-design).

**2026-03-18** · `data-structures` — TOrderedStringMap for scope bindings. A `TScopeMap` with linear scan was attempted and abandoned after profiling showed 2.7× slowdown vs hash-based lookup. [#66](https://github.com/frostney/GocciaScript/pull/66). [spikes/fpc-hashmap-performance.md](spikes/fpc-hashmap-performance.md).

**2026-03-18** · `data-structures` — No TFPDataHashTable. Catastrophic insert performance (400,000 ns/insert vs 50 ns for `TOrderedStringMap`). [#66](https://github.com/frostney/GocciaScript/pull/66). [spikes/fpc-hashmap-performance.md](spikes/fpc-hashmap-performance.md).

**2026-03-18** · `fpc-platform` — Generics have zero runtime cost. Type aliases and multi-level generic inheritance produce byte-identical machine code. [#105](https://github.com/frostney/GocciaScript/pull/105). [spikes/fpc-generics-performance.md](spikes/fpc-generics-performance.md).

**2026-03-18** · `fpc-platform` — Virtual dispatch is constant-time. Depth 1 and depth 5 cost the same ~2.5ns. [#105](https://github.com/frostney/GocciaScript/pull/105). [spikes/fpc-dispatch-performance.md](spikes/fpc-dispatch-performance.md).

**2026-03-18** · `fpc-platform` — ObjFPC vs Delphi mode: zero performance impact. Both produce byte-identical machine code. [#105](https://github.com/frostney/GocciaScript/pull/105). [spikes/fpc-dispatch-performance.md](spikes/fpc-dispatch-performance.md).

**2026-02-26** · `bytecode` — Register-based bytecode. The VM uses a register machine because it reduces instruction count and avoids redundant operand shuffling compared with a stack VM. [#38](https://github.com/frostney/GocciaScript/pull/38). [bytecode-vm.md § Why Register-Based](bytecode-vm.md#why-register-based).

**2026-02-26** · `bytecode` — Compiler-side desugaring as default. Nullish coalescing, template literals, and object spread compile into existing instruction sequences rather than expanding the opcode surface. [#38](https://github.com/frostney/GocciaScript/pull/38). [bytecode-vm.md § Compiler-Side Desugaring](bytecode-vm.md#compiler-side-desugaring).

**2026-02-26** · `bytecode` — Core vs semantic opcode split follows hotness, not old runtime layering. [#38](https://github.com/frostney/GocciaScript/pull/38). [bytecode-vm.md § Opcode Layout](bytecode-vm.md#opcode-layout).

**2025-06-25** · `runtime` — Singleton special values. `undefined`, `null`, `true`, `false`, `NaN`, `Infinity` are singletons enabling pointer equality instead of type checks. [core-patterns.md § Singleton Special Values](core-patterns.md#singleton-special-values).

**2025-06-25** · `runtime` — Number dual representation. Numbers use `Double` + `TGocciaNumberSpecialValue` enum to handle `NaN`, `Infinity`, `-0` correctly. [value-system.md § Number Representation](value-system.md#number-representation).
