Skip to content

Engine lane into main - #51

Merged
fenerax merged 21 commits into
mainfrom
en/lane-to-main
Nov 1, 2026
Merged

fenerax merged 21 commits into
mainfrom
en/lane-to-main

Conversation

@fenerax

@fenerax fenerax commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Engine lane into main

This brings the engine rebuild lane into main. Nothing goes live until a release.

What the lane adds

  • Calculator checker, init bundle, profile and sharded private gate (EN1–EN4): tooling only.
  • Capacity measurements (EN6).
  • EN7: snapshot and eligibility caches shared across pickers in a worker, under one 384 MiB budget. This is the lane's only app change.

EN7 results

  • Private corpus, all 48 shards: 414 states, 12,333 comparisons, 0 differences, 0 errors.
  • Public calc-check: 4,067 states, 0 differences.
  • Two-picker warm round 17.0 s to 8.2 s; eight-picker p95 5.0 s to 2.4 s; eight-picker peak RSS 2.40 to 1.98 GiB.
  • One picker, interleaved against the lane: +6.0%, +2.5% and −1.6% on three private states (was +25% to +36% before the size-reuse fix).
  • Reviews: one Codex P2 (cache charging) and two Cloud-review P2s (buffer copies, compression failure), all fixed with tests.

This merge

  • Only scripts/test-selection.mjs conflicted; both sides' registrations are kept. app/tasks/engine.mjs merged automatically.
  • A rehearsal of the same merge (EN7 at dcccaa8 against this main): quick 2,932/0; public checker 4,354 states, private sample and real duo in both routing modes, all 0 differences.
  • New tests/engine-cache-multi-picker.test.mjs: one picker can't read another picker's cached data through a shared calculator.
  • Details in docs/audit/engine-main-gate.md.

Open, not blocking: cold replies still spend ~0.23 s sizing the baseline snapshot. Moving it off the reply path needs size bounds from the calculator (a worker.mjs change), so it's a later item.

🤖 Generated with Claude Code

fenerax and others added 16 commits October 28, 2026 22:01
Exports everything the calculator reads at startup into a verified,
language-neutral bundle (JSON with Map/Set/undefined tags plus named
catalogue files, every file pinned by length and SHA-256). An opt-in
bundle worker loads it without touching packs, defaults files or dataset
environment variables; the normal scheduler is unchanged.

Equivalence: 14 requests plus the ready catalogue on a copied real save
and on open-f2p, zero differences. Duo startup median 3.9 s -> 2.3 s.
docs/calculator-protocol.md lists every input with sizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review on #16. An installed profile adds capabilities.features after
the legacy digest is computed, so every profile-selected export failed its
own verification; those flags are now left out of the preimage and must
match the profile's values. open-starter had no digest branch at all.
Tests cover both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measurement only, no app changes. scripts/perf/engine-profile.mjs runs the
reference calculator as a process worker per request type (cold and warm,
five runs, CPU core-seconds, RSS, optional CPU and heap sampling).
docs/perf/engine-profile.md records the baseline on a copied real save and
open-f2p (duo: compile 1.8 s, cold play 4.8 s and 8.2 core-s, warm play
54 ms, 688 MiB at ready), the port surface, and the bake-off plan: port
evaluate first behind supports, race an optimised-JS contender on the same
boundary, compare at 1/8/32 pickers with zero checker differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
calculator/reference hosts the unchanged task worker behind protocol 1.
tools/calc-check replays a corpus (generated games, harvested unit-test
fixtures, a real save) against a reference and a candidate and compares
every reply exactly. Only measurement fields listed in volatile.json are
ignored, each with its source line. Reports hold paths and hashes only.

Built in Codex Cloud (EN1). Integration fixes for Windows: module flags
for fixture capture are passed as file URLs, and the wrong-candidate
wrapper resolves its child with fileURLToPath instead of URL.pathname.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anging

A result part was decoded after its request left the pending map, so a
decode error could no longer reject it and check() waited forever. Parts
are now decoded first. init() now races its write against the watchdog
and kills a candidate that never reads stdin; the ready promise is
handled from construction, so a timeout no longer surfaces as an
unhandled rejection that drops the report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It runs three core test files in a child process. Under the full suite on
GitHub it passed 30 s; locally it takes 10-13 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in P1 (plugin finished tasks and Roll 2 summaries, #22) and the
worker protocol step (#23: worker supersession, state refs, cache
inputs and cancel scoped by pickerId; absent pickerId is legacy-shared).

The only conflict was scripts/test-selection.mjs, where both sides
added entries; the resolution keeps both, with no duplicates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single private run of the full 414-state corpus projects to about 12
hours: the calculator's own CPU dominates. So the private gate now runs
as N parallel shards and keeps every state, rather than a sample.

- --shard i/n assigns states by interleaving them in manifest order
  after limits. Each shard report records:
  - the corpus manifest and its hash;
  - the seed, engine versions and configuration;
  - its state ids, comparisons and differences.
- --merge-reports refuses mismatched manifests, seeds, versions or
  configurations. It also refuses gaps, overlaps and wrong assignments.
  Zero differences over full coverage is the passing gate. Partial
  merges are not supported.
- Preparation per shard fell from 995 s to about 32 s:
  - only the shard's save snapshots are loaded, lazily, with parsed
    static data reused;
  - comparisons walk values structurally and hash only differing
    leaves.

  Verdicts are unchanged: same public comparisons, and the wrong
  candidate is caught at identical paths.
- New perf tools: calc-check-profile.mjs (numeric profiler) and
  calc-check-normalized-parity.mjs (checks all 414 normalized inputs
  against the earlier fingerprint).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes both Codex findings on #28:
- Retained archives were decompressed while listing, and every archived
  document stayed reachable for the whole check. That included documents
  belonging to other shards. The listing now keeps only archive names and
  snapshot ids. A shard decompresses the archives holding its own states
  on demand, at most two at a time.
- A small save has no static/ or history/ directory. The source
  inventory now treats a missing directory as empty, instead of failing
  with ENOENT.

The reader test checks both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two benchmarks under scripts/perf: one for the calculator protocol and
one for the server. Both use released packs with fresh and grown pickers,
plus the production transport. The report in docs/perf/engine-profile.md
sizes the 6 vCPU / 11 GiB VPS:

- MP4 as it stands: 2 contexts, 4 calculators. Cold median 8.11 s,
  warm p95 at most 47 ms, peak RSS 6.13 GiB.
- With a global calculator cap (MP4 lacks one): 8 contexts sharing 2
  calculators. Cold p95 6.36 s, warm p95 at most 0.93 s, peak RSS at
  most 6.26 GiB.
- One worker shares compiled data across pickers. For 8 pickers, startup
  CPU drops from 2.33 to 0.26 core-seconds and RSS from 1,357 to 376 MiB.

The earlier 32-context recommendation is withdrawn: it came from empty
synthetic pickers. App code is unchanged. The smoke test runs on Linux
only, because the benchmark reads /proc and getconf.

Cloud task EN6 (START ff08cea): quick 2,466 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Snapshot and eligibility caches are shared across pickers in a worker,
admitted against one budget. At 384 MiB the two-picker warm round goes
from 10.6 s to 6.5 s, the eight-picker p95 improves 16.8% and median
peak RSS drops 261 MiB. 256 MiB was faster for eight pickers but made
two pickers 39% slower: the proof reservation left too little room for
snapshots. The EN6 capacity test now expects 4 engine evaluations: EN7
computes the four unique inputs and reuses the rest.

Public calc-check: 4,067 states, 70,384 comparisons, 0 differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty sandbox request without a preview or planning chunk reuses the
baseline snapshot, which belongs to the complete cache. The retained
playResult and sandboxDelta were attached after the only refresh, and
that refresh went to the sandbox cache, so their bytes stayed uncharged
(the new test measures 75,651 bytes against 54,091 charged). Refresh
both caches after the objects are assigned.

Public calc-check 4,067 states and private sample 8 states: 0 differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 48-shard private check showed EN7 about 30% slower per request for
one picker: every insert re-measured whole snapshot graphs. Snapshot
sizes are now measured once per component and reused, and eligibility
buffers are charged in full until idle compression replaces them, so
the budget is never under-charged.

Interleaved single-picker latency against the lane, 5 pairs per state:
+6.0%, +2.5% and -1.6% (was +25% to +36%). Multi-picker gains kept at
384 MiB: two-picker warm round 17.0 s to 8.2 s, eight-picker p95 5.0 s
to 2.4 s, eight-picker peak RSS 2.40 to 1.98 GiB. Public calc-check
4,067 states: 0 differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A caller that mutated a Buffer returned from a raw (not yet compressed)
eligibility hit corrupted later results, and a compression failure in
the idle callback escaped and ended the worker. Raw hits now decode
from a copy, and a failed compression keeps the fully charged raw
buffer. Found by the Cloud review of 2a35bcc; the new tests fail on it.

Quick suite: 2,496 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings main (the multiplayer lane and G15) into lane/engine so the lane
can go to main. Only scripts/test-selection.mjs conflicted; both sides'
registrations are kept. engine.mjs merged automatically and keeps both
EN7's shared budget and main's slayer gates and ports-scroll rules.

Adds tests/engine-cache-multi-picker.test.mjs (one picker can never read
another picker's cached data through one shared calculator) and
docs/audit/engine-main-gate.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-31T15:58:37.128649Z
We couldn’t complete this review. Comment @codex review to try again.
c17fc74 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@fenerax

fenerax commented Oct 31, 2026

Copy link
Copy Markdown
Owner Author

Holding this merge. The gate run passed every correctness check (public 4,354 states, private 8 states, real duo in both modes, option-absent parity: all 0 differences), but on the private sample EN7 makes some interactive requests slower than main: chunk planning 336 to 1,161 ms, sandbox 3,178 to 5,052 ms, area check 9,969 to 13,441 ms (one trial, overlapping other checks). A Cloud task is reproducing and fixing this on lane/engine; the fix comes back here through a lane merge.

fenerax and others added 5 commits November 1, 2026 03:20
Transactional cache admission (a rejected entry never evicts the
baseline), cached-draft reuse, prediction bounds, sizing reused from the
eligibility proof (137 -> 32 ms), and the temporary sizing buffer is
released. Multi-picker gains hold at 384 MiB: 2 pickers 16.5 -> 7.8 s,
8 pickers 117.8 -> 60.5 s; peak worker RSS 1.97 / 2.03 GiB.

Remaining over 5% (single picker): state-7 sandbox +6.7%, detailed first
+7.6%, area +8.7 to +13.8%. Checker: 16 public shards and the private
8-state sample clean (0 differences).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/tasks/worker.mjs
#	scripts/test-selection.mjs
# Conflicts:
#	app/tasks/engine.mjs
#	app/tasks/worker.mjs
#	scripts/test-selection.mjs
…mates

Main re-prepares chunk predictions when the revision changes (pending offers),
reading state.revision. The lane's cached sandbox drafts keep their prediction
but drop their state, so a cached detailed draft passed undefined state and
threw. Compare against the caller's state (source) and rebuild the hypothetical
state when a cached draft's prediction is stale.

Main also added the first static JSON import in app/ (unknown-drop estimates).
The calculator bundle forbids source JSON reads, so the estimates are now an
.mjs module with identical contents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fenerax
fenerax merged commit 5c984be into main Nov 1, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant