Move to session last prediction outcome - #46
Merged
Merged
Conversation
…g in-memory storage
… from its decomposition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
An agent's view of its own last turn now survives a reload, traversal speed is computed exactly, and the downloaded log records what it was written under and can be checked end to end.
get_last_prediction_outcomeno longer resets to a first-turnnullafter a reload.Deploying this bumps the storage schema from 5.1 to 5.2, which discards every stored value: agent seats (including saved credentials and headers), logs, progress and win records. Download any logs worth keeping first.
masterStatement coverage dips because two pieces of new code are not executed by the tests; both are named under Verification.
27 files, +762 / −52. App version
2.6.1→2.6.2.typecheck,lint,testandbuild:frontendare clean. Each new test was mutation-checked by reverting the logic it covers.1. The last prediction outcome, saved with the round
get_last_prediction_outcomeused to read the previous turn's outcome from memory only. After a same-tab reload or a poller rebind, the maze kept its progress while the tool reported a first-turnnull, so the agent was told nothing had happened.lastActionResultnow lives in game state and in the saved round snapshot.Four decisions:
cloneMazeActionResultruns wherever the outcome passes between the game, the agent control, the poller and storage. It copies every nested value (the submitted moves, the replay start cell, the moves schema), so nothing that still holds the original can change a saved outcome.isValidPersistedRoundreject the snapshot like any other inconsistency in a restored round.clearActionResultdoes nothing, so the reset insidestartRoundWithDimensionsis the only thing clearing it there.__commitAgentTurnsaves the snapshot. Both commit paths (replayed moves and rejected responses) now record the turn's merged outcome first. Before, a turn was saved holding the previous turn's outcome, so a tab killed before the next save restored this turn's board with last turn's moves. The two tests for this fail on the old order and pass on the new one.2. Traversal speed: rounding and exact arithmetic
Speed units now round to nearest. The one exception is within one unit of 1.0000x, where plain rounding could push a value into the wrong class: there, values below are floored and values above are ceiled.
Before, backtracker speeds were always floored and trailblazer speeds always ceiled. So 4/3 showed as
1.3334xwhile other values rounded down, and a speed rebuilt from its components downstream could land on a different figure than the one logged.The arithmetic now multiplies before it divides:
uniqueCellsVisited * scaleis an exact integer, so this is a single correctly-rounded division. The old(U / D) * scalerounds twice and can push an exact tie below.5. Checked against exact integer rounding for every U and D up to 6000:(U / D) * scale(U * scale) / D57/800 is exactly 712.5 units but used to come out as 712. The parity harness's copy of the calculation uses the same formula. The benchmark's conservative minimum winning speed example moves from
0.9900xto0.9901x.3. Storage schema 5.2 and the privacy policy
Win-speed records saved under the old rounding can sit one unit off the new rounding, so repeating an identical win could read as "0.0001 slower". Tapoo has no legacy data support, so the fix is not an allowance for old records but a schema bump, which clears everything written under 5.1.
The privacy policy now states what that guarantees: a downloaded log contains only data written under the storage schema the running build supports. Old entries are never migrated or read, so a version change cannot rewrite or corrupt stored data.
4. The downloaded log file
Three decisions:
.jsonremain. Those last characters are what tell runs apart, including experiments started within the same second on one machine. The number is the first entry's ownepochMs, so a filename can be searched for inside its log.The envelope gains two fields:
storageVersion: the schema the entries were written under. It is separate from the appversion, since two releases can share a schema. It is a string because it is an identifier: as a number,5.10would read back as5.1.entriesChecksum: FNV-1a 64 over the entries serialized compactly. A consumer can verify it from the file alone:fnv1a64Checksum(JSON.stringify(JSON.parse(text).entries)). It catches entries edited between download and report generation. It is not a signature: the algorithm is public and uses no key, so a deliberate rewrite can simply recompute it.5. The checksum at log scale
Logs can reach 100 MB.
fnv1a64Checksumdid one BigInt operation per byte and hashed a full serialized copy, which made it the slowest step of a download. Measured in Node's V8 on 92,152 generated entries (about 100 MB):JSON.stringify(payload, null, 2)The rewrite exposes three functions:
encodeIntowrites into one reused buffer, so the bytes are exactlyTextEncoder's, lone surrogates included.JSON.stringify(entries)while holding one entry's text at a time.MessageChannelrather thansetTimeout, which browsers clamp to at least 4 ms per call. A log small enough to finish in one slice never yields.A first attempt with two 32-bit halves was both wrong and slower. JavaScript's
^returns a signed integer, which corrupted the carry into the high half, and a function call per byte cost 1760 ms. The test suite keeps a BigInt implementation written straight from the specification as a reference, so the limb version is checked against an independent implementation rather than against itself.6. Tapoo Oracle
Submodule
82d835f→fc36ea4:Oracle shows
storageVersionin the report's provenance. It recomputesentriesChecksumoverenvelope.entriesexactly as stored, before unreadable entries are filtered out, so a log carrying a decode stand-in does not falsely fail. A mismatch stops the report and states both digests. A log without the field, written before this branch, loads as before.Verification
728 tests across 36 files;
typecheck,lintandbuild:frontendare clean. The branch does not touch Go, and the Go tests were not run.Mutations confirmed to fail their tests, each applied and then reverted:
nulloutcomeThe save-before-commit tests fail on the old order and pass on the new one.
Coverage dip: the new statements the tests don't execute are:
yieldToEventLoop(logs.ts:426-433). ItsMessageChannelpath only runs in a browser, because the tests inject the yield.isValidMazeActionResult(traversal.ts:396).The checksum implementations agree:
Oracle agrees with Tapoo: Oracle's own
checksumEntrieswas run against the specification reference through a real pretty-print and parse round trip, with emoji, lone surrogates,-0, non-ASCII keys and decode stand-ins in the entries. It matched, and an entry edited after download was refused. Oracle's suite passes atfc36ea4(717 tests).