Skip to content

Update docs#302

Merged
frostney merged 18 commits into
mainfrom
documentation-restructure
Apr 14, 2026
Merged

Update docs#302
frostney merged 18 commits into
mainfrom
documentation-restructure

Conversation

@frostney
Copy link
Copy Markdown
Owner

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 449dbce0-f4eb-413c-8b21-fb964b029ab1

📥 Commits

Reviewing files that changed from the base of the PR and between a657924 and b7ba6c6.

📒 Files selected for processing (2)
  • docs/architecture.md
  • scripts/check-doc-duplication.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/architecture.md
  • scripts/check-doc-duplication.ts

📝 Walkthrough

Walkthrough

Consolidates and reorganizes repository documentation and QA: adds a CONTRIBUTING hub and contributor-facing docs, repurposes AGENTS.md for AI assistants, splits engine implementation docs, adds docs-quality TypeScript validation scripts plus a CI docs job, and migrates legacy design/style content into new authoritative locations.

Changes

Cohort / File(s) Summary
Contributing hub
CONTRIBUTING.md, docs/contributing/workflow.md, docs/contributing/code-style.md, docs/contributing/tooling.md
Adds a root CONTRIBUTING guide and three contributor-facing docs that codify workflow, FreePascal code style, tooling/formatter behavior, and critical contributor rules.
Agent-specific guidance
AGENTS.md
Repurposes AGENTS.md into an AI-assistant onboarding/navigation file with expectations, quick checks, and condensed run/build references.
Core engine docs
docs/interpreter.md, docs/bytecode-vm.md, docs/garbage-collector.md, docs/core-patterns.md, docs/value-system.md
Adds/rewrites engine docs: interpreter evaluation model, three-tier VM opcode model and rationale, unified mark-and-sweep GC, shared Pascal implementation patterns, and revised runtime value/type hierarchy.
Language & built-ins
docs/language.md, docs/language-tables.md, docs/built-ins.md, docs/built-ins-temporal.md, docs/built-ins-binary-data.md, docs/adding-built-in-types.md
Renames/refactors language docs, introduces language-tables, splits built-ins into focused pages (Temporal, binary-data), updates built-in lists (adds DisposableStack/AsyncDisposableStack) and built-in addition checklist.
Testing & test API
docs/testing.md, docs/testing-api.md, docs/tutorial.md
Reframes testing strategy to prioritize JavaScript end-to-end tests (interpreter + bytecode with --asi), adds JS testing API spec, and updates tutorial/CI testing guidance.
Docs QA scripts
scripts/check-doc-duplication.ts, scripts/check-doc-length.ts, scripts/check-doc-links.ts, scripts/check-doc-symbols.ts
Adds TypeScript doc-QA tooling: exact/near-duplicate detection (suffix/LCP + MinHash/LSH), per-file length checks, link/anchor validation, and Pascal symbol-reference verification; scripts fail CI on errors.
Docs CI & linting
.github/workflows/pr.yml, .markdownlint-cli2.jsonc
Adds a docs job to PR CI to run markdownlint and the new validation scripts; introduces markdownlint config and ignore rules.
Build, embedding & README
docs/build-system.md, docs/embedding.md, README.md, docs/architecture.md
Adjusts build examples (ScriptLoader/source-map), refactors README documentation index and architecture wording, and updates embedding/build links to CONTRIBUTING.md.
Decision log & legacy migrations
docs/decision-log.md, docs/design-decisions.md, docs/code-style.md
Introduces templated immutable decision-log and removes legacy docs/design-decisions.md and old docs/code-style.md, migrating authoritative content into CONTRIBUTING and docs/contributing/*.
Documentation refinements & templates
.github/ISSUE_TEMPLATE/default.md, .github/pull_request_template.md, docs/spikes/*, various docs
Formatting/spacing tweaks to templates, fenced-code labeling changes, anchor/headline relocations, and minor cross-reference updates across many docs.
High-review scripts
scripts/check-doc-duplication.ts, scripts/check-doc-symbols.ts
Complex analysis scripts (suffix/LCP exact-clone detection, MinHash+LSH fuzzy matching, Pascal symbol extraction/parsing) that warrant focused algorithmic and correctness review.

Sequence Diagram(s)

sequenceDiagram
    participant PR as Pull Request
    participant GH as GitHub Actions
    participant Scripts as Docs Validation Scripts
    participant Repo as Repository

    PR->>GH: push / open (trigger)
    GH->>Repo: checkout code
    GH->>Scripts: run markdownlint + check-doc-*.ts
    Scripts->>Repo: read docs + Pascal sources (headings, anchors, symbols, lengths, duplicates)
    Scripts-->>GH: emit reports & exit code
    GH-->>PR: display status (pass / fail)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request has no description provided by the author, missing required sections like Summary (change description, constraints, issue links) and Testing (verification checkboxes and documentation updates). Add a comprehensive description covering: (1) the main documentation restructuring objective; (2) creation of CONTRIBUTING.md with guides for contributors; (3) creation/refactoring of docs/ files; (4) verification that documentation builds correctly and links are valid; and (5) confirmation of passing documentation checks from the new CI job.
Title check ❓ Inconclusive The title 'Update docs' is overly vague and generic, using non-descriptive language that fails to convey the specific nature or scope of the substantial documentation restructuring. Replace with a more specific title that captures the main restructuring effort, such as 'Restructure documentation for contributors and AI agents' or 'Reorganize docs into CONTRIBUTING guides and agent-specific navigation.'
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added the documentation Improvements or additions to documentation label Apr 14, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/architecture.md (1)

10-10: ⚠️ Potential issue | 🟡 Minor

Tighten wording to avoid implying TGocciaValue-backed VM registers.

“Operates directly on TGocciaValue” conflicts with the documented tagged-register execution model later in this file. Consider rephrasing to emphasize shared runtime semantics/values at boundaries, not register storage.

Based on learnings: “the bytecode VM register file uses a tagged TGocciaRegister type, not TGocciaValue directly.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/architecture.md` at line 10, The wording wrongly implies VM registers
store TGocciaValue directly; update the sentence to state that the bytecode VM
shares runtime semantics/values at API/boundaries using TGocciaValue but that
the register file itself uses a tagged TGocciaRegister type (not TGocciaValue
directly), aligning it with the tagged-register execution model described
later—mention TGocciaValue for boundary/shared semantics and TGocciaRegister for
register storage to remove the ambiguity.
🧹 Nitpick comments (1)
docs/contributing/code-style.md (1)

195-196: Consider clarifying iteration guidance to avoid mixed signals.

This page recommends indexed loops for array-returning map APIs, but later recommends for..in over dictionary key/value collections. Suggest a short clarification that these are context-specific patterns (dynamic arrays vs keyed collections) to prevent over-generalization.

Also applies to: 317-320

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/contributing/code-style.md` around lines 195 - 196, Clarify the
iteration guidance to state that the recommendation to use indexed for loops
applies specifically to APIs that return dynamic arrays (e.g., Keys, Values,
ToArray) from custom maps that implement the TDictionary-style core API (Add,
AddOrSetValue, TryGetValue, ContainsKey, Remove, Clear), whereas the later
recommendation to prefer for..in applies to keyed collection enumerations — add
a short sentence distinguishing "array-returning map methods (use indexed for)"
from "enumerable/keyed collections (for..in)" so readers don't over-generalize.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/bytecode-vm.md`:
- Line 163: Reword the sentence that currently says “The current VM uses
TGocciaValue directly” to explicitly differentiate the shared runtime object
model (TGocciaValue) from the VM's internal register representation
(TGocciaRegister); state that the register file stores tagged TGocciaRegister
entries and that hot scalar kinds are kept unboxed in registers until crossing
object/runtime boundaries. Update the same clarification at the second
occurrence as well so both mentions accurately describe TGocciaValue as the
runtime object representation and TGocciaRegister as the VM's internal, possibly
unboxed, register representation.
- Around line 154-158: The opcode-range description is incorrect: update the
prose to reflect the project's three-tier model — core 0..127, non-core generic
128..166, and semantic/helper 167..255 — and mention that semantic opcodes start
at OP_SEM_FIRST (value 167) as defined in units/Goccia.Bytecode.pas so
contributors are not misled; replace the single "semantic 128–255" paragraph
with this corrected split and a short note referencing OP_SEM_FIRST for readers
who want the authoritative constant.

In `@docs/decision-log.md`:
- Around line 7-9: The decision-log template and entries use brittle relative PR
links (`../../pull/N`); update the template line containing "`../../pull/N`" and
all decision entries (lines referencing that same pattern) to use absolute
GitHub PR URLs or repo-root absolute paths (for example replace `../../pull/N`
with `https://github.com/frostney/GocciaScript/pull/N` or
`/frostney/GocciaScript/pull/N`) so the PR links resolve correctly from rendered
docs; ensure you update every occurrence of the `../../pull/` pattern in this
file.

In `@docs/interpreter.md`:
- Around line 24-29: Update the TGocciaEvaluationContext record in the docs so
it matches the actual declaration: include the missing fields CurrentFilePath,
CoverageEnabled and DisposalTracker alongside the existing Scope, OnError and
LoadModule entries (i.e., mirror the exact field names and order used in the
TGocciaEvaluationContext declaration in the codebase) so the documentation
snippet reflects the current units/Goccia.Evaluator.Context.pas definition.

In `@docs/language.md`:
- Line 157: Update the markdown link so its fragment matches the actual heading
text: replace the reference `[Decorators](`#decorators`)` with the fragment that
matches the target heading `### Decorators (Stage 3)` (e.g., use
`#decorators-stage-3`) so the link correctly points to the `Decorators (Stage
3)` section.
- Around line 62-76: Rename the first level-2 heading "TC39 Proposals" to a
distinct title (e.g., "TC39 Proposals — Overview" or "TC39 Proposals (Summary)")
to avoid duplicate headings with the detailed section also titled "TC39
Proposals"; update the table-introducing heading that currently reads "TC39
Proposals" so fragment links and MD024 checks resolve correctly while leaving
the later detailed section heading unchanged.

In `@docs/value-system.md`:
- Around line 714-715: The documentation incorrectly states that TGocciaValue
inherits from TInterfacedObject; update the sentence to reflect the actual
declaration TGocciaValue = class(TGCManagedObject) so the memory-management
rationale is accurate, and mention that values are stored as class references
(TGocciaValue) rather than interface references to explain why switching to
interfaces would be a large refactor and risk circular references between
objects and their prototypes in the evaluator.

---

Outside diff comments:
In `@docs/architecture.md`:
- Line 10: The wording wrongly implies VM registers store TGocciaValue directly;
update the sentence to state that the bytecode VM shares runtime
semantics/values at API/boundaries using TGocciaValue but that the register file
itself uses a tagged TGocciaRegister type (not TGocciaValue directly), aligning
it with the tagged-register execution model described later—mention TGocciaValue
for boundary/shared semantics and TGocciaRegister for register storage to remove
the ambiguity.

---

Nitpick comments:
In `@docs/contributing/code-style.md`:
- Around line 195-196: Clarify the iteration guidance to state that the
recommendation to use indexed for loops applies specifically to APIs that return
dynamic arrays (e.g., Keys, Values, ToArray) from custom maps that implement the
TDictionary-style core API (Add, AddOrSetValue, TryGetValue, ContainsKey,
Remove, Clear), whereas the later recommendation to prefer for..in applies to
keyed collection enumerations — add a short sentence distinguishing
"array-returning map methods (use indexed for)" from "enumerable/keyed
collections (for..in)" so readers don't over-generalize.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1135e8ee-ee37-4db2-8942-008b59ab5063

📥 Commits

Reviewing files that changed from the base of the PR and between ab0ddac and 411eb32.

📒 Files selected for processing (20)
  • AGENTS.md
  • CONTRIBUTING.md
  • README.md
  • docs/adding-built-in-types.md
  • docs/architecture.md
  • docs/build-system.md
  • docs/built-ins.md
  • docs/bytecode-vm.md
  • docs/code-style.md
  • docs/contributing/code-style.md
  • docs/contributing/tooling.md
  • docs/contributing/workflow.md
  • docs/core-patterns.md
  • docs/decision-log.md
  • docs/design-decisions.md
  • docs/interpreter.md
  • docs/language.md
  • docs/testing.md
  • docs/tutorial.md
  • docs/value-system.md
💤 Files with no reviewable changes (2)
  • docs/design-decisions.md
  • docs/code-style.md

Comment thread docs/bytecode-vm.md Outdated
Comment thread docs/bytecode-vm.md Outdated
Comment thread docs/decision-log.md
Comment thread docs/interpreter.md
Comment thread docs/language.md Outdated
Comment thread docs/language.md Outdated
Comment thread docs/value-system.md Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

Suite Timing

Suite Metric Interpreted Bytecode
Tests Total 5355 5355
Tests Passed 5314 ✅ 5355 ✅
Tests Skipped 41 0
Tests Test Duration 379.9ms 348.8ms
Tests Lex 103.0ms 70.3ms
Tests Parse 133.8ms 140.5ms
Tests Compile 83.5ms
Tests Execute 397.8ms 384.6ms
Tests Engine Total 634.6ms 678.8ms
Benchmarks Total 364 364
Benchmarks Duration 9.76min 8.27min

Measured on ubuntu-latest x64.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 14, 2026

Benchmark Results

364 benchmarks

Interpreted: 🟢 191 improved · 🔴 26 regressed · 147 unchanged · avg +1.6%
Bytecode: 🟢 45 improved · 🔴 143 regressed · 176 unchanged · avg -0.8%

arraybuffer.js — Interp: 🟢 8, 🔴 1, 5 unch. · avg +1.5% · Bytecode: 🔴 7, 7 unch. · avg -1.9%
Benchmark Interpreted Δ Bytecode Δ
create ArrayBuffer(0) 454,060 ops/sec [448,960..455,925] → 444,453 ops/sec [405,616..451,295] ~ overlap (-2.1%) 511,921 ops/sec [509,493..514,612] → 513,120 ops/sec [508,254..515,007] ~ overlap (+0.2%)
create ArrayBuffer(64) 434,945 ops/sec [434,213..437,325] → 433,111 ops/sec [429,202..436,967] ~ overlap (-0.4%) 488,527 ops/sec [487,140..489,384] → 488,938 ops/sec [485,692..489,560] ~ overlap (+0.1%)
create ArrayBuffer(1024) 337,675 ops/sec [334,935..340,142] → 339,444 ops/sec [338,711..340,176] ~ overlap (+0.5%) 369,955 ops/sec [368,537..373,382] → 371,258 ops/sec [368,268..372,645] ~ overlap (+0.4%)
create ArrayBuffer(8192) 149,912 ops/sec [148,453..152,132] → 151,534 ops/sec [132,151..154,183] ~ overlap (+1.1%) 159,482 ops/sec [156,443..160,851] → 159,972 ops/sec [156,119..160,767] ~ overlap (+0.3%)
slice full buffer (64 bytes) 470,983 ops/sec [470,434..473,055] → 478,966 ops/sec [477,496..480,545] 🟢 +1.7% 602,680 ops/sec [600,616..606,148] → 561,619 ops/sec [548,287..572,292] 🔴 -6.8%
slice half buffer (512 of 1024 bytes) 403,546 ops/sec [401,900..404,838] → 413,457 ops/sec [411,554..413,650] 🟢 +2.5% 505,724 ops/sec [482,005..509,005] → 490,609 ops/sec [489,059..492,233] ~ overlap (-3.0%)
slice with negative indices 394,890 ops/sec [393,682..397,231] → 404,333 ops/sec [401,822..407,414] 🟢 +2.4% 548,642 ops/sec [544,204..552,126] → 533,410 ops/sec [530,443..538,220] 🔴 -2.8%
slice empty range 450,886 ops/sec [448,772..452,626] → 463,532 ops/sec [461,190..465,005] 🟢 +2.8% 582,721 ops/sec [578,517..586,154] → 564,498 ops/sec [559,631..567,854] 🔴 -3.1%
byteLength access 1,272,289 ops/sec [1,270,671..1,274,878] → 1,296,270 ops/sec [1,179,675..1,306,166] ~ overlap (+1.9%) 1,719,944 ops/sec [1,715,513..1,723,398] → 1,695,113 ops/sec [1,693,761..1,695,722] 🔴 -1.4%
Symbol.toStringTag access 983,174 ops/sec [982,310..984,960] → 1,001,758 ops/sec [1,000,272..1,006,724] 🟢 +1.9% 1,067,837 ops/sec [1,065,520..1,069,326] → 1,016,973 ops/sec [1,015,215..1,017,571] 🔴 -4.8%
ArrayBuffer.isView 712,485 ops/sec [710,928..714,479] → 704,425 ops/sec [701,227..705,926] 🔴 -1.1% 841,337 ops/sec [839,310..842,181] → 812,332 ops/sec [800,580..814,200] 🔴 -3.4%
clone ArrayBuffer(64) 401,310 ops/sec [400,555..402,705] → 410,611 ops/sec [409,474..411,430] 🟢 +2.3% 486,470 ops/sec [485,956..486,921] → 478,731 ops/sec [473,671..479,233] 🔴 -1.6%
clone ArrayBuffer(1024) 315,238 ops/sec [313,920..316,385] → 324,178 ops/sec [322,697..327,221] 🟢 +2.8% 365,261 ops/sec [359,244..369,573] → 366,865 ops/sec [364,753..369,048] ~ overlap (+0.4%)
clone ArrayBuffer inside object 253,436 ops/sec [247,284..254,688] → 265,463 ops/sec [264,602..266,158] 🟢 +4.7% 296,528 ops/sec [292,621..298,216] → 294,829 ops/sec [294,308..295,490] ~ overlap (-0.6%)
arrays.js — Interp: 🟢 12, 🔴 1, 6 unch. · avg +1.5% · Bytecode: 🟢 2, 🔴 7, 10 unch. · avg -1.0%
Benchmark Interpreted Δ Bytecode Δ
Array.from length 100 12,577 ops/sec [12,562..12,585] → 12,838 ops/sec [12,707..12,950] 🟢 +2.1% 17,018 ops/sec [16,932..17,093] → 16,597 ops/sec [16,574..16,606] 🔴 -2.5%
Array.from 10 elements 210,795 ops/sec [209,793..211,511] → 218,635 ops/sec [217,253..219,090] 🟢 +3.7% 230,726 ops/sec [228,166..232,595] → 234,424 ops/sec [232,903..236,120] 🟢 +1.6%
Array.of 10 elements 259,500 ops/sec [258,334..260,176] → 269,542 ops/sec [268,927..270,572] 🟢 +3.9% 296,011 ops/sec [293,424..297,099] → 292,050 ops/sec [291,669..294,274] ~ overlap (-1.3%)
spread into new array 319,632 ops/sec [318,907..320,155] → 334,062 ops/sec [332,615..334,458] 🟢 +4.5% 190,140 ops/sec [184,132..192,651] → 197,506 ops/sec [195,920..198,104] 🟢 +3.9%
map over 50 elements 23,839 ops/sec [23,702..23,971] → 24,330 ops/sec [24,277..24,373] 🟢 +2.1% 32,060 ops/sec [30,721..32,189] → 30,933 ops/sec [30,848..31,227] ~ overlap (-3.5%)
filter over 50 elements 21,579 ops/sec [21,505..21,701] → 22,100 ops/sec [22,011..22,141] 🟢 +2.4% 29,675 ops/sec [29,527..29,773] → 28,975 ops/sec [28,523..29,110] 🔴 -2.4%
reduce sum 50 elements 25,315 ops/sec [25,236..25,323] → 25,297 ops/sec [25,224..25,368] ~ overlap (-0.1%) 30,859 ops/sec [30,370..31,134] → 30,368 ops/sec [30,187..30,505] ~ overlap (-1.6%)
forEach over 50 elements 22,173 ops/sec [22,091..22,225] → 21,987 ops/sec [21,900..22,017] 🔴 -0.8% 31,523 ops/sec [31,365..31,539] → 30,319 ops/sec [30,242..30,404] 🔴 -3.8%
find in 50 elements 30,889 ops/sec [30,814..30,915] → 31,372 ops/sec [31,360..31,412] 🟢 +1.6% 45,628 ops/sec [45,387..45,953] → 43,611 ops/sec [43,482..43,684] 🔴 -4.4%
sort 20 elements 12,062 ops/sec [12,000..12,099] → 12,024 ops/sec [10,745..12,069] ~ overlap (-0.3%) 16,563 ops/sec [16,492..16,605] → 16,631 ops/sec [16,171..16,667] ~ overlap (+0.4%)
flat nested array 113,063 ops/sec [112,577..113,688] → 113,786 ops/sec [113,345..114,132] ~ overlap (+0.6%) 127,090 ops/sec [119,834..129,320] → 127,045 ops/sec [125,643..127,418] ~ overlap (-0.0%)
flatMap 70,831 ops/sec [70,469..70,961] → 72,107 ops/sec [71,829..72,467] 🟢 +1.8% 85,080 ops/sec [84,312..86,524] → 84,855 ops/sec [83,896..86,289] ~ overlap (-0.3%)
map inside map (5x5) 19,721 ops/sec [19,417..19,883] → 19,938 ops/sec [19,893..19,943] 🟢 +1.1% 23,882 ops/sec [23,831..23,933] → 23,803 ops/sec [23,707..23,839] ~ overlap (-0.3%)
filter inside map (5x10) 14,811 ops/sec [14,744..14,844] → 15,100 ops/sec [15,036..15,128] 🟢 +2.0% 19,198 ops/sec [19,032..19,479] → 18,598 ops/sec [18,548..18,665] 🔴 -3.1%
reduce inside map (5x10) 18,521 ops/sec [18,437..18,579] → 18,387 ops/sec [18,333..18,448] ~ overlap (-0.7%) 22,494 ops/sec [22,278..22,676] → 22,368 ops/sec [22,328..22,421] ~ overlap (-0.6%)
forEach inside forEach (5x10) 16,015 ops/sec [15,907..16,098] → 16,001 ops/sec [15,936..16,036] ~ overlap (-0.1%) 23,555 ops/sec [23,460..23,656] → 23,353 ops/sec [23,317..23,369] 🔴 -0.9%
find inside some (10x10) 12,783 ops/sec [12,738..12,844] → 12,893 ops/sec [12,832..12,932] ~ overlap (+0.9%) 17,325 ops/sec [17,061..17,380] → 16,957 ops/sec [16,916..16,960] 🔴 -2.1%
map+filter chain nested (5x20) 4,934 ops/sec [4,911..4,951] → 5,017 ops/sec [5,007..5,036] 🟢 +1.7% 6,428 ops/sec [6,386..6,440] → 6,397 ops/sec [6,376..6,416] ~ overlap (-0.5%)
reduce flatten (10x5) 38,030 ops/sec [37,865..38,040] → 38,624 ops/sec [38,550..38,974] 🟢 +1.6% 17,367 ops/sec [17,279..17,672] → 17,641 ops/sec [17,515..17,702] ~ overlap (+1.6%)
async-await.js — Interp: 🟢 1, 🔴 3, 2 unch. · avg -1.0% · Bytecode: 🟢 2, 🔴 2, 2 unch. · avg +0.2%
Benchmark Interpreted Δ Bytecode Δ
single await 380,289 ops/sec [379,180..381,360] → 372,190 ops/sec [371,474..374,105] 🔴 -2.1% 394,080 ops/sec [392,004..394,846] → 391,515 ops/sec [390,538..392,266] ~ overlap (-0.7%)
multiple awaits 170,568 ops/sec [169,489..171,464] → 164,795 ops/sec [164,095..165,419] 🔴 -3.4% 168,542 ops/sec [167,801..169,715] → 166,657 ops/sec [161,069..167,142] 🔴 -1.1%
await non-Promise value 832,542 ops/sec [830,396..834,159] → 850,649 ops/sec [844,445..857,047] 🟢 +2.2% 1,093,557 ops/sec [1,091,786..1,100,476] → 1,132,825 ops/sec [1,130,740..1,137,789] 🟢 +3.6%
await with try/catch 369,592 ops/sec [368,606..371,251] → 363,309 ops/sec [360,866..364,734] 🔴 -1.7% 386,999 ops/sec [385,761..389,735] → 386,180 ops/sec [385,597..388,284] ~ overlap (-0.2%)
await Promise.all 50,518 ops/sec [50,078..50,800] → 50,314 ops/sec [50,042..50,580] ~ overlap (-0.4%) 49,840 ops/sec [49,611..50,036] → 48,827 ops/sec [48,098..49,486] 🔴 -2.0%
nested async function call 192,491 ops/sec [191,783..193,168] → 191,395 ops/sec [190,668..192,471] ~ overlap (-0.6%) 227,673 ops/sec [225,684..229,766] → 231,091 ops/sec [230,046..231,829] 🟢 +1.5%
base64.js — Interp: 🟢 4, 6 unch. · avg +1.1% · Bytecode: 🟢 2, 🔴 3, 5 unch. · avg -0.6%
Benchmark Interpreted Δ Bytecode Δ
short ASCII (13 chars) 504,406 ops/sec [496,868..504,982] → 513,318 ops/sec [505,737..522,962] 🟢 +1.8% 622,321 ops/sec [618,313..624,582] → 618,776 ops/sec [616,267..621,869] ~ overlap (-0.6%)
medium ASCII (450 chars) 149,973 ops/sec [149,592..150,305] → 150,884 ops/sec [150,471..151,064] 🟢 +0.6% 158,637 ops/sec [158,135..159,973] → 158,376 ops/sec [158,208..158,535] ~ overlap (-0.2%)
Latin-1 characters 522,801 ops/sec [518,532..524,660] → 533,642 ops/sec [519,296..537,303] ~ overlap (+2.1%) 637,480 ops/sec [634,164..638,564] → 617,717 ops/sec [612,464..620,133] 🔴 -3.1%
short base64 (20 chars) 382,526 ops/sec [379,005..388,721] → 390,540 ops/sec [381,517..394,374] ~ overlap (+2.1%) 442,176 ops/sec [441,004..443,046] → 443,226 ops/sec [442,147..445,063] ~ overlap (+0.2%)
medium base64 (600 chars) 84,642 ops/sec [84,259..84,843] → 86,354 ops/sec [86,249..86,386] 🟢 +2.0% 88,384 ops/sec [87,912..89,511] → 89,226 ops/sec [88,829..90,739] ~ overlap (+1.0%)
Latin-1 output 403,095 ops/sec [399,602..408,151] → 401,965 ops/sec [396,747..405,954] ~ overlap (-0.3%) 470,873 ops/sec [464,705..471,634] → 463,115 ops/sec [462,230..464,734] ~ overlap (-1.6%)
forgiving (no padding) 432,190 ops/sec [427,325..440,854] → 428,165 ops/sec [423,933..441,861] ~ overlap (-0.9%) 450,842 ops/sec [448,962..452,179] → 441,757 ops/sec [440,021..443,228] 🔴 -2.0%
with whitespace 392,204 ops/sec [389,152..398,168] → 391,298 ops/sec [359,754..403,917] ~ overlap (-0.2%) 409,947 ops/sec [408,820..411,439] → 401,704 ops/sec [400,160..404,867] 🔴 -2.0%
atob(btoa(short)) 246,644 ops/sec [245,583..249,791] → 250,871 ops/sec [247,261..251,928] ~ overlap (+1.7%) 269,946 ops/sec [268,590..270,684] → 271,879 ops/sec [271,600..272,796] 🟢 +0.7%
atob(btoa(medium)) 55,365 ops/sec [55,290..55,552] → 56,486 ops/sec [56,360..56,515] 🟢 +2.0% 56,623 ops/sec [56,491..56,869] → 57,574 ops/sec [57,446..57,762] 🟢 +1.7%
classes.js — Interp: 🟢 14, 🔴 2, 15 unch. · avg +1.2% · Bytecode: 🟢 1, 🔴 4, 26 unch. · avg -0.8%
Benchmark Interpreted Δ Bytecode Δ
simple class new 136,508 ops/sec [135,606..137,001] → 136,392 ops/sec [133,717..137,052] ~ overlap (-0.1%) 206,297 ops/sec [204,813..208,348] → 207,083 ops/sec [205,919..207,425] ~ overlap (+0.4%)
class with defaults 108,506 ops/sec [107,512..109,783] → 108,767 ops/sec [108,712..108,930] ~ overlap (+0.2%) 139,034 ops/sec [137,332..139,830] → 138,245 ops/sec [136,979..139,378] ~ overlap (-0.6%)
50 instances via Array.from 5,447 ops/sec [5,403..5,472] → 5,530 ops/sec [5,518..5,553] 🟢 +1.5% 8,413 ops/sec [8,380..8,445] → 7,891 ops/sec [7,883..7,918] 🔴 -6.2%
instance method call 69,669 ops/sec [69,333..70,241] → 69,728 ops/sec [69,574..69,855] ~ overlap (+0.1%) 98,236 ops/sec [97,501..99,731] → 96,478 ops/sec [96,125..97,038] 🔴 -1.8%
static method call 111,484 ops/sec [110,593..111,821] → 110,436 ops/sec [110,145..110,837] ~ overlap (-0.9%) 192,084 ops/sec [190,859..192,841] → 191,049 ops/sec [190,500..191,740] ~ overlap (-0.5%)
single-level inheritance 54,588 ops/sec [54,299..54,772] → 54,942 ops/sec [54,608..55,072] ~ overlap (+0.6%) 73,515 ops/sec [72,569..74,579] → 72,460 ops/sec [71,792..73,236] ~ overlap (-1.4%)
two-level inheritance 47,041 ops/sec [46,884..47,379] → 47,295 ops/sec [47,114..47,452] ~ overlap (+0.5%) 60,187 ops/sec [58,005..60,739] → 58,693 ops/sec [57,463..59,224] ~ overlap (-2.5%)
private field access 71,056 ops/sec [70,910..71,177] → 70,350 ops/sec [70,152..70,413] 🔴 -1.0% 93,472 ops/sec [92,957..94,292] → 94,170 ops/sec [93,503..94,786] ~ overlap (+0.7%)
private methods 78,030 ops/sec [77,802..78,313] → 77,724 ops/sec [75,132..77,893] ~ overlap (-0.4%) 99,530 ops/sec [98,625..99,982] → 98,766 ops/sec [98,279..99,272] ~ overlap (-0.8%)
getter/setter access 74,674 ops/sec [74,576..74,741] → 74,169 ops/sec [73,835..74,228] 🔴 -0.7% 105,971 ops/sec [105,047..106,523] → 109,363 ops/sec [108,304..109,690] 🟢 +3.2%
class decorator (identity) 91,820 ops/sec [90,826..92,397] → 92,811 ops/sec [91,453..93,315] ~ overlap (+1.1%) 110,113 ops/sec [106,910..112,330] → 103,926 ops/sec [103,437..105,173] 🔴 -5.6%
class decorator (wrapping) 53,647 ops/sec [52,913..54,031] → 54,675 ops/sec [54,150..55,046] 🟢 +1.9% 57,417 ops/sec [56,452..59,210] → 59,742 ops/sec [56,603..61,742] ~ overlap (+4.0%)
identity method decorator 64,572 ops/sec [64,392..65,012] → 66,267 ops/sec [65,506..66,530] 🟢 +2.6% 88,593 ops/sec [82,763..94,667] → 87,948 ops/sec [81,942..92,874] ~ overlap (-0.7%)
wrapping method decorator 52,144 ops/sec [51,931..52,459] → 52,276 ops/sec [52,236..52,644] ~ overlap (+0.3%) 63,956 ops/sec [60,476..69,986] → 63,653 ops/sec [59,975..67,804] ~ overlap (-0.5%)
stacked method decorators (x3) 35,776 ops/sec [35,658..36,181] → 36,048 ops/sec [35,931..36,205] ~ overlap (+0.8%) 42,317 ops/sec [38,835..45,310] → 42,680 ops/sec [39,951..45,633] ~ overlap (+0.9%)
identity field decorator 69,748 ops/sec [69,324..70,004] → 72,364 ops/sec [72,187..72,853] 🟢 +3.8% 76,134 ops/sec [74,556..80,598] → 76,354 ops/sec [73,678..78,955] ~ overlap (+0.3%)
field initializer decorator 59,625 ops/sec [59,407..60,017] → 60,536 ops/sec [60,458..60,786] 🟢 +1.5% 66,364 ops/sec [64,588..71,880] → 65,281 ops/sec [62,814..67,834] ~ overlap (-1.6%)
getter decorator (identity) 64,702 ops/sec [64,174..64,987] → 66,108 ops/sec [65,986..66,193] 🟢 +2.2% 75,379 ops/sec [74,455..77,479] → 74,506 ops/sec [73,368..76,144] ~ overlap (-1.2%)
setter decorator (identity) 53,963 ops/sec [53,662..54,410] → 55,316 ops/sec [55,210..55,409] 🟢 +2.5% 61,867 ops/sec [61,144..63,049] → 60,019 ops/sec [59,248..60,382] 🔴 -3.0%
static method decorator 70,490 ops/sec [69,896..71,107] → 70,577 ops/sec [70,087..70,709] ~ overlap (+0.1%) 83,053 ops/sec [82,329..89,159] → 82,632 ops/sec [80,737..85,239] ~ overlap (-0.5%)
static field decorator 82,819 ops/sec [82,108..83,440] → 85,672 ops/sec [85,100..86,242] 🟢 +3.4% 89,515 ops/sec [84,900..94,316] → 86,297 ops/sec [83,864..92,406] ~ overlap (-3.6%)
private method decorator 52,810 ops/sec [52,729..53,042] → 53,821 ops/sec [53,584..54,070] 🟢 +1.9% 72,681 ops/sec [69,770..75,770] → 72,184 ops/sec [69,021..75,875] ~ overlap (-0.7%)
private field decorator 58,619 ops/sec [58,141..58,917] → 59,776 ops/sec [59,606..59,836] 🟢 +2.0% 63,447 ops/sec [61,612..65,515] → 62,996 ops/sec [61,593..65,818] ~ overlap (-0.7%)
plain auto-accessor (no decorator) 103,115 ops/sec [102,254..106,518] → 106,486 ops/sec [104,785..108,399] ~ overlap (+3.3%) 98,991 ops/sec [96,044..104,313] → 96,893 ops/sec [94,874..102,281] ~ overlap (-2.1%)
auto-accessor with decorator 54,977 ops/sec [54,211..56,185] → 54,377 ops/sec [49,376..55,549] ~ overlap (-1.1%) 58,300 ops/sec [56,466..63,381] → 57,019 ops/sec [55,295..61,611] ~ overlap (-2.2%)
decorator writing metadata 44,082 ops/sec [43,541..44,264] → 44,321 ops/sec [43,943..44,439] ~ overlap (+0.5%) 50,445 ops/sec [49,888..55,755] → 48,859 ops/sec [48,187..54,045] ~ overlap (-3.1%)
static getter read 125,780 ops/sec [125,221..126,522] → 130,177 ops/sec [129,681..130,547] 🟢 +3.5% 168,392 ops/sec [162,294..170,234] → 168,825 ops/sec [166,117..171,533] ~ overlap (+0.3%)
static getter/setter pair 96,921 ops/sec [96,211..97,407] → 98,365 ops/sec [97,511..98,704] 🟢 +1.5% 124,146 ops/sec [123,374..127,886] → 124,181 ops/sec [121,505..127,341] ~ overlap (+0.0%)
inherited static getter 76,613 ops/sec [75,983..77,072] → 79,072 ops/sec [78,464..79,448] 🟢 +3.2% 93,749 ops/sec [92,271..94,921] → 94,679 ops/sec [93,251..96,289] ~ overlap (+1.0%)
inherited static setter 83,040 ops/sec [82,484..83,455] → 84,113 ops/sec [83,778..84,392] 🟢 +1.3% 101,409 ops/sec [100,187..102,664] → 102,521 ops/sec [102,059..103,318] ~ overlap (+1.1%)
inherited static getter with this binding 72,106 ops/sec [71,875..72,471] → 71,956 ops/sec [71,569..72,120] ~ overlap (-0.2%) 85,608 ops/sec [84,457..88,234] → 87,773 ops/sec [87,011..88,346] ~ overlap (+2.5%)
closures.js — Interp: 🟢 8, 🔴 1, 2 unch. · avg +1.0% · Bytecode: 🔴 2, 9 unch. · avg +0.2%
Benchmark Interpreted Δ Bytecode Δ
closure over single variable 128,992 ops/sec [128,133..130,157] → 131,434 ops/sec [130,415..131,784] 🟢 +1.9% 317,000 ops/sec [316,314..318,922] → 320,574 ops/sec [318,718..323,180] ~ overlap (+1.1%)
closure over multiple variables 114,780 ops/sec [113,198..115,997] → 116,518 ops/sec [116,092..116,928] 🟢 +1.5% 317,883 ops/sec [317,430..319,421] → 320,604 ops/sec [312,892..321,710] ~ overlap (+0.9%)
nested closures 121,209 ops/sec [120,841..121,771] → 123,566 ops/sec [122,866..124,129] 🟢 +1.9% 282,056 ops/sec [278,837..287,138] → 289,348 ops/sec [284,074..293,236] ~ overlap (+2.6%)
function as argument 90,511 ops/sec [90,280..90,792] → 90,241 ops/sec [89,172..90,715] ~ overlap (-0.3%) 318,203 ops/sec [317,355..318,857] → 316,248 ops/sec [313,479..317,846] ~ overlap (-0.6%)
function returning function 112,914 ops/sec [112,316..113,417] → 114,137 ops/sec [114,027..114,143] 🟢 +1.1% 339,034 ops/sec [336,461..358,138] → 344,351 ops/sec [343,318..350,418] ~ overlap (+1.6%)
compose two functions 67,891 ops/sec [67,758..67,946] → 69,187 ops/sec [68,519..69,753] 🟢 +1.9% 192,573 ops/sec [190,655..193,602] → 190,861 ops/sec [188,906..195,022] ~ overlap (-0.9%)
fn.call 151,619 ops/sec [150,509..153,584] → 147,532 ops/sec [146,637..149,483] 🔴 -2.7% 209,810 ops/sec [206,199..214,463] → 212,057 ops/sec [206,979..213,969] ~ overlap (+1.1%)
fn.apply 110,862 ops/sec [110,287..111,113] → 112,362 ops/sec [111,985..112,391] 🟢 +1.4% 201,823 ops/sec [197,138..204,481] → 202,153 ops/sec [199,423..203,122] ~ overlap (+0.2%)
fn.bind 138,577 ops/sec [137,412..139,099] → 137,171 ops/sec [135,242..137,578] ~ overlap (-1.0%) 357,156 ops/sec [351,505..364,236] → 363,149 ops/sec [359,557..364,996] ~ overlap (+1.7%)
recursive sum to 50 11,259 ops/sec [11,202..11,310] → 11,720 ops/sec [11,600..11,862] 🟢 +4.1% 48,568 ops/sec [48,341..49,083] → 47,178 ops/sec [46,588..47,737] 🔴 -2.9%
recursive tree traversal 19,613 ops/sec [19,575..19,621] → 19,921 ops/sec [19,792..19,990] 🟢 +1.6% 42,779 ops/sec [42,469..42,991] → 41,863 ops/sec [41,453..42,135] 🔴 -2.1%
collections.js — Interp: 🟢 7, 5 unch. · avg +1.9% · Bytecode: 🔴 10, 2 unch. · avg -3.1%
Benchmark Interpreted Δ Bytecode Δ
add 50 elements 6,484 ops/sec [6,474..6,518] → 6,683 ops/sec [6,587..6,705] 🟢 +3.1% 7,065 ops/sec [7,052..7,080] → 6,738 ops/sec [6,711..6,749] 🔴 -4.6%
has lookup (50 elements) 82,438 ops/sec [80,537..82,712] → 81,873 ops/sec [80,178..82,146] ~ overlap (-0.7%) 89,800 ops/sec [89,738..89,875] → 86,755 ops/sec [86,394..87,167] 🔴 -3.4%
delete elements 44,122 ops/sec [43,704..44,225] → 43,896 ops/sec [43,618..44,487] ~ overlap (-0.5%) 45,703 ops/sec [45,625..45,819] → 44,982 ops/sec [44,847..45,032] 🔴 -1.6%
forEach iteration 15,388 ops/sec [15,201..15,605] → 15,211 ops/sec [15,120..15,403] ~ overlap (-1.1%) 21,441 ops/sec [21,399..21,488] → 19,701 ops/sec [19,610..19,728] 🔴 -8.1%
spread to array 28,661 ops/sec [28,440..28,973] → 29,802 ops/sec [29,762..29,860] 🟢 +4.0% 217,810 ops/sec [215,109..218,784] → 206,126 ops/sec [201,624..207,129] 🔴 -5.4%
deduplicate array 36,454 ops/sec [36,045..36,837] → 37,886 ops/sec [37,655..38,235] 🟢 +3.9% 67,058 ops/sec [66,995..67,225] → 66,552 ops/sec [65,949..66,763] 🔴 -0.8%
set 50 entries 4,834 ops/sec [4,818..4,855] → 5,008 ops/sec [4,880..5,042] 🟢 +3.6% 5,360 ops/sec [5,217..5,416] → 5,287 ops/sec [5,119..5,364] ~ overlap (-1.4%)
get lookup (50 entries) 74,834 ops/sec [74,390..77,197] → 77,612 ops/sec [77,016..77,818] ~ overlap (+3.7%) 79,205 ops/sec [78,896..79,292] → 78,738 ops/sec [78,592..78,929] ~ overlap (-0.6%)
has check 115,953 ops/sec [115,755..116,396] → 117,136 ops/sec [116,464..117,826] 🟢 +1.0% 122,955 ops/sec [122,581..123,073] → 121,941 ops/sec [121,786..122,196] 🔴 -0.8%
delete entries 41,133 ops/sec [40,951..41,427] → 41,868 ops/sec [41,579..41,969] 🟢 +1.8% 41,768 ops/sec [41,684..41,983] → 41,212 ops/sec [41,073..41,307] 🔴 -1.3%
forEach iteration 15,429 ops/sec [15,303..15,555] → 15,231 ops/sec [15,075..15,480] ~ overlap (-1.3%) 21,638 ops/sec [21,459..21,758] → 20,658 ops/sec [20,573..20,715] 🔴 -4.5%
keys/values/entries 7,760 ops/sec [7,661..7,846] → 8,173 ops/sec [8,111..8,204] 🟢 +5.3% 26,715 ops/sec [26,422..26,957] → 25,559 ops/sec [25,212..25,822] 🔴 -4.3%
destructuring.js — Interp: 🟢 16, 🔴 1, 5 unch. · avg +2.4% · Bytecode: 🟢 6, 🔴 1, 15 unch. · avg +1.6%
Benchmark Interpreted Δ Bytecode Δ
simple array destructuring 378,513 ops/sec [374,115..379,938] → 384,746 ops/sec [380,950..388,925] 🟢 +1.6% 256,756 ops/sec [254,826..260,271] → 264,483 ops/sec [262,178..268,385] 🟢 +3.0%
with rest element 240,908 ops/sec [239,135..241,768] → 252,021 ops/sec [251,050..252,702] 🟢 +4.6% 193,032 ops/sec [189,385..194,725] → 201,707 ops/sec [200,268..203,403] 🟢 +4.5%
with defaults 376,006 ops/sec [368,306..384,414] → 389,466 ops/sec [388,174..390,645] 🟢 +3.6% 276,185 ops/sec [275,724..280,499] → 309,534 ops/sec [304,306..311,043] 🟢 +12.1%
skip elements 397,601 ops/sec [395,501..398,896] → 405,386 ops/sec [403,989..405,821] 🟢 +2.0% 268,871 ops/sec [263,605..271,965] → 283,276 ops/sec [278,838..284,604] 🟢 +5.4%
nested array destructuring 165,045 ops/sec [164,695..166,248] → 174,176 ops/sec [173,422..174,800] 🟢 +5.5% 87,970 ops/sec [86,932..88,973] → 89,480 ops/sec [87,602..91,129] ~ overlap (+1.7%)
swap variables 491,404 ops/sec [488,273..495,104] → 514,205 ops/sec [508,323..516,458] 🟢 +4.6% 321,944 ops/sec [315,861..326,187] → 331,642 ops/sec [328,962..332,422] 🟢 +3.0%
simple object destructuring 304,344 ops/sec [300,445..308,140] → 317,065 ops/sec [316,285..318,529] 🟢 +4.2% 373,702 ops/sec [371,644..376,059] → 374,798 ops/sec [371,460..376,792] ~ overlap (+0.3%)
with defaults 341,005 ops/sec [338,355..345,273] → 347,996 ops/sec [346,303..349,826] 🟢 +2.1% 525,703 ops/sec [521,961..532,178] → 513,836 ops/sec [511,309..519,178] 🔴 -2.3%
with renaming 328,958 ops/sec [326,575..330,679] → 331,292 ops/sec [329,958..332,182] ~ overlap (+0.7%) 382,922 ops/sec [379,176..388,975] → 381,272 ops/sec [378,074..384,132] ~ overlap (-0.4%)
nested object destructuring 153,024 ops/sec [151,509..154,768] → 149,221 ops/sec [148,610..149,797] 🔴 -2.5% 176,481 ops/sec [176,062..179,738] → 176,908 ops/sec [176,338..178,572] ~ overlap (+0.2%)
rest properties 182,015 ops/sec [180,198..183,779] → 187,599 ops/sec [187,106..188,186] 🟢 +3.1% 168,464 ops/sec [165,943..170,294] → 170,612 ops/sec [168,600..171,433] ~ overlap (+1.3%)
object parameter 94,827 ops/sec [93,163..96,321] → 94,842 ops/sec [93,230..95,571] ~ overlap (+0.0%) 144,101 ops/sec [142,775..145,766] → 145,395 ops/sec [143,896..148,980] ~ overlap (+0.9%)
array parameter 116,481 ops/sec [115,761..116,828] → 122,809 ops/sec [122,512..123,098] 🟢 +5.4% 121,962 ops/sec [120,573..122,262] → 126,596 ops/sec [125,481..127,863] 🟢 +3.8%
mixed destructuring in map 31,728 ops/sec [31,512..31,966] → 32,208 ops/sec [32,151..32,528] 🟢 +1.5% 41,750 ops/sec [40,692..43,029] → 42,310 ops/sec [41,838..43,108] ~ overlap (+1.3%)
forEach with array destructuring 61,472 ops/sec [61,214..61,951] → 64,299 ops/sec [63,891..64,521] 🟢 +4.6% 48,891 ops/sec [47,697..49,831] → 48,018 ops/sec [47,740..48,695] ~ overlap (-1.8%)
map with array destructuring 61,475 ops/sec [60,913..61,790] → 63,735 ops/sec [63,445..64,191] 🟢 +3.7% 43,228 ops/sec [42,725..43,565] → 43,862 ops/sec [42,943..44,238] ~ overlap (+1.5%)
filter with array destructuring 64,656 ops/sec [64,007..65,196] → 66,074 ops/sec [64,727..66,833] ~ overlap (+2.2%) 47,771 ops/sec [47,210..48,618] → 47,562 ops/sec [46,426..47,786] ~ overlap (-0.4%)
reduce with array destructuring 71,816 ops/sec [71,089..72,379] → 72,929 ops/sec [72,403..73,747] 🟢 +1.5% 47,388 ops/sec [46,735..48,379] → 48,662 ops/sec [47,908..49,431] ~ overlap (+2.7%)
map with object destructuring 70,273 ops/sec [69,666..70,722] → 71,395 ops/sec [71,300..71,762] 🟢 +1.6% 92,529 ops/sec [91,605..93,445] → 91,719 ops/sec [90,316..92,122] ~ overlap (-0.9%)
map with nested destructuring 59,339 ops/sec [59,050..59,684] → 59,683 ops/sec [59,544..60,172] ~ overlap (+0.6%) 84,094 ops/sec [83,460..84,910] → 83,956 ops/sec [82,704..84,546] ~ overlap (-0.2%)
map with rest in destructuring 40,937 ops/sec [40,508..41,060] → 41,873 ops/sec [41,527..42,094] 🟢 +2.3% 25,338 ops/sec [24,809..25,841] → 24,894 ops/sec [24,620..25,144] ~ overlap (-1.8%)
map with defaults in destructuring 54,842 ops/sec [54,439..55,067] → 55,008 ops/sec [54,696..55,326] ~ overlap (+0.3%) 72,494 ops/sec [71,845..72,929] → 72,862 ops/sec [72,456..75,166] ~ overlap (+0.5%)
fibonacci.js — Interp: 🟢 2, 🔴 1, 5 unch. · avg +0.4% · Bytecode: 🟢 2, 🔴 4, 2 unch. · avg -0.4%
Benchmark Interpreted Δ Bytecode Δ
recursive fib(15) 306 ops/sec [300..311] → 311 ops/sec [308..314] ~ overlap (+1.7%) 1,431 ops/sec [1,429..1,438] → 1,383 ops/sec [1,378..1,385] 🔴 -3.4%
recursive fib(20) 27 ops/sec [27..28] → 28 ops/sec [28..28] 🟢 +1.1% 129 ops/sec [129..130] → 125 ops/sec [125..125] 🔴 -3.3%
recursive fib(15) typed 304 ops/sec [297..308] → 312 ops/sec [307..314] ~ overlap (+2.7%) 1,119 ops/sec [1,116..1,122] → 1,089 ops/sec [1,079..1,092] 🔴 -2.7%
recursive fib(20) typed 27 ops/sec [27..28] → 28 ops/sec [28..28] 🟢 +1.6% 101 ops/sec [100..102] → 99 ops/sec [98..100] 🔴 -1.9%
iterative fib(20) via reduce 12,313 ops/sec [12,231..12,408] → 12,227 ops/sec [12,170..12,250] ~ overlap (-0.7%) 19,583 ops/sec [19,447..19,786] → 20,293 ops/sec [20,207..20,884] 🟢 +3.6%
iterator fib(20) 9,546 ops/sec [9,473..9,690] → 9,540 ops/sec [9,497..9,551] ~ overlap (-0.1%) 17,239 ops/sec [17,104..17,416] → 17,384 ops/sec [17,144..17,650] ~ overlap (+0.8%)
iterator fib(20) via Iterator.from + take 15,659 ops/sec [15,466..15,775] → 15,341 ops/sec [15,238..15,470] ~ overlap (-2.0%) 20,053 ops/sec [20,023..20,090] → 20,707 ops/sec [20,536..20,846] 🟢 +3.3%
iterator fib(20) last value via reduce 11,406 ops/sec [11,357..11,439] → 11,244 ops/sec [11,177..11,310] 🔴 -1.4% 14,107 ops/sec [13,962..14,234] → 14,110 ops/sec [14,081..14,290] ~ overlap (+0.0%)
float16array.js — Interp: 🟢 19, 🔴 2, 11 unch. · avg +1.7% · Bytecode: 🟢 10, 🔴 6, 16 unch. · avg +1.0%
Benchmark Interpreted Δ Bytecode Δ
new Float16Array(0) 328,333 ops/sec [324,518..329,624] → 335,488 ops/sec [333,870..336,333] 🟢 +2.2% 353,819 ops/sec [351,910..355,070] → 365,099 ops/sec [361,556..365,852] 🟢 +3.2%
new Float16Array(100) 308,546 ops/sec [304,726..309,858] → 313,334 ops/sec [311,551..314,484] 🟢 +1.6% 329,050 ops/sec [322,299..338,788] → 340,264 ops/sec [339,180..343,904] 🟢 +3.4%
new Float16Array(1000) 232,856 ops/sec [228,984..235,432] → 238,320 ops/sec [234,946..240,400] ~ overlap (+2.3%) 249,689 ops/sec [249,202..250,792] → 241,935 ops/sec [239,435..244,110] 🔴 -3.1%
Float16Array.from([...100]) 161,852 ops/sec [158,387..164,970] → 161,210 ops/sec [155,050..163,552] ~ overlap (-0.4%) 168,236 ops/sec [165,981..170,604] → 157,929 ops/sec [156,507..159,540] 🔴 -6.1%
Float16Array.of(1.5, 2.5, 3.5, 4.5, 5.5) 297,185 ops/sec [293,993..298,090] → 287,290 ops/sec [286,277..287,847] 🔴 -3.3% 229,943 ops/sec [223,703..234,636] → 239,782 ops/sec [237,801..242,712] 🟢 +4.3%
new Float16Array(float64Array) 196,017 ops/sec [195,224..196,750] → 195,817 ops/sec [192,785..196,934] ~ overlap (-0.1%) 207,032 ops/sec [205,297..207,950] → 210,647 ops/sec [209,029..211,274] 🟢 +1.7%
sequential write 100 elements 3,223 ops/sec [3,173..3,240] → 3,260 ops/sec [3,194..3,294] ~ overlap (+1.2%) 9,994 ops/sec [9,764..10,185] → 10,452 ops/sec [10,362..10,641] 🟢 +4.6%
sequential read 100 elements 3,537 ops/sec [3,486..3,586] → 3,508 ops/sec [3,485..3,525] ~ overlap (-0.8%) 13,461 ops/sec [13,435..13,513] → 13,709 ops/sec [13,629..13,887] 🟢 +1.8%
write special values (NaN, Inf, -0) 196,018 ops/sec [191,320..197,329] → 196,116 ops/sec [191,517..198,294] ~ overlap (+0.1%) 300,886 ops/sec [300,433..302,583] → 319,225 ops/sec [299,759..320,955] ~ overlap (+6.1%)
Float16Array write 3,222 ops/sec [3,194..3,248] → 3,274 ops/sec [3,258..3,281] 🟢 +1.6% 10,105 ops/sec [10,042..10,140] → 10,799 ops/sec [10,539..10,901] 🟢 +6.9%
Float32Array write 3,171 ops/sec [3,149..3,189] → 3,276 ops/sec [3,222..3,303] 🟢 +3.3% 10,099 ops/sec [10,043..10,123] → 10,497 ops/sec [10,449..10,553] 🟢 +3.9%
Float64Array write 3,204 ops/sec [3,183..3,215] → 3,305 ops/sec [3,300..3,370] 🟢 +3.2% 10,313 ops/sec [10,247..10,565] → 10,596 ops/sec [10,480..10,677] ~ overlap (+2.7%)
Float16Array read 3,358 ops/sec [3,339..3,398] → 3,427 ops/sec [3,399..3,458] 🟢 +2.1% 12,986 ops/sec [12,763..13,437] → 13,381 ops/sec [13,352..13,464] ~ overlap (+3.0%)
Float32Array read 3,477 ops/sec [3,438..3,487] → 3,522 ops/sec [3,515..3,532] 🟢 +1.3% 14,350 ops/sec [14,066..14,578] → 14,318 ops/sec [14,248..14,350] ~ overlap (-0.2%)
Float64Array read 3,490 ops/sec [3,473..3,501] → 3,533 ops/sec [3,511..3,540] 🟢 +1.2% 14,231 ops/sec [14,164..14,267] → 14,313 ops/sec [14,207..14,399] ~ overlap (+0.6%)
fill(1.5) 40,715 ops/sec [40,673..40,795] → 40,904 ops/sec [40,884..40,926] 🟢 +0.5% 40,838 ops/sec [40,772..40,905] → 40,762 ops/sec [40,655..40,824] ~ overlap (-0.2%)
slice() 136,804 ops/sec [135,586..137,751] → 146,681 ops/sec [145,116..147,215] 🟢 +7.2% 149,015 ops/sec [146,803..149,819] → 150,557 ops/sec [149,541..151,426] ~ overlap (+1.0%)
map(x => x * 2) 7,301 ops/sec [7,203..7,397] → 7,447 ops/sec [7,430..7,468] 🟢 +2.0% 8,677 ops/sec [8,606..8,693] → 8,736 ops/sec [8,690..8,818] ~ overlap (+0.7%)
filter(x => x > 25) 7,581 ops/sec [7,464..7,603] → 7,491 ops/sec [7,445..7,496] ~ overlap (-1.2%) 9,795 ops/sec [9,635..9,858] → 9,448 ops/sec [9,289..9,544] 🔴 -3.5%
reduce (sum) 7,347 ops/sec [7,277..7,459] → 7,359 ops/sec [7,312..7,398] ~ overlap (+0.2%) 8,069 ops/sec [7,970..8,207] → 7,946 ops/sec [7,900..8,016] ~ overlap (-1.5%)
sort() 27,453 ops/sec [27,402..27,509] → 27,550 ops/sec [27,522..27,576] 🟢 +0.4% 27,834 ops/sec [27,754..27,848] → 27,783 ops/sec [27,744..27,788] ~ overlap (-0.2%)
indexOf() 173,115 ops/sec [172,062..174,142] → 179,834 ops/sec [179,777..179,874] 🟢 +3.9% 192,055 ops/sec [192,019..192,129] → 191,258 ops/sec [191,186..191,465] 🔴 -0.4%
reverse() 183,706 ops/sec [182,634..184,605] → 194,313 ops/sec [193,515..194,836] 🟢 +5.8% 197,327 ops/sec [195,059..197,572] → 204,563 ops/sec [204,127..204,705] 🟢 +3.7%
toReversed() 79,964 ops/sec [79,527..80,507] → 77,738 ops/sec [77,295..77,989] 🔴 -2.8% 84,882 ops/sec [84,761..85,389] → 80,891 ops/sec [80,159..81,175] 🔴 -4.7%
toSorted() 987 ops/sec [985..988] → 989 ops/sec [988..990] 🟢 +0.2% 983 ops/sec [929..989] → 987 ops/sec [986..988] ~ overlap (+0.4%)
create view over existing buffer 382,567 ops/sec [381,224..385,447] → 400,688 ops/sec [399,144..404,939] 🟢 +4.7% 440,888 ops/sec [437,813..442,122] → 442,815 ops/sec [441,327..444,909] ~ overlap (+0.4%)
subarray() 411,250 ops/sec [397,334..417,290] → 420,058 ops/sec [416,753..421,105] ~ overlap (+2.1%) 556,566 ops/sec [553,079..566,256] → 551,008 ops/sec [547,788..556,224] ~ overlap (-1.0%)
set() from array 529,342 ops/sec [523,987..533,721] → 530,908 ops/sec [518,023..533,184] ~ overlap (+0.3%) 609,818 ops/sec [609,058..610,742] → 594,615 ops/sec [593,363..594,824] 🔴 -2.5%
for-of loop 4,687 ops/sec [4,625..4,732] → 4,970 ops/sec [4,947..4,994] 🟢 +6.0% 19,157 ops/sec [18,957..19,409] → 20,000 ops/sec [19,752..20,145] 🟢 +4.4%
spread into array 16,356 ops/sec [16,128..16,511] → 17,373 ops/sec [17,299..17,432] 🟢 +6.2% 92,836 ops/sec [91,957..95,408] → 92,875 ops/sec [91,364..94,036] ~ overlap (+0.0%)
f16round(1.337) 663,094 ops/sec [661,678..663,542] → 680,741 ops/sec [676,810..685,706] 🟢 +2.7% 644,968 ops/sec [639,486..648,309] → 654,499 ops/sec [647,542..656,020] ~ overlap (+1.5%)
f16round over 100 values 3,394 ops/sec [3,338..3,438] → 3,432 ops/sec [3,384..3,485] ~ overlap (+1.1%) 7,583 ops/sec [7,079..7,711] → 7,608 ops/sec [7,577..7,625] ~ overlap (+0.3%)
for-of.js — Interp: 🟢 6, 1 unch. · avg +4.5% · Bytecode: 🟢 4, 3 unch. · avg +5.5%
Benchmark Interpreted Δ Bytecode Δ
for...of with 10-element array 43,413 ops/sec [42,900..43,871] → 45,300 ops/sec [45,106..45,677] 🟢 +4.3% 261,708 ops/sec [257,809..269,415] → 285,803 ops/sec [283,351..289,550] 🟢 +9.2%
for...of with 100-element array 4,963 ops/sec [4,919..4,998] → 5,239 ops/sec [5,210..5,270] 🟢 +5.6% 34,788 ops/sec [33,675..35,502] → 35,431 ops/sec [34,687..35,795] ~ overlap (+1.8%)
for...of with string (10 chars) 32,940 ops/sec [32,521..33,190] → 32,478 ops/sec [32,378..32,855] ~ overlap (-1.4%) 77,452 ops/sec [76,425..80,298] → 80,862 ops/sec [80,289..81,234] ~ overlap (+4.4%)
for...of with Set (10 elements) 43,340 ops/sec [42,937..43,623] → 45,851 ops/sec [45,480..45,914] 🟢 +5.8% 245,092 ops/sec [236,780..249,977] → 249,615 ops/sec [246,558..251,680] ~ overlap (+1.8%)
for...of with Map entries (10 entries) 27,672 ops/sec [27,580..27,812] → 29,606 ops/sec [29,434..29,735] 🟢 +7.0% 30,836 ops/sec [30,142..31,229] → 33,324 ops/sec [32,820..33,634] 🟢 +8.1%
for...of with destructuring 36,471 ops/sec [36,068..36,731] → 38,869 ops/sec [38,658..39,007] 🟢 +6.6% 43,346 ops/sec [43,130..44,018] → 44,842 ops/sec [44,198..45,647] 🟢 +3.5%
for-await-of with sync array 42,057 ops/sec [41,122..42,468] → 43,715 ops/sec [43,697..43,722] 🟢 +3.9% 165,679 ops/sec [164,333..168,303] → 181,402 ops/sec [180,451..183,692] 🟢 +9.5%
helpers/bench-module.js — Interp: 0 · Bytecode: 0
Benchmark Interpreted Δ Bytecode Δ
iterators.js — Interp: 🟢 24, 18 unch. · avg +2.3% · Bytecode: 🟢 11, 🔴 7, 24 unch. · avg +0.7%
Benchmark Interpreted Δ Bytecode Δ
Iterator.from({next}).toArray() — 20 elements 15,213 ops/sec [15,183..15,285] → 15,319 ops/sec [15,117..15,490] ~ overlap (+0.7%) 19,969 ops/sec [19,542..20,509] → 21,032 ops/sec [20,916..21,123] 🟢 +5.3%
Iterator.from({next}).toArray() — 50 elements 6,685 ops/sec [6,636..6,725] → 6,672 ops/sec [6,595..6,716] ~ overlap (-0.2%) 9,079 ops/sec [8,990..9,135] → 9,366 ops/sec [9,310..9,480] 🟢 +3.2%
spread pre-wrapped iterator — 20 elements 11,345 ops/sec [11,246..11,458] → 11,596 ops/sec [11,571..11,602] 🟢 +2.2% 20,150 ops/sec [19,794..20,547] → 20,629 ops/sec [20,542..20,846] ~ overlap (+2.4%)
Iterator.from({next}).forEach — 50 elements 4,482 ops/sec [4,454..4,541] → 4,510 ops/sec [4,465..4,512] ~ overlap (+0.6%) 6,144 ops/sec [6,134..6,223] → 6,274 ops/sec [6,246..6,318] 🟢 +2.1%
Iterator.from({next}).reduce — 50 elements 4,575 ops/sec [4,536..4,610] → 4,546 ops/sec [4,525..4,565] ~ overlap (-0.6%) 6,257 ops/sec [6,209..6,313] → 6,153 ops/sec [5,997..6,231] ~ overlap (-1.7%)
wrap array iterator 161,982 ops/sec [161,334..162,322] → 161,466 ops/sec [160,777..162,669] ~ overlap (-0.3%) 175,604 ops/sec [174,170..176,603] → 175,768 ops/sec [173,234..177,888] ~ overlap (+0.1%)
wrap plain {next()} object 10,671 ops/sec [10,566..10,688] → 10,682 ops/sec [10,625..10,699] ~ overlap (+0.1%) 14,657 ops/sec [14,610..14,715] → 14,673 ops/sec [14,518..14,920] ~ overlap (+0.1%)
map + toArray (50 elements) 4,484 ops/sec [4,411..4,542] → 4,581 ops/sec [4,549..4,595] 🟢 +2.2% 6,420 ops/sec [6,353..6,429] → 6,274 ops/sec [6,104..6,300] 🔴 -2.3%
filter + toArray (50 elements) 4,389 ops/sec [4,385..4,395] → 4,443 ops/sec [4,393..4,478] ~ overlap (+1.2%) 6,300 ops/sec [6,086..6,348] → 6,123 ops/sec [6,091..6,144] ~ overlap (-2.8%)
take(10) + toArray (50 element source) 27,674 ops/sec [27,163..28,008] → 27,923 ops/sec [27,426..28,225] ~ overlap (+0.9%) 35,828 ops/sec [35,402..36,148] → 35,705 ops/sec [35,565..35,927] ~ overlap (-0.3%)
drop(40) + toArray (50 element source) 6,644 ops/sec [6,585..6,717] → 6,701 ops/sec [6,673..6,731] ~ overlap (+0.9%) 9,383 ops/sec [9,245..9,404] → 9,207 ops/sec [8,999..9,291] ~ overlap (-1.9%)
chained map + filter + take (100 element source) 8,016 ops/sec [7,904..8,146] → 8,134 ops/sec [8,046..8,174] ~ overlap (+1.5%) 11,088 ops/sec [11,012..11,138] → 11,190 ops/sec [11,112..11,225] ~ overlap (+0.9%)
some + every (50 elements) 2,587 ops/sec [2,575..2,600] → 2,590 ops/sec [2,577..2,600] ~ overlap (+0.1%) 3,704 ops/sec [3,683..3,711] → 3,666 ops/sec [3,635..3,690] ~ overlap (-1.0%)
find (50 elements) 5,608 ops/sec [5,549..5,662] → 5,674 ops/sec [5,615..5,711] ~ overlap (+1.2%) 8,012 ops/sec [7,963..8,098] → 7,879 ops/sec [7,758..7,914] 🔴 -1.7%
concat 2 arrays (10 + 10 elements) 157,206 ops/sec [154,886..159,161] → 159,445 ops/sec [156,389..161,722] ~ overlap (+1.4%) 169,638 ops/sec [165,901..171,685] → 168,730 ops/sec [166,459..169,888] ~ overlap (-0.5%)
concat 5 arrays (10 elements each) 97,948 ops/sec [97,350..98,765] → 99,748 ops/sec [99,224..100,038] 🟢 +1.8% 104,826 ops/sec [100,604..106,223] → 106,294 ops/sec [105,592..107,236] ~ overlap (+1.4%)
concat 2 arrays (20 + 20 elements) 144,239 ops/sec [143,465..144,816] → 146,464 ops/sec [145,556..146,687] 🟢 +1.5% 148,989 ops/sec [148,061..149,909] → 153,058 ops/sec [149,308..154,721] ~ overlap (+2.7%)
concat + filter + toArray (20 + 20 elements) 15,722 ops/sec [15,676..15,808] → 16,271 ops/sec [16,042..16,351] 🟢 +3.5% 22,623 ops/sec [22,549..22,905] → 22,354 ops/sec [21,994..22,517] 🔴 -1.2%
concat + map + take (20 + 20 elements, take 10) 45,484 ops/sec [45,201..45,852] → 47,854 ops/sec [47,363..48,343] 🟢 +5.2% 59,664 ops/sec [59,069..60,298] → 59,396 ops/sec [58,943..60,251] ~ overlap (-0.4%)
concat Sets (15 + 15 elements) 142,510 ops/sec [141,001..143,645] → 146,546 ops/sec [146,037..147,099] 🟢 +2.8% 149,483 ops/sec [148,571..149,825] → 149,281 ops/sec [148,796..152,547] ~ overlap (-0.1%)
concat strings (13 + 13 characters) 106,311 ops/sec [106,062..106,755] → 110,297 ops/sec [109,703..110,612] 🟢 +3.7% 102,838 ops/sec [99,816..105,660] → 105,858 ops/sec [104,549..107,360] ~ overlap (+2.9%)
zip 2 arrays (10 + 10 elements) 54,915 ops/sec [54,369..55,849] → 58,048 ops/sec [57,022..58,261] 🟢 +5.7% 54,302 ops/sec [53,897..54,986] → 55,757 ops/sec [55,004..55,836] 🟢 +2.7%
zip 3 arrays (10 elements each) 52,609 ops/sec [52,334..53,002] → 53,774 ops/sec [52,635..53,958] ~ overlap (+2.2%) 50,110 ops/sec [49,185..50,819] → 52,673 ops/sec [51,257..52,855] 🟢 +5.1%
zip 2 arrays (20 + 20 elements) 36,257 ops/sec [35,569..37,582] → 38,565 ops/sec [38,311..38,714] 🟢 +6.4% 35,216 ops/sec [34,933..35,927] → 37,464 ops/sec [37,165..37,670] 🟢 +6.4%
zip 2 arrays (50 + 50 elements) 18,888 ops/sec [18,651..19,345] → 19,595 ops/sec [19,529..19,623] 🟢 +3.7% 17,451 ops/sec [17,184..17,626] → 19,157 ops/sec [19,009..19,321] 🟢 +9.8%
zip shortest mode (20 + 10 elements) 56,862 ops/sec [56,348..57,052] → 57,778 ops/sec [57,503..57,986] 🟢 +1.6% 54,333 ops/sec [53,870..54,762] → 55,139 ops/sec [54,510..55,847] ~ overlap (+1.5%)
zip longest mode (10 + 20 elements) 33,238 ops/sec [32,907..33,662] → 33,825 ops/sec [33,460..34,066] ~ overlap (+1.8%) 31,196 ops/sec [30,378..31,543] → 32,666 ops/sec [32,090..32,952] 🟢 +4.7%
zip strict mode (20 + 20 elements) 36,167 ops/sec [35,674..36,275] → 36,767 ops/sec [36,154..37,015] ~ overlap (+1.7%) 33,676 ops/sec [33,230..34,229] → 35,781 ops/sec [35,555..36,095] 🟢 +6.2%
zip + map + toArray (20 + 20 elements) 17,110 ops/sec [16,998..17,178] → 17,319 ops/sec [17,240..17,533] 🟢 +1.2% 11,667 ops/sec [11,571..11,842] → 11,618 ops/sec [11,462..11,702] ~ overlap (-0.4%)
zip + filter + toArray (20 + 20 elements) 16,057 ops/sec [15,954..16,253] → 16,628 ops/sec [16,582..16,670] 🟢 +3.6% 11,463 ops/sec [11,388..11,625] → 11,735 ops/sec [11,530..11,739] ~ overlap (+2.4%)
zip Sets (15 + 15 elements) 44,853 ops/sec [44,157..45,699] → 45,556 ops/sec [45,297..45,894] ~ overlap (+1.6%) 42,046 ops/sec [41,729..42,430] → 44,039 ops/sec [43,323..44,462] 🟢 +4.7%
zipKeyed 2 keys (10 elements each) 53,382 ops/sec [52,769..54,168] → 55,059 ops/sec [54,634..55,440] 🟢 +3.1% 53,699 ops/sec [52,891..54,178] → 54,296 ops/sec [54,080..55,128] ~ overlap (+1.1%)
zipKeyed 3 keys (20 elements each) 27,055 ops/sec [26,704..27,418] → 28,035 ops/sec [27,698..28,174] 🟢 +3.6% 28,578 ops/sec [28,379..29,110] → 28,337 ops/sec [28,063..28,871] ~ overlap (-0.8%)
zipKeyed longest mode (10 + 20 elements) 30,674 ops/sec [30,376..30,877] → 31,464 ops/sec [31,145..31,729] 🟢 +2.6% 31,009 ops/sec [30,742..31,115] → 30,904 ops/sec [30,443..31,365] ~ overlap (-0.3%)
zipKeyed strict mode (20 + 20 elements) 32,590 ops/sec [32,464..32,783] → 33,525 ops/sec [32,731..33,653] ~ overlap (+2.9%) 33,043 ops/sec [31,972..33,358] → 34,212 ops/sec [33,634..34,338] 🟢 +3.5%
zipKeyed + filter + map (20 elements) 12,123 ops/sec [11,975..12,223] → 12,515 ops/sec [12,487..12,568] 🟢 +3.2% 14,833 ops/sec [14,496..14,919] → 14,413 ops/sec [14,299..14,591] ~ overlap (-2.8%)
array.values().map().filter().toArray() 7,873 ops/sec [7,799..7,938] → 8,032 ops/sec [8,000..8,075] 🟢 +2.0% 11,102 ops/sec [11,068..11,198] → 10,595 ops/sec [10,372..10,724] 🔴 -4.6%
array.values().take(5).toArray() 208,922 ops/sec [205,394..210,478] → 215,116 ops/sec [213,650..215,654] 🟢 +3.0% 237,451 ops/sec [236,148..239,752] → 234,093 ops/sec [232,224..236,476] ~ overlap (-1.4%)
array.values().drop(45).toArray() 193,502 ops/sec [190,009..197,661] → 202,823 ops/sec [202,310..202,902] 🟢 +4.8% 215,765 ops/sec [211,576..218,879] → 215,124 ops/sec [215,051..215,304] ~ overlap (-0.3%)
map.entries() chained helpers 9,785 ops/sec [9,630..9,956] → 10,017 ops/sec [9,961..10,093] 🟢 +2.4% 6,361 ops/sec [6,239..6,417] → 6,020 ops/sec [5,953..6,113] 🔴 -5.4%
set.values() chained helpers 16,614 ops/sec [16,557..16,729] → 17,084 ops/sec [16,900..17,331] 🟢 +2.8% 23,752 ops/sec [23,609..23,919] → 22,347 ops/sec [22,143..22,691] 🔴 -5.9%
string iterator map + toArray 13,121 ops/sec [12,937..13,360] → 13,824 ops/sec [13,669..13,921] 🟢 +5.4% 14,563 ops/sec [14,486..14,792] → 14,058 ops/sec [13,824..14,221] 🔴 -3.5%
json.js — Interp: 🟢 10, 10 unch. · avg +1.9% · Bytecode: 🟢 3, 🔴 4, 13 unch. · avg +0.1%
Benchmark Interpreted Δ Bytecode Δ
parse simple object 143,467 ops/sec [140,973..144,296] → 147,700 ops/sec [145,173..148,862] 🟢 +3.0% 148,662 ops/sec [147,276..149,918] → 147,258 ops/sec [145,695..150,140] ~ overlap (-0.9%)
parse nested object 92,157 ops/sec [91,572..93,384] → 93,043 ops/sec [92,584..93,195] ~ overlap (+1.0%) 94,012 ops/sec [93,376..95,430] → 94,937 ops/sec [92,829..96,058] ~ overlap (+1.0%)
parse array of objects 53,580 ops/sec [52,977..54,069] → 55,145 ops/sec [54,988..55,286] 🟢 +2.9% 53,858 ops/sec [53,486..53,961] → 54,535 ops/sec [54,142..55,023] 🟢 +1.3%
parse large flat object 59,650 ops/sec [58,872..60,056] → 60,369 ops/sec [59,759..60,586] ~ overlap (+1.2%) 60,055 ops/sec [59,405..60,302] → 59,081 ops/sec [58,557..59,431] ~ overlap (-1.6%)
parse mixed types 66,931 ops/sec [65,837..68,040] → 67,915 ops/sec [67,290..68,559] ~ overlap (+1.5%) 67,303 ops/sec [66,864..67,734] → 68,941 ops/sec [67,720..69,105] ~ overlap (+2.4%)
stringify simple object 151,215 ops/sec [150,136..151,674] → 149,931 ops/sec [148,678..151,125] ~ overlap (-0.8%) 150,365 ops/sec [149,502..152,251] → 147,687 ops/sec [146,761..149,378] 🔴 -1.8%
stringify nested object 83,227 ops/sec [82,160..83,406] → 83,550 ops/sec [82,772..83,967] ~ overlap (+0.4%) 81,980 ops/sec [81,794..83,217] → 84,155 ops/sec [82,518..84,311] ~ overlap (+2.7%)
stringify array of objects 35,684 ops/sec [35,487..35,813] → 35,708 ops/sec [35,593..35,814] ~ overlap (+0.1%) 35,731 ops/sec [35,626..35,758] → 35,534 ops/sec [35,424..35,579] 🔴 -0.6%
stringify mixed types 63,761 ops/sec [62,991..64,565] → 64,085 ops/sec [63,353..64,427] ~ overlap (+0.5%) 61,032 ops/sec [60,537..61,233] → 62,246 ops/sec [61,998..62,705] 🟢 +2.0%
reviver doubles numbers 31,120 ops/sec [30,920..31,271] → 32,523 ops/sec [32,316..32,741] 🟢 +4.5% 34,966 ops/sec [34,878..35,010] → 34,686 ops/sec [34,469..34,994] ~ overlap (-0.8%)
reviver filters properties 29,154 ops/sec [28,570..29,277] → 30,083 ops/sec [29,788..30,242] 🟢 +3.2% 31,212 ops/sec [30,809..31,405] → 31,021 ops/sec [30,689..31,188] ~ overlap (-0.6%)
reviver on nested object 35,548 ops/sec [34,082..36,080] → 37,284 ops/sec [37,112..37,370] 🟢 +4.9% 38,732 ops/sec [38,667..38,784] → 37,713 ops/sec [37,657..37,745] 🔴 -2.6%
reviver on array 19,824 ops/sec [19,620..20,055] → 20,208 ops/sec [20,122..20,285] 🟢 +1.9% 21,875 ops/sec [21,736..21,965] → 21,607 ops/sec [21,494..21,666] 🔴 -1.2%
replacer function doubles numbers 38,109 ops/sec [37,731..38,381] → 39,897 ops/sec [39,183..40,202] 🟢 +4.7% 43,206 ops/sec [42,726..44,225] → 43,131 ops/sec [42,606..43,549] ~ overlap (-0.2%)
replacer function excludes properties 51,855 ops/sec [51,219..52,337] → 53,773 ops/sec [53,538..53,901] 🟢 +3.7% 52,987 ops/sec [52,712..53,626] → 53,499 ops/sec [52,599..53,907] ~ overlap (+1.0%)
array replacer (allowlist) 91,121 ops/sec [90,637..92,103] → 93,294 ops/sec [92,436..94,448] 🟢 +2.4% 92,411 ops/sec [90,150..93,869] → 91,290 ops/sec [90,915..92,551] ~ overlap (-1.2%)
stringify with 2-space indent 73,537 ops/sec [72,605..74,026] → 74,927 ops/sec [74,679..75,920] 🟢 +1.9% 72,743 ops/sec [72,239..73,388] → 74,000 ops/sec [73,495..74,460] 🟢 +1.7%
stringify with tab indent 73,166 ops/sec [72,660..73,702] → 73,671 ops/sec [73,460..74,490] ~ overlap (+0.7%) 73,263 ops/sec [72,415..74,132] → 73,124 ops/sec [72,642..74,647] ~ overlap (-0.2%)
parse then stringify 42,721 ops/sec [42,525..43,499] → 42,644 ops/sec [42,432..43,089] ~ overlap (-0.2%) 43,868 ops/sec [43,600..44,107] → 44,354 ops/sec [44,081..44,505] ~ overlap (+1.1%)
stringify then parse 25,203 ops/sec [25,034..25,418] → 25,269 ops/sec [25,104..25,440] ~ overlap (+0.3%) 25,861 ops/sec [25,619..26,391] → 26,217 ops/sec [26,101..26,340] ~ overlap (+1.4%)
jsx.jsx — Interp: 🟢 11, 10 unch. · avg +2.0% · Bytecode: 🔴 13, 8 unch. · avg -1.7%
Benchmark Interpreted Δ Bytecode Δ
simple element 213,792 ops/sec [212,443..214,752] → 214,186 ops/sec [213,527..214,677] ~ overlap (+0.2%) 316,786 ops/sec [314,562..317,667] → 314,545 ops/sec [313,526..314,959] ~ overlap (-0.7%)
self-closing element 220,357 ops/sec [216,948..221,206] → 221,784 ops/sec [220,841..222,095] ~ overlap (+0.6%) 352,922 ops/sec [351,309..355,363] → 351,995 ops/sec [350,493..352,423] ~ overlap (-0.3%)
element with string attribute 179,391 ops/sec [178,719..181,245] → 180,555 ops/sec [179,411..180,776] ~ overlap (+0.6%) 250,154 ops/sec [248,789..251,370] → 249,058 ops/sec [244,576..251,004] ~ overlap (-0.4%)
element with multiple attributes 151,082 ops/sec [148,194..152,195] → 163,778 ops/sec [162,609..164,312] 🟢 +8.4% 197,559 ops/sec [196,482..197,911] → 192,815 ops/sec [190,240..194,657] 🔴 -2.4%
element with expression attribute 165,248 ops/sec [164,727..165,712] → 169,402 ops/sec [168,010..170,430] 🟢 +2.5% 250,860 ops/sec [248,876..253,568] → 250,349 ops/sec [246,359..253,551] ~ overlap (-0.2%)
text child 212,068 ops/sec [210,879..214,800] → 214,308 ops/sec [213,853..215,035] ~ overlap (+1.1%) 325,669 ops/sec [321,671..327,163] → 319,926 ops/sec [319,508..320,337] 🔴 -1.8%
expression child 208,679 ops/sec [206,913..209,959] → 211,022 ops/sec [210,210..212,813] 🟢 +1.1% 319,602 ops/sec [317,041..320,885] → 315,635 ops/sec [314,800..315,792] 🔴 -1.2%
mixed text and expression 196,514 ops/sec [195,060..197,095] → 199,349 ops/sec [198,669..200,258] 🟢 +1.4% 279,212 ops/sec [277,699..279,814] → 273,126 ops/sec [270,018..275,342] 🔴 -2.2%
nested elements (3 levels) 79,218 ops/sec [78,726..79,549] → 80,712 ops/sec [80,272..80,997] 🟢 +1.9% 121,930 ops/sec [121,529..122,676] → 119,420 ops/sec [118,862..120,082] 🔴 -2.1%
sibling children 59,335 ops/sec [58,767..59,774] → 58,819 ops/sec [58,440..59,206] ~ overlap (-0.9%) 86,254 ops/sec [85,572..87,828] → 86,484 ops/sec [85,696..87,470] ~ overlap (+0.3%)
component element 148,731 ops/sec [147,718..149,395] → 153,266 ops/sec [149,481..153,525] 🟢 +3.0% 226,046 ops/sec [224,631..226,912] → 222,655 ops/sec [221,646..224,031] 🔴 -1.5%
component with children 92,285 ops/sec [90,712..92,988] → 92,781 ops/sec [91,883..93,556] ~ overlap (+0.5%) 137,921 ops/sec [135,990..139,304] → 133,120 ops/sec [132,792..133,670] 🔴 -3.5%
dotted component 124,801 ops/sec [122,989..125,053] → 128,572 ops/sec [128,048..129,169] 🟢 +3.0% 171,969 ops/sec [170,463..173,281] → 170,218 ops/sec [168,198..171,815] ~ overlap (-1.0%)
empty fragment 220,861 ops/sec [219,836..222,209] → 222,380 ops/sec [220,948..224,222] ~ overlap (+0.7%) 393,134 ops/sec [391,634..394,844] → 384,593 ops/sec [384,025..387,822] 🔴 -2.2%
fragment with children 58,789 ops/sec [58,316..59,172] → 60,734 ops/sec [60,579..60,823] 🟢 +3.3% 89,921 ops/sec [88,468..91,658] → 88,501 ops/sec [86,772..89,622] ~ overlap (-1.6%)
spread attributes 109,639 ops/sec [107,860..110,501] → 109,952 ops/sec [107,128..110,339] ~ overlap (+0.3%) 130,198 ops/sec [128,417..131,655] → 125,193 ops/sec [124,584..126,067] 🔴 -3.8%
spread with overrides 96,083 ops/sec [94,833..97,219] → 96,740 ops/sec [95,987..97,231] ~ overlap (+0.7%) 112,958 ops/sec [110,963..113,867] → 109,786 ops/sec [109,124..110,069] 🔴 -2.8%
shorthand props 160,926 ops/sec [159,886..161,846] → 165,209 ops/sec [159,697..166,176] ~ overlap (+2.7%) 235,460 ops/sec [234,491..237,382] → 227,762 ops/sec [226,870..229,708] 🔴 -3.3%
nav bar structure 27,199 ops/sec [27,052..27,750] → 28,227 ops/sec [27,865..28,326] 🟢 +3.8% 39,067 ops/sec [38,872..39,478] → 38,809 ops/sec [38,610..39,093] ~ overlap (-0.7%)
card component tree 31,881 ops/sec [31,624..32,074] → 32,615 ops/sec [32,484..33,167] 🟢 +2.3% 44,331 ops/sec [43,985..44,889] → 43,508 ops/sec [43,024..43,820] 🔴 -1.9%
10 list items via Array.from 14,322 ops/sec [14,138..14,542] → 14,889 ops/sec [14,671..15,123] 🟢 +4.0% 19,022 ops/sec [18,827..19,161] → 18,348 ops/sec [18,162..18,473] 🔴 -3.5%
modules.js — Interp: 🟢 2, 🔴 3, 4 unch. · avg -0.8% · Bytecode: 🔴 7, 2 unch. · avg -1.0%
Benchmark Interpreted Δ Bytecode Δ
call imported function 455,601 ops/sec [445,943..458,644] → 435,920 ops/sec [433,152..438,940] 🔴 -4.3% 661,119 ops/sec [657,292..665,497] → 642,293 ops/sec [636,064..644,460] 🔴 -2.8%
call two imported functions 254,069 ops/sec [251,508..255,531] → 242,598 ops/sec [241,450..243,379] 🔴 -4.5% 346,379 ops/sec [342,986..351,423] → 346,899 ops/sec [342,066..347,909] ~ overlap (+0.1%)
read imported constant 1,586,693 ops/sec [1,569,122..1,595,572] → 1,610,639 ops/sec [1,603,603..1,613,274] 🟢 +1.5% 5,660,524 ops/sec [5,653,018..5,663,713] → 5,588,955 ops/sec [5,538,082..5,612,629] 🔴 -1.3%
read imported string 1,583,169 ops/sec [1,573,127..1,596,624] → 1,596,480 ops/sec [1,581,592..1,606,281] ~ overlap (+0.8%) 5,664,945 ops/sec [5,645,915..5,668,288] → 5,604,848 ops/sec [5,596,015..5,609,320] 🔴 -1.1%
read JSON string property 1,597,267 ops/sec [1,570,427..1,615,416] → 1,601,887 ops/sec [1,595,444..1,608,953] ~ overlap (+0.3%) 5,646,094 ops/sec [5,621,507..5,654,845] → 5,607,189 ops/sec [5,585,986..5,609,466] 🔴 -0.7%
read JSON number property 1,576,136 ops/sec [1,570,303..1,586,136] → 1,583,203 ops/sec [1,567,170..1,598,140] ~ overlap (+0.4%) 5,658,923 ops/sec [5,642,985..5,666,792] → 5,609,047 ops/sec [5,593,853..5,612,671] 🔴 -0.9%
read JSON boolean property 1,581,314 ops/sec [1,578,592..1,593,807] → 1,609,510 ops/sec [1,597,244..1,627,006] 🟢 +1.8% 5,662,491 ops/sec [5,655,656..5,666,155] → 5,607,785 ops/sec [5,601,914..5,614,216] 🔴 -1.0%
read JSON array property 1,602,861 ops/sec [1,596,478..1,608,851] → 1,601,980 ops/sec [1,596,116..1,629,103] ~ overlap (-0.1%) 5,664,004 ops/sec [5,635,752..5,664,594] → 5,603,418 ops/sec [5,593,376..5,695,409] ~ overlap (-1.1%)
read multiple JSON properties 932,553 ops/sec [920,371..942,455] → 900,208 ops/sec [894,001..903,757] 🔴 -3.5% 4,830,815 ops/sec [4,821,457..4,842,445] → 4,802,928 ops/sec [4,795,221..4,806,204] 🔴 -0.6%
numbers.js — Interp: 🟢 5, 6 unch. · avg +2.1% · Bytecode: 🔴 8, 3 unch. · avg -2.8%
Benchmark Interpreted Δ Bytecode Δ
integer arithmetic 481,093 ops/sec [473,768..490,227] → 503,253 ops/sec [496,117..509,540] 🟢 +4.6% 1,732,305 ops/sec [1,703,576..1,756,751] → 1,738,008 ops/sec [1,686,412..1,768,294] ~ overlap (+0.3%)
floating point arithmetic 555,558 ops/sec [551,052..559,546] → 599,814 ops/sec [595,698..607,476] 🟢 +8.0% 941,880 ops/sec [933,894..948,333] → 918,546 ops/sec [916,962..923,156] 🔴 -2.5%
number coercion 175,642 ops/sec [175,473..176,306] → 176,442 ops/sec [171,952..179,719] ~ overlap (+0.5%) 207,930 ops/sec [206,052..210,645] → 208,343 ops/sec [206,525..209,634] ~ overlap (+0.2%)
toFixed 99,158 ops/sec [98,409..99,570] → 99,680 ops/sec [98,938..100,232] ~ overlap (+0.5%) 109,128 ops/sec [107,860..109,262] → 105,006 ops/sec [104,214..105,616] 🔴 -3.8%
toString 148,777 ops/sec [148,103..149,932] → 151,045 ops/sec [150,420..152,034] 🟢 +1.5% 178,582 ops/sec [177,677..179,244] → 176,738 ops/sec [174,629..178,577] ~ overlap (-1.0%)
valueOf 224,877 ops/sec [221,391..226,069] → 226,400 ops/sec [224,747..227,281] ~ overlap (+0.7%) 259,555 ops/sec [258,329..260,288] → 253,301 ops/sec [250,164..256,834] 🔴 -2.4%
toPrecision 140,016 ops/sec [139,018..140,515] → 139,421 ops/sec [138,975..139,542] ~ overlap (-0.4%) 157,752 ops/sec [156,771..158,328] → 155,071 ops/sec [153,817..156,495] 🔴 -1.7%
Number.isNaN 299,814 ops/sec [298,743..301,373] → 306,788 ops/sec [304,753..310,925] 🟢 +2.3% 302,383 ops/sec [300,979..303,067] → 288,836 ops/sec [287,268..291,339] 🔴 -4.5%
Number.isFinite 295,952 ops/sec [293,017..297,447] → 298,718 ops/sec [295,325..300,727] ~ overlap (+0.9%) 290,241 ops/sec [288,369..290,660] → 276,310 ops/sec [273,301..276,767] 🔴 -4.8%
Number.isInteger 306,358 ops/sec [305,271..309,132] → 311,801 ops/sec [305,512..313,489] ~ overlap (+1.8%) 311,472 ops/sec [311,359..311,802] → 298,930 ops/sec [297,375..301,012] 🔴 -4.0%
Number.parseInt and parseFloat 236,707 ops/sec [231,566..237,854] → 242,664 ops/sec [239,702..245,385] 🟢 +2.5% 241,711 ops/sec [238,371..244,734] → 225,828 ops/sec [225,086..226,297] 🔴 -6.6%
objects.js — Interp: 🟢 7 · avg +4.5% · Bytecode: 🟢 1, 🔴 5, 1 unch. · avg -2.5%
Benchmark Interpreted Δ Bytecode Δ
create simple object 472,897 ops/sec [466,221..475,999] → 497,669 ops/sec [494,403..498,943] 🟢 +5.2% 573,692 ops/sec [569,750..574,428] → 544,808 ops/sec [538,353..547,156] 🔴 -5.0%
create nested object 237,663 ops/sec [234,437..238,406] → 246,556 ops/sec [245,391..247,030] 🟢 +3.7% 240,996 ops/sec [238,635..242,419] → 235,957 ops/sec [235,718..239,211] ~ overlap (-2.1%)
create 50 objects via Array.from 9,158 ops/sec [9,018..9,251] → 9,714 ops/sec [9,670..9,749] 🟢 +6.1% 9,710 ops/sec [9,659..9,762] → 9,454 ops/sec [9,432..9,475] 🔴 -2.6%
property read 525,428 ops/sec [519,326..528,077] → 538,891 ops/sec [536,701..540,505] 🟢 +2.6% 735,046 ops/sec [724,653..737,454] → 759,577 ops/sec [758,034..777,298] 🟢 +3.3%
Object.keys 261,657 ops/sec [258,305..262,322] → 269,843 ops/sec [269,067..271,459] 🟢 +3.1% 305,981 ops/sec [303,111..308,625] → 288,215 ops/sec [282,026..293,841] 🔴 -5.8%
Object.entries 97,197 ops/sec [96,548..98,147] → 104,328 ops/sec [103,009..104,964] 🟢 +7.3% 110,319 ops/sec [109,760..111,786] → 107,424 ops/sec [106,620..107,946] 🔴 -2.6%
spread operator 179,803 ops/sec [177,888..180,847] → 186,403 ops/sec [185,947..187,245] 🟢 +3.7% 206,809 ops/sec [205,791..207,908] → 201,764 ops/sec [200,530..203,217] 🔴 -2.4%
promises.js — Interp: 🟢 7, 🔴 1, 4 unch. · avg +1.4% · Bytecode: 🔴 9, 3 unch. · avg -2.6%
Benchmark Interpreted Δ Bytecode Δ
Promise.resolve(value) 524,329 ops/sec [517,569..525,976] → 500,215 ops/sec [490,654..510,191] 🔴 -4.6% 545,106 ops/sec [541,512..549,062] → 524,760 ops/sec [519,895..534,452] 🔴 -3.7%
new Promise(resolve => resolve(value)) 176,972 ops/sec [175,934..177,663] → 185,894 ops/sec [184,915..186,546] 🟢 +5.0% 229,266 ops/sec [228,681..230,289] → 223,249 ops/sec [221,693..224,637] 🔴 -2.6%
Promise.reject(reason) 540,339 ops/sec [521,878..541,152] → 528,846 ops/sec [526,371..531,944] ~ overlap (-2.1%) 520,838 ops/sec [518,178..521,586] → 504,108 ops/sec [500,038..507,011] 🔴 -3.2%
resolve + then (1 handler) 172,193 ops/sec [170,144..174,439] → 175,305 ops/sec [174,427..176,688] ~ overlap (+1.8%) 207,413 ops/sec [206,697..209,219] → 200,987 ops/sec [200,518..201,989] 🔴 -3.1%
resolve + then chain (3 deep) 69,168 ops/sec [68,347..70,049] → 70,255 ops/sec [70,067..70,667] 🟢 +1.6% 81,877 ops/sec [81,293..82,813] → 81,529 ops/sec [80,630..83,304] ~ overlap (-0.4%)
resolve + then chain (10 deep) 22,142 ops/sec [21,893..22,549] → 22,963 ops/sec [22,654..23,042] 🟢 +3.7% 26,745 ops/sec [26,608..26,793] → 26,498 ops/sec [26,244..26,780] ~ overlap (-0.9%)
reject + catch + then 99,553 ops/sec [98,582..100,044] → 100,532 ops/sec [100,180..100,821] 🟢 +1.0% 108,910 ops/sec [108,056..109,886] → 107,244 ops/sec [105,200..109,797] ~ overlap (-1.5%)
resolve + finally + then 82,112 ops/sec [81,162..82,829] → 84,513 ops/sec [84,047..85,031] 🟢 +2.9% 93,508 ops/sec [92,540..94,041] → 91,296 ops/sec [90,899..92,037] 🔴 -2.4%
Promise.all (5 resolved) 30,724 ops/sec [30,006..31,162] → 31,407 ops/sec [31,107..31,514] ~ overlap (+2.2%) 33,110 ops/sec [32,621..33,337] → 31,332 ops/sec [31,192..31,636] 🔴 -5.4%
Promise.race (5 resolved) 33,123 ops/sec [32,887..33,367] → 33,425 ops/sec [33,206..33,777] ~ overlap (+0.9%) 34,492 ops/sec [34,121..34,624] → 33,630 ops/sec [33,406..34,061] 🔴 -2.5%
Promise.allSettled (5 mixed) 25,988 ops/sec [25,609..26,165] → 26,479 ops/sec [26,240..26,581] 🟢 +1.9% 27,351 ops/sec [27,125..27,634] → 26,606 ops/sec [26,239..26,795] 🔴 -2.7%
Promise.any (5 mixed) 30,519 ops/sec [30,314..30,807] → 31,363 ops/sec [31,106..31,549] 🟢 +2.8% 32,164 ops/sec [31,899..32,459] → 31,186 ops/sec [30,837..31,490] 🔴 -3.0%
regexp.js — Interp: 🟢 8, 3 unch. · avg +2.8% · Bytecode: 🔴 5, 6 unch. · avg -2.9%
Benchmark Interpreted Δ Bytecode Δ
regex literal creation 133,743 ops/sec [132,593..136,032] → 143,822 ops/sec [140,528..144,734] 🟢 +7.5% 126,053 ops/sec [125,487..126,631] → 124,887 ops/sec [123,817..126,635] ~ overlap (-0.9%)
new RegExp(pattern, flags) 119,330 ops/sec [118,037..120,151] → 123,371 ops/sec [122,046..123,580] 🟢 +3.4% 124,135 ops/sec [122,942..125,765] → 123,884 ops/sec [122,538..125,213] ~ overlap (-0.2%)
RegExp(existingRegex) returns the same regex 670,085 ops/sec [666,484..672,038] → 692,417 ops/sec [689,601..698,731] 🟢 +3.3% 1,012,802 ops/sec [1,006,554..1,014,208] → 948,711 ops/sec [946,601..949,651] 🔴 -6.3%
test() on a global regex 130,087 ops/sec [129,972..130,282] → 129,176 ops/sec [128,647..130,163] ~ overlap (-0.7%) 151,173 ops/sec [150,016..152,673] → 143,382 ops/sec [142,221..147,813] 🔴 -5.2%
exec() with capture groups 106,541 ops/sec [105,775..107,023] → 107,062 ops/sec [106,371..107,752] ~ overlap (+0.5%) 121,589 ops/sec [120,886..122,950] → 117,357 ops/sec [116,595..118,094] 🔴 -3.5%
toString() 463,402 ops/sec [456,568..464,435] → 462,584 ops/sec [458,313..467,652] ~ overlap (-0.2%) 624,846 ops/sec [619,977..629,685] → 614,535 ops/sec [611,728..617,871] 🔴 -1.7%
match() with global regex 35,798 ops/sec [35,251..35,985] → 37,020 ops/sec [36,909..37,201] 🟢 +3.4% 37,091 ops/sec [36,531..37,268] → 35,307 ops/sec [34,753..35,652] 🔴 -4.8%
matchAll() with capture groups 18,673 ops/sec [18,570..18,862] → 19,028 ops/sec [18,935..19,116] 🟢 +1.9% 22,550 ops/sec [22,464..22,630] → 22,260 ops/sec [21,991..22,678] ~ overlap (-1.3%)
replace() with global regex 34,315 ops/sec [33,978..34,747] → 35,706 ops/sec [35,450..35,883] 🟢 +4.1% 35,759 ops/sec [34,676..35,912] → 34,337 ops/sec [34,003..34,839] ~ overlap (-4.0%)
search() with regex 70,577 ops/sec [69,314..71,741] → 73,806 ops/sec [73,319..73,946] 🟢 +4.6% 70,558 ops/sec [68,560..72,564] → 68,126 ops/sec [67,577..69,943] ~ overlap (-3.4%)
split() with regex separator 36,012 ops/sec [35,938..36,163] → 37,239 ops/sec [37,134..37,431] 🟢 +3.4% 35,714 ops/sec [35,676..36,344] → 35,574 ops/sec [35,335..35,898] ~ overlap (-0.4%)
strings.js — Interp: 🟢 9, 🔴 1, 9 unch. · avg +1.6% · Bytecode: 🔴 10, 9 unch. · avg -2.3%
Benchmark Interpreted Δ Bytecode Δ
string concatenation 385,975 ops/sec [382,332..388,194] → 410,075 ops/sec [405,924..412,470] 🟢 +6.2% 278,244 ops/sec [271,080..283,271] → 267,405 ops/sec [255,394..269,157] 🔴 -3.9%
template literal 748,118 ops/sec [742,018..756,772] → 788,190 ops/sec [783,344..791,799] 🟢 +5.4% 486,013 ops/sec [439,218..489,368] → 470,632 ops/sec [468,510..481,045] ~ overlap (-3.2%)
string repeat 381,966 ops/sec [373,192..384,141] → 385,491 ops/sec [384,267..385,613] 🟢 +0.9% 441,238 ops/sec [435,442..446,391] → 437,599 ops/sec [432,219..443,755] ~ overlap (-0.8%)
split and join 131,316 ops/sec [130,931..131,545] → 136,332 ops/sec [135,653..137,105] 🟢 +3.8% 148,827 ops/sec [148,051..150,568] → 142,574 ops/sec [142,062..143,817] 🔴 -4.2%
indexOf and includes 157,928 ops/sec [157,839..159,849] → 162,797 ops/sec [161,514..163,061] 🟢 +3.1% 173,852 ops/sec [172,552..175,575] → 170,614 ops/sec [169,307..172,285] 🔴 -1.9%
toUpperCase and toLowerCase 248,642 ops/sec [246,349..251,646] → 255,256 ops/sec [253,765..255,971] 🟢 +2.7% 327,820 ops/sec [324,806..329,666] → 313,115 ops/sec [310,856..314,736] 🔴 -4.5%
slice and substring 147,355 ops/sec [145,379..148,532] → 148,271 ops/sec [147,046..148,919] ~ overlap (+0.6%) 186,931 ops/sec [184,295..188,841] → 183,810 ops/sec [179,814..189,763] ~ overlap (-1.7%)
trim operations 184,657 ops/sec [181,180..186,767] → 188,866 ops/sec [187,474..190,429] 🟢 +2.3% 237,239 ops/sec [236,239..239,086] → 228,424 ops/sec [225,730..229,332] 🔴 -3.7%
replace and replaceAll 197,111 ops/sec [194,316..199,084] → 197,159 ops/sec [195,845..198,009] ~ overlap (+0.0%) 210,689 ops/sec [209,912..212,234] → 208,037 ops/sec [207,022..208,442] 🔴 -1.3%
startsWith and endsWith 130,833 ops/sec [130,190..131,917] → 131,022 ops/sec [130,528..132,119] ~ overlap (+0.1%) 145,535 ops/sec [144,264..147,731] → 140,016 ops/sec [139,147..141,273] 🔴 -3.8%
padStart and padEnd 189,530 ops/sec [188,371..191,095] → 190,739 ops/sec [189,995..191,777] ~ overlap (+0.6%) 214,635 ops/sec [213,948..216,140] → 212,430 ops/sec [211,423..216,453] ~ overlap (-1.0%)
identity tag, no substitutions 471,627 ops/sec [463,068..476,731] → 469,767 ops/sec [464,697..475,345] ~ overlap (-0.4%) 1,426,583 ops/sec [1,420,449..1,428,879] → 1,386,295 ops/sec [1,384,108..1,388,071] 🔴 -2.8%
tag with 1 substitution 87,569 ops/sec [86,237..88,853] → 86,785 ops/sec [86,337..87,165] ~ overlap (-0.9%) 107,098 ops/sec [105,741..108,302] → 106,098 ops/sec [104,316..107,386] ~ overlap (-0.9%)
tag with 3 substitutions 48,097 ops/sec [47,859..48,503] → 47,742 ops/sec [47,250..48,023] ~ overlap (-0.7%) 61,829 ops/sec [61,473..62,399] → 61,371 ops/sec [59,806..62,506] ~ overlap (-0.7%)
tag with 6 substitutions 28,644 ops/sec [28,488..28,787] → 29,219 ops/sec [29,098..29,286] 🟢 +2.0% 38,011 ops/sec [37,262..38,090] → 37,455 ops/sec [37,307..37,721] ~ overlap (-1.5%)
String.raw, no substitutions 580,792 ops/sec [574,496..590,561] → 589,212 ops/sec [581,413..591,196] ~ overlap (+1.4%) 570,793 ops/sec [550,313..575,696] → 559,457 ops/sec [549,481..566,209] ~ overlap (-2.0%)
String.raw, 2 substitutions 406,650 ops/sec [399,608..413,543] → 423,295 ops/sec [418,911..425,609] 🟢 +4.1% 418,965 ops/sec [414,223..423,264] → 404,041 ops/sec [402,408..404,479] 🔴 -3.6%
tag accessing .raw array 170,756 ops/sec [169,281..172,049] → 168,482 ops/sec [168,075..169,152] 🔴 -1.3% 192,184 ops/sec [186,174..204,174] → 195,492 ops/sec [192,638..196,267] ~ overlap (+1.7%)
method as tag (this binding) 62,454 ops/sec [60,103..63,604] → 62,886 ops/sec [62,827..63,665] ~ overlap (+0.7%) 85,588 ops/sec [82,913..86,325] → 81,810 ops/sec [79,729..82,900] 🔴 -4.4%
typed-arrays.js — Interp: 🟢 9, 🔴 1, 12 unch. · avg +1.5% · Bytecode: 🔴 16, 6 unch. · avg -2.8%
Benchmark Interpreted Δ Bytecode Δ
new Int32Array(0) 332,346 ops/sec [331,059..335,278] → 333,578 ops/sec [332,510..335,155] ~ overlap (+0.4%) 373,030 ops/sec [372,367..376,722] → 366,326 ops/sec [364,641..367,687] 🔴 -1.8%
new Int32Array(100) 301,247 ops/sec [299,380..303,848] → 301,494 ops/sec [300,234..302,481] ~ overlap (+0.1%) 332,996 ops/sec [331,556..334,225] → 332,062 ops/sec [330,380..336,046] ~ overlap (-0.3%)
new Int32Array(1000) 186,312 ops/sec [185,591..188,055] → 184,153 ops/sec [183,148..185,456] 🔴 -1.2% 197,607 ops/sec [196,579..198,212] → 195,193 ops/sec [193,296..196,216] 🔴 -1.2%
new Float64Array(100) 286,641 ops/sec [277,441..288,052] → 279,894 ops/sec [276,878..282,761] ~ overlap (-2.4%) 308,897 ops/sec [304,955..310,312] → 303,950 ops/sec [301,946..305,215] ~ overlap (-1.6%)
Int32Array.from([...]) 172,165 ops/sec [168,955..172,785] → 168,697 ops/sec [167,596..170,042] ~ overlap (-2.0%) 194,326 ops/sec [190,779..197,640] → 178,442 ops/sec [176,554..179,774] 🔴 -8.2%
Int32Array.of(1, 2, 3, 4, 5) 302,628 ops/sec [297,131..304,649] → 304,727 ops/sec [302,616..305,887] ~ overlap (+0.7%) 352,924 ops/sec [343,295..355,037] → 326,556 ops/sec [323,741..329,507] 🔴 -7.5%
sequential write 100 elements 3,399 ops/sec [3,377..3,422] → 3,539 ops/sec [3,526..3,571] 🟢 +4.1% 14,994 ops/sec [14,821..15,204] → 15,104 ops/sec [14,963..15,156] ~ overlap (+0.7%)
sequential read 100 elements 3,484 ops/sec [3,448..3,517] → 3,624 ops/sec [3,601..3,646] 🟢 +4.0% 15,237 ops/sec [15,222..15,287] → 14,923 ops/sec [14,761..14,990] 🔴 -2.1%
Float64Array write 100 elements 3,178 ops/sec [3,085..3,197] → 3,332 ops/sec [3,323..3,363] 🟢 +4.9% 9,904 ops/sec [9,816..9,952] → 9,780 ops/sec [9,627..9,863] ~ overlap (-1.2%)
fill(42) 46,592 ops/sec [46,471..46,819] → 46,846 ops/sec [46,688..46,898] ~ overlap (+0.5%) 47,252 ops/sec [47,205..47,273] → 47,280 ops/sec [47,217..47,344] ~ overlap (+0.1%)
slice() 196,783 ops/sec [193,603..199,555] → 205,063 ops/sec [202,574..207,479] 🟢 +4.2% 224,335 ops/sec [222,336..225,197] → 216,813 ops/sec [215,237..217,014] 🔴 -3.4%
map(x => x * 2) 7,669 ops/sec [7,619..7,745] → 7,970 ops/sec [7,868..8,030] 🟢 +3.9% 9,908 ops/sec [9,632..10,025] → 9,324 ops/sec [9,256..9,363] 🔴 -5.9%
filter(x => x > 50) 7,948 ops/sec [7,858..7,980] → 7,976 ops/sec [7,937..8,002] ~ overlap (+0.4%) 10,836 ops/sec [10,727..10,930] → 9,800 ops/sec [9,718..9,955] 🔴 -9.6%
reduce (sum) 7,732 ops/sec [7,674..7,935] → 7,789 ops/sec [7,756..7,807] ~ overlap (+0.7%) 9,577 ops/sec [9,555..9,616] → 8,971 ops/sec [8,919..9,168] 🔴 -6.3%
sort() 165,687 ops/sec [164,354..166,453] → 167,489 ops/sec [164,612..167,840] ~ overlap (+1.1%) 181,216 ops/sec [181,011..181,349] → 180,272 ops/sec [180,117..180,373] 🔴 -0.5%
indexOf() 401,454 ops/sec [397,450..404,034] → 409,118 ops/sec [407,854..412,993] 🟢 +1.9% 487,451 ops/sec [485,769..488,701] → 481,466 ops/sec [477,536..482,534] 🔴 -1.2%
reverse() 325,995 ops/sec [322,807..328,059] → 325,280 ops/sec [323,321..326,072] ~ overlap (-0.2%) 358,506 ops/sec [357,890..358,988] → 354,171 ops/sec [353,536..354,285] 🔴 -1.2%
create view over existing buffer 388,172 ops/sec [383,366..392,890] → 395,374 ops/sec [392,579..398,102] ~ overlap (+1.9%) 453,044 ops/sec [449,225..454,668] → 446,243 ops/sec [443,471..448,626] 🔴 -1.5%
subarray() 397,668 ops/sec [394,582..401,813] → 403,431 ops/sec [400,127..409,499] ~ overlap (+1.4%) 508,882 ops/sec [503,463..516,245] → 485,807 ops/sec [483,442..488,898] 🔴 -4.5%
set() from array 540,403 ops/sec [538,087..547,615] → 551,623 ops/sec [549,679..553,852] 🟢 +2.1% 629,295 ops/sec [627,310..631,332] → 612,331 ops/sec [611,261..614,481] 🔴 -2.7%
for-of loop 4,846 ops/sec [4,760..4,871] → 5,048 ops/sec [5,010..5,064] 🟢 +4.2% 27,227 ops/sec [27,160..27,371] → 27,308 ops/sec [27,218..27,325] ~ overlap (+0.3%)
spread into array 17,454 ops/sec [17,375..17,511] → 17,742 ops/sec [17,678..17,815] 🟢 +1.6% 97,596 ops/sec [96,951..98,394] → 95,008 ops/sec [93,589..95,747] 🔴 -2.7%
uint8array-encoding.js — Interp: 🟢 2, 🔴 8, 8 unch. · avg -1.4% · Bytecode: 🟢 1, 🔴 13, 4 unch. · avg -2.7%
Benchmark Interpreted Δ Bytecode Δ
short (5 bytes) 745,891 ops/sec [723,107..753,116] → 786,605 ops/sec [780,210..793,951] 🟢 +5.5% 1,116,139 ops/sec [1,111,638..1,116,864] → 1,075,637 ops/sec [1,060,501..1,098,515] 🔴 -3.6%
medium (450 bytes) 287,486 ops/sec [286,350..287,960] → 289,608 ops/sec [288,040..291,546] 🟢 +0.7% 319,489 ops/sec [279,948..326,653] → 316,311 ops/sec [313,811..318,508] ~ overlap (-1.0%)
large (4096 bytes) 45,240 ops/sec [45,164..45,363] → 45,520 ops/sec [43,671..45,963] ~ overlap (+0.6%) 44,636 ops/sec [44,380..46,880] → 44,678 ops/sec [44,561..47,311] ~ overlap (+0.1%)
base64url alphabet 201,500 ops/sec [199,936..202,452] → 201,398 ops/sec [199,093..203,100] ~ overlap (-0.1%) 209,322 ops/sec [208,219..210,064] → 205,274 ops/sec [203,352..206,530] 🔴 -1.9%
omitPadding 368,995 ops/sec [367,154..377,022] → 361,909 ops/sec [357,923..363,786] 🔴 -1.9% 436,156 ops/sec [435,404..439,865] → 409,675 ops/sec [403,260..409,978] 🔴 -6.1%
short (8 chars) 335,039 ops/sec [332,906..340,412] → 338,152 ops/sec [337,879..341,536] ~ overlap (+0.9%) 370,730 ops/sec [368,990..374,285] → 352,023 ops/sec [350,724..355,652] 🔴 -5.0%
medium (600 chars) 147,936 ops/sec [144,362..152,045] → 148,946 ops/sec [147,875..150,158] ~ overlap (+0.7%) 155,683 ops/sec [154,725..156,264] → 148,800 ops/sec [147,978..149,023] 🔴 -4.4%
large (5464 chars) 28,102 ops/sec [27,932..28,277] → 26,867 ops/sec [26,730..26,927] 🔴 -4.4% 27,669 ops/sec [26,752..27,873] → 27,387 ops/sec [25,959..27,709] ~ overlap (-1.0%)
short (5 bytes) 797,975 ops/sec [786,034..812,554] → 816,972 ops/sec [811,303..826,905] ~ overlap (+2.4%) 1,151,849 ops/sec [1,140,020..1,158,591] → 1,105,080 ops/sec [1,097,421..1,109,756] 🔴 -4.1%
medium (450 bytes) 310,996 ops/sec [308,003..313,017] → 291,163 ops/sec [287,080..293,800] 🔴 -6.4% 342,019 ops/sec [336,001..347,677] → 328,058 ops/sec [326,131..329,587] 🔴 -4.1%
large (4096 bytes) 51,725 ops/sec [51,625..51,879] → 47,896 ops/sec [47,790..48,065] 🔴 -7.4% 51,227 ops/sec [49,156..51,454] → 47,972 ops/sec [46,385..48,083] 🔴 -6.4%
short (10 chars) 378,853 ops/sec [378,353..379,760] → 370,676 ops/sec [369,188..371,575] 🔴 -2.2% 406,862 ops/sec [399,947..410,364] → 382,144 ops/sec [378,573..388,976] 🔴 -6.1%
medium (900 chars) 235,778 ops/sec [231,381..238,100] → 232,291 ops/sec [229,539..232,935] ~ overlap (-1.5%) 235,103 ops/sec [231,220..235,526] → 234,722 ops/sec [233,407..236,037] ~ overlap (-0.2%)
large (8192 chars) 58,574 ops/sec [58,419..58,845] → 56,027 ops/sec [55,707..56,352] 🔴 -4.3% 54,271 ops/sec [53,943..54,476] → 56,606 ops/sec [56,174..57,003] 🟢 +4.3%
setFromBase64 (450 bytes) 137,606 ops/sec [136,130..140,735] → 137,238 ops/sec [135,627..137,511] ~ overlap (-0.3%) 147,302 ops/sec [147,135..147,421] → 144,608 ops/sec [141,620..146,182] 🔴 -1.8%
setFromHex (450 bytes) 204,368 ops/sec [202,509..206,441] → 196,854 ops/sec [195,815..198,291] 🔴 -3.7% 223,954 ops/sec [223,134..224,439] → 216,862 ops/sec [213,783..219,460] 🔴 -3.2%
toBase64 → fromBase64 (450 bytes) 104,316 ops/sec [103,521..104,698] → 103,301 ops/sec [102,965..104,815] ~ overlap (-1.0%) 106,767 ops/sec [106,567..106,958] → 105,338 ops/sec [102,214..106,292] 🔴 -1.3%
toHex → fromHex (450 bytes) 142,965 ops/sec [142,231..143,061] → 138,840 ops/sec [137,030..139,020] 🔴 -2.9% 144,916 ops/sec [143,928..145,577] → 141,531 ops/sec [140,617..143,344] 🔴 -2.3%

Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context.

frostney and others added 2 commits April 14, 2026 13:51
Add comprehensive documentation and automated doc validation: introduce new docs (built-ins-temporal, built-ins-binary-data, garbage-collector, language-tables, testing-api) and update many existing docs (built-ins, README, CONTRIBUTING, build-system, adding-built-in-types, etc.). Add markdownlint configuration (.markdownlint-cli2.jsonc) and new doc-check scripts (check-doc-symbols.ts, check-doc-duplication.ts, check-doc-length.ts, check-doc-links.ts). Integrate a new docs job into .github/workflows/pr.yml to run markdownlint and these checks on PRs. Also refine repository templates and doc formatting (issue & PR templates, markdown fence types, executive summary rules) to enforce documentation standards and improve discoverability.
- bytecode-vm.md: Correct opcode tiers to three-tier model (core 0-127,
  non-core 128-166, semantic 167-255) matching OP_SEM_FIRST in source.
  Clarify TGocciaRegister vs TGocciaValue distinction in register file.
- decision-log.md: Replace brittle relative PR links (../../pull/N) with
  absolute GitHub URLs.
- interpreter.md: Add missing TGocciaEvaluationContext fields
  (CurrentFilePath, CoverageEnabled, DisposalTracker).
- garbage-collector.md: Fix incorrect TInterfacedObject inheritance claim;
  TGocciaValue inherits from TGCManagedObject.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/value-system.md (1)

513-518: ⚠️ Potential issue | 🟠 Major

The native callback snippet no longer matches the implementation.

The codebase uses TGocciaNativeFunctionCallback(const AArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue): TGocciaValue of object, not a TGocciaValueArray-based TGocciaNativeFunctionValueCallback. Anyone copying this snippet for a built-in will end up with the wrong callback type and call surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/value-system.md` around lines 513 - 518, The docs show an outdated
native callback signature using TGocciaNativeFunctionValueCallback with
TGocciaValueArray, but the codebase uses TGocciaNativeFunctionCallback(const
AArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue): TGocciaValue
of object; update the snippet to match the real callback type and parameter
names (use TGocciaNativeFunctionCallback and TGocciaArgumentsCollection and the
const AThisValue: TGocciaValue parameter) so examples and built-ins use the
correct call surface.
🧹 Nitpick comments (1)
scripts/check-doc-symbols.ts (1)

185-206: Walk the docs tree recursively.

collectMdFiles() only sees markdown files at the top level of docs/ and docs/contributing/, so any future docs/**/foo.md page will skip symbol validation entirely. A recursive walk plus the existing skip rules would make this checker follow the documentation tree instead of today's exact layout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/check-doc-symbols.ts` around lines 185 - 206, collectMdFiles
currently only collects markdown at the immediate MD_ROOT_FILES and one-level
MD_DIRS; update it to recursively walk directories so nested docs (e.g.,
docs/**/foo.md) are included. Replace the existing loops in collectMdFiles with
a helper recursive function (e.g., walkDir or collectFromDir) that uses
readdirSync(..., { withFileTypes: true }) to traverse directories, filter for
files ending with ".md", apply the existing skip rules used elsewhere, and push
matching full paths into the files array; ensure collectMdFiles still seeds with
MD_ROOT_FILES and MD_DIRS but calls the recursive walker for directories and
returns the accumulated files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributing/code-style.md`:
- Around line 43-46: Add a short exception note to the Units section clarifying
that not all Pascal units use the Goccia.<Category>.<Name>.pas pattern:
shared/utility infrastructure units (e.g., BaseMap.pas, HashMap.pas)
intentionally live outside the Goccia.* namespace and follow a separate naming
convention tracked in issue `#104`; update the Units table text and mirror the
same exception wording in the other affected section (around the Classes/Units
guidance) so contributors know to follow the alternate convention for those
cross-cutting utilities.

In `@docs/contributing/tooling.md`:
- Around line 36-38: The documentation overstates the formatter's behavior:
update the "Function naming" bullet to match the actual implementation in
./format.pas and the FixFuncNames routine by specifying that it only uppercases
the first character of routine names (does not enforce internal PascalCase,
remove underscores, or re-camelize existing names); reword to say "capitalizes
the first character of function, procedure, constructor, and destructor names"
and note that it renames references within the same file but does not perform
further normalization or underscore removal.

In `@docs/interpreter.md`:
- Line 42: The docs incorrectly state that OnError is stored on TGocciaScope and
propagated to child scopes; update the text to say that OnError is on
TGocciaEvaluationContext (not TGocciaScope) and that TGocciaScope.CreateChild
only inherits ThisValue, so scopes do not propagate the error handler—adjust the
sentences at the referenced locations (including the repeated claim around lines
93-94) to reflect that error handling lives on TGocciaEvaluationContext and not
on TGocciaScope/CreateChild.

In `@docs/language.md`:
- Line 118: Update the documentation text in docs/language.md that claims tagged
templates set the cooked segment to `undefined` on malformed escape sequences;
the lexer currently throws for both tagged and untagged templates. Remove or
replace the clause referencing "Tagged templates support the TC39 Template
Literal Revision (ES2018): malformed escape sequences ... set the cooked segment
to `undefined`" and instead state that malformed template escapes are currently
rejected (lexer throws) for both tagged and untagged templates until the
deferred cooked=`undefined` behavior is implemented; ensure references to
`tagged templates`, `String.raw`, and examples like `\u{`/`\xG1` are updated
accordingly.
- Line 355: The example uses Exclude(Engine.Preprocessors, ppJSX) which won't
compile because Exclude requires a var set parameter and Engine.Preprocessors is
a property; update the documentation example to show assigning the modified set
back to the property (e.g., subtract ppJSX from Engine.Preprocessors and assign
the result) or, if disabling all preprocessors is acceptable, assign an empty
set to Engine.Preprocessors; reference Engine.Preprocessors, Exclude and ppJSX
in the explanation and replace the non-compiling call with the assignment-based
form.

In `@docs/value-system.md`:
- Around line 25-26: Diagram currently shows TGocciaNativeFunctionValue
inheriting from TGocciaValue; change the arrows so TGocciaNativeFunctionValue
inherits from TGocciaFunctionBase (i.e., place native built-ins under the
function branch) and ensure TGocciaError remains directly under TGocciaValue;
update the corresponding repeated section (the other instance referencing
TGocciaNativeFunctionValue) to match this corrected hierarchy so built-ins
appear as a subtype of TGocciaFunctionBase rather than a separate branch.

In `@README.md`:
- Around line 216-218: Replace the sentence that claims the bytecode VM
"executes directly on TGocciaValue" with wording that clarifies the VM uses a
register file of tagged TGocciaRegister values and only materializes
TGocciaValue at the runtime/object boundary; specifically update the README text
referencing the bytecode backend so it states the bytecode VM operates on
TGocciaRegister in its register file and converts to TGocciaValue only when
interacting with runtime/object APIs.

---

Outside diff comments:
In `@docs/value-system.md`:
- Around line 513-518: The docs show an outdated native callback signature using
TGocciaNativeFunctionValueCallback with TGocciaValueArray, but the codebase uses
TGocciaNativeFunctionCallback(const AArgs: TGocciaArgumentsCollection; const
AThisValue: TGocciaValue): TGocciaValue of object; update the snippet to match
the real callback type and parameter names (use TGocciaNativeFunctionCallback
and TGocciaArgumentsCollection and the const AThisValue: TGocciaValue parameter)
so examples and built-ins use the correct call surface.

---

Nitpick comments:
In `@scripts/check-doc-symbols.ts`:
- Around line 185-206: collectMdFiles currently only collects markdown at the
immediate MD_ROOT_FILES and one-level MD_DIRS; update it to recursively walk
directories so nested docs (e.g., docs/**/foo.md) are included. Replace the
existing loops in collectMdFiles with a helper recursive function (e.g., walkDir
or collectFromDir) that uses readdirSync(..., { withFileTypes: true }) to
traverse directories, filter for files ending with ".md", apply the existing
skip rules used elsewhere, and push matching full paths into the files array;
ensure collectMdFiles still seeds with MD_ROOT_FILES and MD_DIRS but calls the
recursive walker for directories and returns the accumulated files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 233814dd-faba-46d7-91f4-627ff928b11b

📥 Commits

Reviewing files that changed from the base of the PR and between 411eb32 and 7adeb95.

📒 Files selected for processing (30)
  • .github/ISSUE_TEMPLATE/default.md
  • .github/pull_request_template.md
  • .github/workflows/pr.yml
  • .markdownlint-cli2.jsonc
  • CONTRIBUTING.md
  • README.md
  • docs/adding-built-in-types.md
  • docs/build-system.md
  • docs/built-ins-binary-data.md
  • docs/built-ins-temporal.md
  • docs/built-ins.md
  • docs/bytecode-vm.md
  • docs/contributing/code-style.md
  • docs/contributing/tooling.md
  • docs/contributing/workflow.md
  • docs/core-patterns.md
  • docs/decision-log.md
  • docs/embedding.md
  • docs/garbage-collector.md
  • docs/interpreter.md
  • docs/language-tables.md
  • docs/language.md
  • docs/spikes/fpc-hashmap-performance.md
  • docs/testing-api.md
  • docs/testing.md
  • docs/value-system.md
  • scripts/check-doc-duplication.ts
  • scripts/check-doc-length.ts
  • scripts/check-doc-links.ts
  • scripts/check-doc-symbols.ts
✅ Files skipped from review due to trivial changes (12)
  • .github/ISSUE_TEMPLATE/default.md
  • .github/pull_request_template.md
  • docs/spikes/fpc-hashmap-performance.md
  • .markdownlint-cli2.jsonc
  • docs/language-tables.md
  • docs/adding-built-in-types.md
  • CONTRIBUTING.md
  • docs/decision-log.md
  • docs/built-ins-binary-data.md
  • docs/testing-api.md
  • docs/bytecode-vm.md
  • scripts/check-doc-duplication.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/build-system.md
  • docs/core-patterns.md
  • docs/testing.md
👮 Files not reviewed due to content moderation or server errors (8)
  • .github/workflows/pr.yml
  • docs/embedding.md
  • scripts/check-doc-length.ts
  • docs/contributing/workflow.md
  • scripts/check-doc-links.ts
  • docs/built-ins-temporal.md
  • docs/garbage-collector.md
  • docs/built-ins.md

Comment thread docs/contributing/code-style.md
Comment thread docs/contributing/tooling.md
Comment thread docs/interpreter.md
Comment thread docs/language.md
Comment thread docs/language.md Outdated
Comment thread docs/value-system.md Outdated
Comment thread README.md Outdated
frostney and others added 2 commits April 14, 2026 14:24
- code-style.md: Note legacy utility units that predate Goccia.* naming
- language.md: Fix non-compiling Exclude() example for preprocessors
- value-system.md: Correct type hierarchy diagram (TGocciaFunctionBase
  intermediate class, TGocciaNativeFunctionValue placement)
- README.md: Clarify bytecode VM uses TGocciaRegister, not TGocciaValue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- code-style.md: Correct wording — shared infrastructure units
  (BaseMap, HashMap, etc.) are intentionally outside Goccia.* namespace,
  not legacy units that haven't been renamed
- value-system.md: Update native callback signature to match actual
  TGocciaNativeFunctionCallback with TGocciaArgumentsCollection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
docs/language.md (1)

118-118: Improve inline-code readability in the template-literal bullet.

The semantics are correct, but this line is hard to scan due to tight punctuation around inline code spans. A small markdown cleanup will improve readability.

✏️ Suggested wording/formatting cleanup
-- Template literals with interpolation and tagged templates (``tag`...```,`String.raw`). Tagged templates support the TC39 Template Literal Revision (ES2018): malformed escape sequences (e.g.,`\u{`,`\xG1`) set the cooked segment to`undefined` while preserving the raw source text; untagged templates still throw `SyntaxError` for invalid escapes.
+- Template literals with interpolation and tagged templates (e.g., ``tag`...` ``, `String.raw`).
+- Tagged templates support the TC39 Template Literal Revision (ES2018): malformed escape sequences (e.g., `\u{`, `\xG1`) set the cooked segment to `undefined` while preserving the raw source text; untagged templates still throw `SyntaxError` for invalid escapes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/language.md` at line 118, The "Template literals with interpolation and
tagged templates" bullet is hard to scan due to tight punctuation around inline
code; split it into two clearer bullets and add spacing/inline-code fences
around examples like tag`...`, `String.raw`, and escape examples (`\u{`, `\xG1`)
so they render cleanly. Specifically, update the bullet that begins "Template
literals with interpolation and tagged templates" to first list the features
(e.g., tag`...`, `String.raw`) and then a separate sentence or bullet that
explains tagged-template semantics per TC39 Template Literal Revision (ES2018)
about cooked becoming `undefined` for malformed escapes while preserving raw
text and that untagged templates still throw `SyntaxError`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributing/code-style.md`:
- Around line 31-37: The compiler-directive `{H+}` is missing the `$` and should
be `{$H+}`; update the snippet so all directives use the same `{$...}` syntax
(specifically replace `{H+}` with `{$H+}`) in the block containing `{$mode
delphi}`, `{$overflowchecks on}`, `{$rangechecks on}`, and `{$modeswitch
advancedrecords}` to restore consistency and correct Pascal directive syntax.

In `@docs/value-system.md`:
- Around line 515-518: The documentation currently references the wrong callback
type name (TGocciaNativeFunctionValueCallback); update the doc to use the real
type name TGocciaNativeFunctionCallback (as defined in the native function unit)
so contributors can find the symbol when grepping; search for occurrences of
TGocciaNativeFunctionValueCallback in docs/value-system.md and replace them with
TGocciaNativeFunctionCallback and verify the surrounding description matches the
signature in TGocciaNativeFunctionCallback from the native function
implementation.
- Line 223: The docs are inconsistent about special-number representation;
update the narrative so all sections (the paragraph referencing
IsNaN/IsInfinity/IsNegativeZero and the section describing tagged special values
including FValue) agree that special values are represented via tagged
value-level encodings that may store 0.0 internally, and that correctness must
rely on the provided accessors IsNaN, IsInfinity, and IsNegativeZero rather than
raw floating-point comparisons or assuming Math.* behavior; change the sentence
claiming direct delegation to Math primitives to state that those accessors
delegate to platform-safe checks (e.g., Math.IsNaN/IsInfinite or endian-neutral
Int64 sign-bit logic) and add a clarifying note that FValue == 0.0 is a known
storage artifact handled by the accessors.

---

Nitpick comments:
In `@docs/language.md`:
- Line 118: The "Template literals with interpolation and tagged templates"
bullet is hard to scan due to tight punctuation around inline code; split it
into two clearer bullets and add spacing/inline-code fences around examples like
tag`...`, `String.raw`, and escape examples (`\u{`, `\xG1`) so they render
cleanly. Specifically, update the bullet that begins "Template literals with
interpolation and tagged templates" to first list the features (e.g., tag`...`,
`String.raw`) and then a separate sentence or bullet that explains
tagged-template semantics per TC39 Template Literal Revision (ES2018) about
cooked becoming `undefined` for malformed escapes while preserving raw text and
that untagged templates still throw `SyntaxError`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98c7b9f3-2267-443c-8be6-4263ff8c85f3

📥 Commits

Reviewing files that changed from the base of the PR and between 7adeb95 and 8ca2804.

📒 Files selected for processing (4)
  • README.md
  • docs/contributing/code-style.md
  • docs/language.md
  • docs/value-system.md
✅ Files skipped from review due to trivial changes (1)
  • README.md

Comment thread docs/contributing/code-style.md
Comment thread docs/value-system.md
Comment thread docs/value-system.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
docs/value-system.md (1)

223-223: ⚠️ Potential issue | 🟡 Minor

Number model docs remain internally contradictory (flagged in previous review).

Line 223 states accessors delegate to Math.IsNaN/Math.IsInfinite without mentioning storage artifacts, while lines 620-629 describe the FValue = 0.0 pitfall. This creates the same contributor confusion flagged in the previous review: someone reading line 223 won't realize raw Value comparisons are unsafe.

The previous review's suggested consistency rewrite still applies: update line 223 to state that accessors delegate to platform-safe checks and note that FValue == 0.0 is a storage artifact handled by the accessors.

Based on learnings: In frostney/GocciaScript tests/docs context, special values can store 0.0 internally and correctness should rely on IsNaN/value-level helpers rather than raw floating-point checks.

Also applies to: 620-629

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/value-system.md` at line 223, Update the docs so the description of the
special-value accessors clarifies they perform platform-safe/value-level checks
(not naive raw storage comparisons): change the sentence about IsNaN, IsInfinity
and IsNegativeZero to say they delegate to platform-safe checks (e.g.,
Math.IsNaN/Math.IsInfinite or an endian-neutral sign-bit check) and explicitly
note that FValue may store 0.0 as a storage artifact so callers must use the
accessors rather than testing Value/FValue == 0.0; apply the same clarification
language to the section around the FValue = 0.0 pitfall (lines ~620-629) and
reference the IsNaN/IsInfinity/IsNegativeZero helpers where appropriate.
docs/contributing/code-style.md (1)

31-37: ⚠️ Potential issue | 🟡 Minor

Fix the compiler-directive syntax inconsistency on line 33.

Line 33 shows {H+} without the $ prefix, while all other directives in this block use the {$...} format. For consistency in this authoritative style guide, change it to {$H+}.

📝 Proposed fix
 {$mode delphi}           // Delphi-compatible mode
-{H+}                     // Long strings by default
+{$H+}                    // Long strings by default
 {$overflowchecks on}     // Runtime overflow checking
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/contributing/code-style.md` around lines 31 - 37, Replace the
inconsistent Pascal compiler directive "{H+}" with the correct "$-prefixed" form
"{$H+}" in the code block so all directives use the "{$...}" syntax; update the
directive inside the fenced Pascal block (the line currently showing {H+}) to
"{$H+}" to match other lines like "{$mode delphi}" and "{$overflowchecks on}".
🧹 Nitpick comments (1)
docs/contributing/code-style.md (1)

58-58: Consider simplifying "very small scopes" to "small scopes".

The intensifier "very" can be omitted without losing clarity: "Short single-letter names in small scopes (e.g. loop I, J) remain acceptable."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/contributing/code-style.md` at line 58, Update the phrasing in the
guideline sentence that reads "Short single-letter names in very small scopes
(e.g. loop `I`, `J`) remain acceptable." to remove "very" so it reads "Short
single-letter names in small scopes (e.g. loop `I`, `J`) remain acceptable.";
ensure the surrounding sentence about avoiding snake_case/mixed_Case and using
descriptive PascalCase names (e.g., `PrimaryScope`, `FallbackScope`) is
unchanged and that the examples `I`, `J` remain intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/contributing/code-style.md`:
- Around line 31-37: Replace the inconsistent Pascal compiler directive "{H+}"
with the correct "$-prefixed" form "{$H+}" in the code block so all directives
use the "{$...}" syntax; update the directive inside the fenced Pascal block
(the line currently showing {H+}) to "{$H+}" to match other lines like "{$mode
delphi}" and "{$overflowchecks on}".

In `@docs/value-system.md`:
- Line 223: Update the docs so the description of the special-value accessors
clarifies they perform platform-safe/value-level checks (not naive raw storage
comparisons): change the sentence about IsNaN, IsInfinity and IsNegativeZero to
say they delegate to platform-safe checks (e.g., Math.IsNaN/Math.IsInfinite or
an endian-neutral sign-bit check) and explicitly note that FValue may store 0.0
as a storage artifact so callers must use the accessors rather than testing
Value/FValue == 0.0; apply the same clarification language to the section around
the FValue = 0.0 pitfall (lines ~620-629) and reference the
IsNaN/IsInfinity/IsNegativeZero helpers where appropriate.

---

Nitpick comments:
In `@docs/contributing/code-style.md`:
- Line 58: Update the phrasing in the guideline sentence that reads "Short
single-letter names in very small scopes (e.g. loop `I`, `J`) remain
acceptable." to remove "very" so it reads "Short single-letter names in small
scopes (e.g. loop `I`, `J`) remain acceptable."; ensure the surrounding sentence
about avoiding snake_case/mixed_Case and using descriptive PascalCase names
(e.g., `PrimaryScope`, `FallbackScope`) is unchanged and that the examples `I`,
`J` remain intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23a3bd05-51b4-431b-aa99-92c18e2985f8

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca2804 and 5659536.

📒 Files selected for processing (2)
  • docs/contributing/code-style.md
  • docs/value-system.md

Remove outdated "dedicated enum" wording from design rationale — the
actual implementation uses a single Double with IEEE 754 property
accessors (IsNaN, IsInfinity, IsNegativeZero). Align the rationale
section with the Numbers section above.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/value-system.md`:
- Line 661: Update the phrasing in the descriptor-merging sentence that
references the ECMAScript wording: locate the sentence mentioning "ECMAScript
spec behavior" near the `Object.defineProperty` example and replace it with a
clearer phrase such as "ECMAScript specification behavior" (or simply
"ECMAScript behavior") so the line reads smoothly while preserving the example
about `Object.defineProperty(obj, "x", { enumerable: false })` and the
explanation that unspecified attributes are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1ef1221-a515-43b8-b228-94097581b46e

📥 Commits

Reviewing files that changed from the base of the PR and between 5659536 and fa83c8f.

📒 Files selected for processing (1)
  • docs/value-system.md

Comment thread docs/value-system.md Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/value-system.md (1)

96-110: ⚠️ Potential issue | 🟡 Minor

Remove TGocciaError from the type hierarchy diagram.

TGocciaError is still rendered inside the TGocciaValue hierarchy diagram, but it is not a TGocciaValue subtype (it extends Pascal Exception). Please remove this class block from the hierarchy diagram to avoid reintroducing the same confusion.

Based on learnings: In GocciaScript (docs/value-system.md), TGocciaError extends Pascal's Exception, not TGocciaValue, and should not appear in the TGocciaValue type hierarchy diagram.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/value-system.md` around lines 96 - 110, Remove the TGocciaError class
block from the TGocciaValue hierarchy diagram in docs/value-system.md because
TGocciaError is not a TGocciaValue subtype (it extends Pascal Exception); locate
the diagram entries (the class blocks such as TGocciaNativeFunctionValue,
TGocciaArrayBufferValue, TGocciaSharedArrayBufferValue, TGocciaTypedArrayValue
and TGocciaError) and delete only the TGocciaError block so the diagram reflects
that TGocciaValue's subtypes remain while TGocciaError is omitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@docs/value-system.md`:
- Around line 96-110: Remove the TGocciaError class block from the TGocciaValue
hierarchy diagram in docs/value-system.md because TGocciaError is not a
TGocciaValue subtype (it extends Pascal Exception); locate the diagram entries
(the class blocks such as TGocciaNativeFunctionValue, TGocciaArrayBufferValue,
TGocciaSharedArrayBufferValue, TGocciaTypedArrayValue and TGocciaError) and
delete only the TGocciaError block so the diagram reflects that TGocciaValue's
subtypes remain while TGocciaError is omitted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7c2cc9c-de8f-48fe-9bc2-46ece8af5066

📥 Commits

Reviewing files that changed from the base of the PR and between 5659536 and 313f96d.

📒 Files selected for processing (1)
  • docs/value-system.md

TGocciaError extends Exception, not TGocciaValue — it doesn't belong
in the TGocciaValue subtype diagram.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/value-system.md (1)

680-685: ⚠️ Potential issue | 🟡 Minor

Error model wording appears internally inconsistent.

Line 680+ describes runtime errors as TGocciaError instances, but Line 393 says throw helpers build TGocciaObjectValue and raise TGocciaThrowValue. Please clarify this section so contributors don’t infer the wrong runtime error type path.

Based on learnings: In GocciaScript (docs/value-system.md), TGocciaError extends Pascal’s Exception, not TGocciaValue.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/value-system.md` around lines 680 - 685, The documentation is internally
inconsistent about runtime error types: update the text to clearly state that
TGocciaError is a Pascal Exception subclass (extends Exception) and not a
TGocciaValue, and then explicitly distinguish that the throw helpers (referenced
as TGocciaObjectValue and TGocciaThrowValue) construct a TGocciaObjectValue
payload and raise it via TGocciaThrowValue at runtime; revise the paragraphs
around TGocciaError, TGocciaObjectValue, TGocciaThrowValue, and TGocciaValue to
reflect this separation (TGocciaError = Exception subclass; throw helpers =
build TGocciaObjectValue and raise TGocciaThrowValue) so readers cannot conflate
TGocciaError with TGocciaValue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/value-system.md`:
- Around line 625-626: The documentation's example for IsActualZero conflicts
with the warning about negative zero; update the doc text around the
IsActualZero example to state that the shown predicate `(Value = 0) and not
IsNaN and not IsInfinite` (as implemented in Goccia.Evaluator.Arithmetic.pas
IsActualZero) is intentionally used for exponentiation where `-0` is treated as
zero, and add a note that in other contexts you should include an explicit
IsNegativeZero guard or use the NumericRank behavior in
Goccia.Values.ArrayValue.pas to distinguish `-0`; alternatively, replace the
example with the guarded form if you prefer the universal rule—be sure to
reference IsActualZero and NumericRank in the updated wording.

---

Outside diff comments:
In `@docs/value-system.md`:
- Around line 680-685: The documentation is internally inconsistent about
runtime error types: update the text to clearly state that TGocciaError is a
Pascal Exception subclass (extends Exception) and not a TGocciaValue, and then
explicitly distinguish that the throw helpers (referenced as TGocciaObjectValue
and TGocciaThrowValue) construct a TGocciaObjectValue payload and raise it via
TGocciaThrowValue at runtime; revise the paragraphs around TGocciaError,
TGocciaObjectValue, TGocciaThrowValue, and TGocciaValue to reflect this
separation (TGocciaError = Exception subclass; throw helpers = build
TGocciaObjectValue and raise TGocciaThrowValue) so readers cannot conflate
TGocciaError with TGocciaValue.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ca14cbd-7c7d-4b5e-bb1d-13ab24ec67df

📥 Commits

Reviewing files that changed from the base of the PR and between 313f96d and 9c836e4.

📒 Files selected for processing (1)
  • docs/value-system.md

Comment thread docs/value-system.md Outdated
frostney and others added 2 commits April 14, 2026 15:06
IsActualZero intentionally treats -0 as zero (correct for exponentiation).
Note that contexts needing -0 distinction should use IsNegativeZero.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive docs: docs/errors.md (error types, display, JSON envelope, Error.cause, embedding notes) and docs/goals.md (project goals, sandboxing and embedding rationale). Update README table to link the new pages and reference errors.md. Propagate errors.md cross-references into embedding.md, interpreter.md, language.md, language-tables.md, and value-system.md. Consolidate and expand testing guidance: remove duplicate testing guidance from testing-api.md and enrich testing.md with testing layers, CLI behavior/CI checks, cross-platform newline rules, and test metadata examples. Minor wording/formatting tweaks across docs to reflect the new documentation structure.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/value-system.md (1)

682-687: ⚠️ Potential issue | 🟠 Major

Align the Error Values section with the object-based JS error model.

This section currently says constructors create TGocciaError instances, but earlier in this same doc (Line 393) you document runtime throws via TGocciaObjectValue + TGocciaThrowValue. That mismatch can mislead contributors implementing error paths.

🛠️ Proposed doc fix
-`TGocciaError` carries JavaScript error information:
-
-- `Name` — Error type (`Error`, `TypeError`, `ReferenceError`, `RangeError`)
-- `Message` — Error description
-
-Error constructors are registered as globals, creating `TGocciaError` instances that can be `throw`n and `catch`ed.
+JavaScript errors are represented as `TGocciaObjectValue` instances (with `name` and `message`) and propagated via `TGocciaThrowValue`.
+
+- `name` — Error type (`Error`, `TypeError`, `ReferenceError`, `RangeError`)
+- `message` — Error description
+
+Error constructors are registered as globals and produce these JS error objects, which are then `throw`n/`catch`ed through the runtime throw-value flow.

Based on learnings: In frostney/GocciaScript (docs/value-system.md), TGocciaError extends Pascal's Exception, not TGocciaValue.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/value-system.md` around lines 682 - 687, The doc currently claims error
constructors create TGocciaError instances, but this conflicts with earlier
documentation stating runtime throws use TGocciaObjectValue + TGocciaThrowValue
and that TGocciaError extends Pascal's Exception rather than TGocciaValue;
update the "Error Values" section to align with the object-based JS error model
by describing that JS errors are represented as TGocciaObjectValue objects (with
fields Name and Message) and that throwing is implemented via TGocciaThrowValue
carrying that object, and note that TGocciaError is a Pascal Exception subtype
(not a TGocciaValue) to avoid confusion between TGocciaError,
TGocciaObjectValue, TGocciaThrowValue, TGocciaValue and Exception.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/errors.md`:
- Around line 15-16: Update the statement that "All error types inherit from
`Error` and work with `instanceof`" to scope it to JavaScript-visible
constructors only and add an explicit note that the CLI-only `TimeoutError` is
not exposed to JS; mention `Error`, `instanceof`, and `TimeoutError` so
reviewers can find and adjust the sentence and add the clarification about the
CLI-only nature of `TimeoutError`.
- Around line 146-147: Update the documentation wording to avoid saying "all
error constructors accept an optional second argument" — clarify that most
built‑in errors (Error, TypeError, RangeError, ReferenceError, SyntaxError,
URIError) accept an options object with a cause as their second parameter, but
AggregateError uses the signature new AggregateError(errors, message, options)
(options is third); also remove SuppressedError from the list that supports
cause and note that SuppressedError's constructor is new SuppressedError(error,
suppressed, message) and does not accept an options/cause argument.

In `@docs/testing.md`:
- Around line 231-239: Update the example TestRunner commands so they run the
full suite with ASI enabled: change the interpreted invocation
"./build/TestRunner tests" to "./build/TestRunner tests --asi" and the bytecode
example "./build/TestRunner tests --mode=bytecode" to "./build/TestRunner tests
--mode=bytecode --asi" (or explicitly note the commands are for non-ASI subsets)
to ensure tests under tests/language/asi/* are included; adjust the surrounding
text to state both modes must pass with --asi present.

---

Outside diff comments:
In `@docs/value-system.md`:
- Around line 682-687: The doc currently claims error constructors create
TGocciaError instances, but this conflicts with earlier documentation stating
runtime throws use TGocciaObjectValue + TGocciaThrowValue and that TGocciaError
extends Pascal's Exception rather than TGocciaValue; update the "Error Values"
section to align with the object-based JS error model by describing that JS
errors are represented as TGocciaObjectValue objects (with fields Name and
Message) and that throwing is implemented via TGocciaThrowValue carrying that
object, and note that TGocciaError is a Pascal Exception subtype (not a
TGocciaValue) to avoid confusion between TGocciaError, TGocciaObjectValue,
TGocciaThrowValue, TGocciaValue and Exception.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 46125e62-a4da-43ae-b337-7b960c78e94c

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9c998 and 32e0471.

📒 Files selected for processing (10)
  • README.md
  • docs/embedding.md
  • docs/errors.md
  • docs/goals.md
  • docs/interpreter.md
  • docs/language-tables.md
  • docs/language.md
  • docs/testing-api.md
  • docs/testing.md
  • docs/value-system.md
✅ Files skipped from review due to trivial changes (4)
  • docs/language-tables.md
  • docs/embedding.md
  • docs/goals.md
  • docs/testing-api.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

Comment thread docs/errors.md Outdated
Comment thread docs/errors.md Outdated
Comment thread docs/testing.md Outdated
frostney and others added 2 commits April 14, 2026 16:10
…inements

- errors.md: Clarify TimeoutError is CLI-only, not a JS constructor.
  Fix Error.cause argument position documentation — second for most
  error types, third for AggregateError, fourth for SuppressedError.
- testing.md: Add --asi flag to Run All Tests examples (CI uses it).
  Fix Pascal unit tests labeled as tertiary consistently.
- goals.md: Inline roadmap items, remove honest assessment heading,
  remove high-stakes security wording.
- language-tables.md: Trim runtime extensions to spec/proposal reference.
- value-system.md: Fix IsActualZero context, ECMAScript spec wording,
  remove TGocciaError from hierarchy diagram.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Describe JavaScript errors as `TGocciaObjectValue` instances
- Explain `TGocciaThrowValue` vs internal `TGocciaError` exceptions
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/testing.md (1)

497-500: Keep the “when to write tests” guidance consistent with the 3-layer model

Line 497–500 currently presents a JS-vs-Pascal split, which omits the documented CLI behavior layer and can misdirect contributors for CLI-facing behavior.

♻️ Suggested update
-- **JavaScript tests** (preferred) — For any behavior observable from script code. This is almost everything.
-- **Pascal unit tests** — Only for internal implementation details not reachable from script code (e.g., the test framework's own pass/fail recording logic).
+- **JavaScript tests** (primary, preferred) — For behavior observable from script code. This is most feature work.
+- **CLI behavior tests** (secondary) — For command-surface behavior (`ScriptLoader`, `TestRunner`, `BenchmarkRunner`) such as flags, output envelopes/formats, and error presentation.
+- **Pascal unit tests** (tertiary) — For internal implementation details not reachable through documented user-facing script/CLI entry points (e.g., the test framework's own pass/fail recording logic).

Based on learnings, this PR’s testing docs intentionally use a three-layer priority (JavaScript → CLI → Pascal).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/testing.md` around lines 497 - 500, Update the "When to write Pascal
unit tests vs JavaScript tests" guidance to follow the three-layer priority
model (JavaScript → CLI → Pascal) instead of a binary JS vs Pascal split: state
that JavaScript tests are preferred for behaviors observable from script code,
that CLI-level tests should cover CLI-facing behavior and integrations exposed
via the command-line, and that Pascal unit tests are reserved only for internal
implementation details not reachable from script or CLI (e.g., test framework
internals); adjust the bullet list to explicitly include the CLI layer and
reorder/examples accordingly so contributors know to prefer JS tests, then CLI
tests, then Pascal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/testing.md`:
- Around line 409-410: Update the sentence "Pascal unit tests are the tertiary
testing layer for low-level value system internals." to a broader description
that explicitly includes other internal concerns (e.g., function/scope behavior,
test framework internals, and memory/type conversion edge cases), so the
paragraph reflects the wider coverage shown in the table and avoids
underspecifying the role of Pascal tests.

---

Nitpick comments:
In `@docs/testing.md`:
- Around line 497-500: Update the "When to write Pascal unit tests vs JavaScript
tests" guidance to follow the three-layer priority model (JavaScript → CLI →
Pascal) instead of a binary JS vs Pascal split: state that JavaScript tests are
preferred for behaviors observable from script code, that CLI-level tests should
cover CLI-facing behavior and integrations exposed via the command-line, and
that Pascal unit tests are reserved only for internal implementation details not
reachable from script or CLI (e.g., test framework internals); adjust the bullet
list to explicitly include the CLI layer and reorder/examples accordingly so
contributors know to prefer JS tests, then CLI tests, then Pascal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb97e606-67ed-4cb4-bebd-c42e9255cb80

📥 Commits

Reviewing files that changed from the base of the PR and between 32e0471 and 8ecdaac.

📒 Files selected for processing (3)
  • docs/errors.md
  • docs/testing.md
  • docs/value-system.md

Comment thread docs/testing.md Outdated
- testing.md: Broaden Pascal test description from "value system
  internals" to include function/scope internals and test-framework
  state behavior
- value-system.md: Fix Error Values section to accurately describe the
  three-class model (TGocciaObjectValue for JS errors,
  TGocciaThrowValue for throw propagation, TGocciaError for Pascal
  parser exceptions)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
docs/testing.md (1)

391-406: Consider marking the CLI checklist table as non-exhaustive to reduce doc drift.

This table is detailed and helpful, but it can become stale as pr.yml evolves. Adding a short “non-exhaustive; workflow is source of truth” note would keep maintenance lighter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/testing.md` around lines 391 - 406, Update the CLI checklist table in
docs/testing.md to include a short one-line note that the table is
non-exhaustive and that the pr.yml workflow is the source of truth; edit the
block containing the table (the lines listing "TestRunner JSON output",
"ScriptLoader JSON output", etc.) to append a sentence such as "This checklist
is non-exhaustive — see the pr.yml workflow for the authoritative test steps" so
readers know to consult pr.yml for the canonical behavior and to reduce doc
drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/testing.md`:
- Around line 391-406: Update the CLI checklist table in docs/testing.md to
include a short one-line note that the table is non-exhaustive and that the
pr.yml workflow is the source of truth; edit the block containing the table (the
lines listing "TestRunner JSON output", "ScriptLoader JSON output", etc.) to
append a sentence such as "This checklist is non-exhaustive — see the pr.yml
workflow for the authoritative test steps" so readers know to consult pr.yml for
the canonical behavior and to reduce doc drift.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2d7d1cab-ad99-4d86-b28a-23eb95bb06b0

📥 Commits

Reviewing files that changed from the base of the PR and between 8ecdaac and 546f0d0.

📒 Files selected for processing (1)
  • docs/testing.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/testing.md`:
- Line 361: The sentence currently reads "lexer → parser → evaluator" and is
interpreter-specific; update the wording to be mode-agnostic by replacing that
phrase with something inclusive of bytecode/VM execution (for example "lexer →
parser → interpreter/VM execution" or "lexer → parser → evaluator/VM") so the
primary testing pipeline covers both interpreter and bytecode modes; edit the
occurrence of the phrase "lexer → parser → evaluator" in docs/testing.md
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5816fc54-a5de-429c-b275-3590573822e6

📥 Commits

Reviewing files that changed from the base of the PR and between 546f0d0 and 5085d80.

📒 Files selected for processing (1)
  • docs/testing.md

Comment thread docs/testing.md Outdated
Changed "lexer → parser → evaluator" to "lexer → parser →
interpreter/VM execution" since tests run in both modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/testing.md (1)

475-487: ⚠️ Potential issue | 🟠 Major

Outdated NaN implementation description.

Lines 486-487 describe the OLD enum-backed design (FSpecialValue = nsvNaN flag with FValue set to 0) that was already removed. Based on learnings, TGocciaNumberLiteralValue now stores a single Double payload (FValue) where special values are represented as standard IEEE 754 bit patterns, not via a separate enum field.

The guidance to use Value.ToNumberLiteral.IsNaN instead of Math.IsNaN is still correct, but the explanation is wrong.

📝 Corrected explanation
 ### NaN Checks in Pascal Tests
 
 When testing for NaN, use the `IsNaN` property on `TGocciaNumberLiteralValue` rather than `Math.IsNaN()`:
 
 ```pascal
 // Correct — uses the internal special value flag
 Expect<Boolean>(Value.ToNumberLiteral.IsNaN).ToBe(True);
 
-// WRONG — NaN values store 0.0 internally, so Math.IsNaN returns False
-IsNaN(Value.ToNumberLiteral.Value)  // always False!
+// WRONG — Direct double access bypasses the value-level accessor
+Math.IsNaN(Value.ToNumberLiteral.Value)  // may not work as expected

-This is because GocciaScript represents NaN via a FSpecialValue = nsvNaN flag with FValue set to 0 (avoiding floating-point NaN propagation issues). The IsNaN property checks the flag; Math.IsNaN checks the stored double.
+TGocciaNumberLiteralValue stores special values (NaN, Infinity, -Infinity, -0) as standard IEEE 754 bit patterns in FValue. The IsNaN property accessor correctly detects the NaN bit pattern; always use the value-level accessors for correctness.

</details>

Based on learnings: "TGocciaNumberLiteralValue stores a single Double payload (FValue); special values (NaN, Infinity, -Infinity, -0) are represented as standard IEEE 754 bit patterns and are detected via property accessors (IsNaN, IsInfinity, IsNegativeZero) — not via a separate enum field or 0.0 storage artifact. The old enum-backed design (where special values stored FValue = 0.0) was removed; always use the value-level accessors for correctness."

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/testing.md around lines 475 - 487, The docs incorrectly describe an old
enum-backed NaN representation; update the paragraph around
TGocciaNumberLiteralValue so it no longer claims FSpecialValue/nsvNaN or that
FValue is set to 0.0. Replace with a concise statement that
TGocciaNumberLiteralValue stores a single Double in FValue using standard
IEEE‑754 bit patterns for NaN/Infinity/-0 and that the
IsNaN/IsInfinity/IsNegativeZero property accessors must be used (rather than
calling Math.IsNaN on FValue) to detect special values correctly.


</details>

</blockquote></details>

</blockquote></details>
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@docs/testing.md`:
- Around line 475-487: The docs incorrectly describe an old enum-backed NaN
representation; update the paragraph around TGocciaNumberLiteralValue so it no
longer claims FSpecialValue/nsvNaN or that FValue is set to 0.0. Replace with a
concise statement that TGocciaNumberLiteralValue stores a single Double in
FValue using standard IEEE‑754 bit patterns for NaN/Infinity/-0 and that the
IsNaN/IsInfinity/IsNegativeZero property accessors must be used (rather than
calling Math.IsNaN on FValue) to detect special values correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6e2b73e-0dd9-4765-ae6d-1e40c29ea2fb

📥 Commits

Reviewing files that changed from the base of the PR and between 5085d80 and af5a769.

📒 Files selected for processing (1)
  • docs/testing.md

Clarify project docs and workflow: mark decision-log entries as immutable and require adding new entries for architectural/design decisions; update CONTRIBUTING and the workflow guide to reference the decision log. Standardize number representation documentation—numbers now use a single IEEE‑754 Double and callers should use IsNaN/IsInfinity/IsNegativeZero property accessors (replace previous enum-based guidance). Update tests and adding-built-in-types docs to prefer property accessors, and add/reorganize recent entries in docs/decision-log.md to reflect the current history.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/decision-log.md (1)

16-16: Tighten redundant phrasing in the immutability note.

“at that point in time” is wordy; “at that point” reads cleaner without changing meaning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/decision-log.md` at line 16, Replace the phrase "at that point in time"
with the tighter wording "at that point" in the immutable entry sentence inside
decision-log.md (the sentence starting "Each entry records what was
decided..."). Keep the rest of the sentence unchanged and preserve the note
about adding a new entry and updating links.
CONTRIBUTING.md (1)

31-31: Minor wording cleanup for readability.

“at that point in time” can be shortened to “at that point” with no loss of meaning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTRIBUTING.md` at line 31, Update the phrasing in the CONTRIBUTING.md
bullet titled "Decision log entries are immutable" by replacing "at that point
in time" with the shorter "at that point" while keeping the rest of the sentence
intact and preserving the reference to docs/decision-log.md; ensure the header
text "Decision log entries are immutable" and the instructions about adding a
new entry and updating links remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributing/workflow.md`:
- Line 55: Update the sentence in the "Add or update tests" bullet so Pascal
test guidance explicitly includes scope-chain changes: when referring to Pascal
units under `units/*.Test.pas`, append "scope chain" to the list (e.g., "when
you touch AST, evaluator, value types, or scope chain") so native Pascal tests
are triggered for scope-chain behavior changes.

---

Nitpick comments:
In `@CONTRIBUTING.md`:
- Line 31: Update the phrasing in the CONTRIBUTING.md bullet titled "Decision
log entries are immutable" by replacing "at that point in time" with the shorter
"at that point" while keeping the rest of the sentence intact and preserving the
reference to docs/decision-log.md; ensure the header text "Decision log entries
are immutable" and the instructions about adding a new entry and updating links
remain unchanged.

In `@docs/decision-log.md`:
- Line 16: Replace the phrase "at that point in time" with the tighter wording
"at that point" in the immutable entry sentence inside decision-log.md (the
sentence starting "Each entry records what was decided..."). Keep the rest of
the sentence unchanged and preserve the note about adding a new entry and
updating links.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 40e70a80-aacd-442b-9a54-1ddfcc2cfa65

📥 Commits

Reviewing files that changed from the base of the PR and between af5a769 and 87afe2b.

📒 Files selected for processing (6)
  • CONTRIBUTING.md
  • docs/adding-built-in-types.md
  • docs/contributing/workflow.md
  • docs/decision-log.md
  • docs/testing.md
  • docs/value-system.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/adding-built-in-types.md

Comment thread docs/contributing/workflow.md
- Replace `MD_FALLOW_*` env vars with `DOC_DUP_*`
- Ignore URLs during tokenization and fuzzy paragraph checks
- Update scanner banner to `doc-duplication`
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/check-doc-duplication.ts`:
- Around line 547-560: The linkText for suggestions is built using
relative(root, canon.file) which produces wrong links when pasted into duplicate
files in nested dirs; update the construction to compute a per-duplicate
relative path from the duplicate's directory to the canonical file (e.g. use
path.relative(dirname(duplicate.file), canon.file) for each entry in
suggestions. Locate the suggestions push where variables sorted, canon,
duplicates, and linkText are used and replace the single relPath based on root
with a per-duplicate relPath (or store a per-duplicate replacement string) so
each duplicates[] entry contains the correct relative link; apply the same
change to the similar block around the 563-577 region.
- Around line 121-123: The duplication scanner is treating vendored assistant
docs as repo-owned because ".agents" isn't ignored; update the IGNORE_DIRS Set
(symbol IGNORE_DIRS in scripts/check-doc-duplication.ts) to include ".agents"
(or the vendored subtree name) so the walker skips vendored docs like
.agents/skills/resolve-reviews/SKILL.md; leave EXTENSIONS unchanged.
- Around line 704-717: The corpus currently concatenates tokens from every file
into one continuous stream which allows suffix-array matches to span file
boundaries; after the call to tokenise(...) that pushes a file's tokens into
corpus (within the loop that also uses buildHeadingIndex, tokenise,
extractParagraphs), append a synthetic boundary token object (e.g., a unique
word like "__doc_boundary_<unique>" with a loc pointing to the file and a line
just past the last line) to corpus so the suffix-array cannot match across
files; ensure you create the boundary after tokenise(...) and before calling
extractParagraphs(...) (use the existing file and lines variables to build a
unique marker and location).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bfc7b0ee-950e-494b-bc34-a3ecf287232e

📥 Commits

Reviewing files that changed from the base of the PR and between 87afe2b and a657924.

📒 Files selected for processing (1)
  • scripts/check-doc-duplication.ts

Comment thread scripts/check-doc-duplication.ts
Comment thread scripts/check-doc-duplication.ts
Comment thread scripts/check-doc-duplication.ts
…tches, optimize sort

- Add .agents to IGNORE_DIRS so vendored skill docs are excluded
- Insert unique sentinel words between files to prevent suffix-array
  matches from spanning file boundaries
- Cap suffix comparison depth at 64 words (12× speedup: 2:24 → 12s)
- Rename MD_FALLOW env vars to DOC_DUP for clarity
- Strip URLs before tokenisation to reduce false similarity
- Fix architecture.md overview duplication with bytecode-vm.md
- Add decision log workflow step to contributing guide
- Add 5 missing decision log entries (VM unification, ASI, profiling,
  Goccia.build, source maps)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@frostney frostney merged commit 5ddc214 into main Apr 14, 2026
10 checks passed
@frostney frostney deleted the documentation-restructure branch April 14, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant