Lane 11 PR 4: the sheet evaluator in the browser, held to the Python engine by the shared fixtures (#737) #965

Merged
claude-bot merged 3 commits from build/lane11-4-js-evaluator into main 2026-09-30 08:57:10 +00:00
Collaborator

Lane 11 (#737) PR 4 (IS-405 in the browser). It's a JavaScript port of PR 2's engine, used for live recalculation while someone edits a sheet (PR 10). The server's values always win on save.

webapp/frontend/src/sheets/:

  • expr.js: tokenizer, parser (tokenize first), evaluator and helpers.
  • evaluate.js: compile the definition (repeat expansion, dependency order, cycle issues) and run it, returning the same shape as Python: {values, derivations, absent, errors}. It never throws.
  • dice-display.js: reads a kept roll for the Dice component. There's no rolling in the browser (D85).
  • index.js: the exports.
  • Plain ES modules, with no new dependencies.

Held to Python:

  • Fixtures: conformance.test.js runs every shared fixture in webapp/backend/app/sheets/conformance/, read by path from the checkout: 309/309 expression cases, 13/13 sheet cases and 2/2 generic fixtures, with nothing weakened or skipped. A harness check replaces floored % with JS % and watches 5 cases fail.
  • Where JavaScript needed care:
    • floored % and rounding half away from zero, by formula;
    • offsets and length limits counted in code points;
    • strings compared by code point;
    • integers past 2^53 printed with BigInt digits (found by fuzzing);
    • own-property reads only, so @constructor is null;
    • -0 normalised away.
  • Differential fuzz (one-off, not committed): 16,000 random expressions, including Unicode and astral characters, and 2,400 perturbed sheets, comparing whole outputs with Python. 0 mismatches.
  • Speed: a 622-field sheet with 30 modifiers compiles once in about 66 ms, then evaluates in about 7 ms.

Tests:

  • src/sheets passes (408), and so does the full vitest suite (3174).
  • eslint shows 0 errors.
  • The fixture path resolves identically in CI's checkout and in a local image.

Next: PR 2b (label expressions, first/last/join, and two Python robustness fixes found while porting) is stacked on this one and follows it.

Changelog: changelog.d/737-lane11-js-evaluator.added.md.

🤖 Generated with Claude Code

Lane 11 (#737) PR 4 (IS-405 in the browser). It's a JavaScript port of PR 2's engine, used for live recalculation while someone edits a sheet (PR 10). The server's values always win on save. **`webapp/frontend/src/sheets/`:** - `expr.js`: tokenizer, parser (tokenize first), evaluator and helpers. - `evaluate.js`: compile the definition (repeat expansion, dependency order, cycle issues) and run it, returning the **same shape as Python**: `{values, derivations, absent, errors}`. It never throws. - `dice-display.js`: reads a kept roll for the `Dice` component. There's no rolling in the browser (D85). - `index.js`: the exports. - Plain ES modules, with no new dependencies. **Held to Python:** - **Fixtures:** `conformance.test.js` runs **every** shared fixture in `webapp/backend/app/sheets/conformance/`, read by path from the checkout: 309/309 expression cases, 13/13 sheet cases and 2/2 generic fixtures, with nothing weakened or skipped. A harness check replaces floored `%` with JS `%` and watches 5 cases fail. - **Where JavaScript needed care:** - floored `%` and rounding half away from zero, by formula; - offsets and length limits counted in code points; - strings compared by code point; - integers past 2^53 printed with BigInt digits (found by fuzzing); - own-property reads only, so `@constructor` is null; - `-0` normalised away. - **Differential fuzz** (one-off, not committed): 16,000 random expressions, including Unicode and astral characters, and 2,400 perturbed sheets, comparing whole outputs with Python. **0 mismatches.** - **Speed:** a 622-field sheet with 30 modifiers compiles once in about 66 ms, then evaluates in about 7 ms. **Tests:** - `src/sheets` passes (408), and so does the full vitest suite (3174). - eslint shows 0 errors. - The fixture path resolves identically in CI's checkout and in a local image. **Next:** PR 2b (label expressions, `first`/`last`/`join`, and two Python robustness fixes found while porting) is stacked on this one and follows it. Changelog: `changelog.d/737-lane11-js-evaluator.added.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
src/sheets/{expr,evaluate,dice-display,index}.js: a port of app/sheets'
expression language and evaluator that passes every shared conformance
fixture (conformance/expr, conformance/sheet, builtin/*/fixtures), read
from the backend by the repository layout. Dice are display-only (D85).
Unit tests for limits, code-point offsets and ordering, totality, the
compile cache, and dice records.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into build/lane11-4-js-evaluator
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 28s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m1s
CI / Does this change touch the recording path? (pull_request) Successful in 34s
CI / Feedback site tests (pull_request) Successful in 53s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Backend lint (ruff) (pull_request) Successful in 1m16s
CI / Docker image build (pull_request) Successful in 1m8s
CI / Bot tests and audit (pull_request) Successful in 4m37s
CI / Frontend (shard 2/2) (pull_request) Successful in 6m9s
CI / Frontend (shard 1/2) (pull_request) Successful in 7m59s
CI / Backend migration, tests, and audit (pull_request) Successful in 16m29s
CI / End-to-end journeys (phone) (pull_request) Successful in 16m45s
CI / End-to-end journeys (desktop) (pull_request) Successful in 18m29s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 18m29s
5cddc43cf7
Sign in to join this conversation.
No description provided.