Repository navigation
Conversation
foldTranscript walked the records three times and called verifyTranscriptRecord in each pass: once to build the per-room map for gap analysis, once inside the hasDeadlineFrame probe, and once in the fold itself. tryDecodeFrame ran twice for the same reason. Ed25519 verification dominates the cost of a large retained export, so the warning feature roughly tripled the price of a fold even for lines that never decode as TCLK frames. This patch builds on the verified-row gap analysis from flop-labs#94/flop-labs#97. Composing that pass with deadline detection and folding repeated signature verification, so this refactor caches verification and decoding once per record and shares the result across all three consumers. No protocol or state-machine behavior change; one warning index is corrected for aliased duplicate objects. Carrying the original index on each checked entry also removes the records.indexOf scan inside the gap loop, which was O(N^2) on the number of verified rows. Measured on one machine with 2000 deterministic signed records, timing a single foldTranscript call after building the input: before 6792.3 ms / 6788.9 ms after 2257.7 ms / 2259.2 ms tests/transcript-cost.test.ts pins the invariant structurally rather than by timing, which would be fragile on shared CI. ed25519 is a frozen object, so vi.spyOn cannot wrap verify; the module is mocked through vi.hoisted in an isolated file and the real implementation is called behind a counter. The test REDs on the unpatched base (12 verifications for 5 records, because the hasDeadlineFrame probe short-circuits) and GREENs here. Gates, per AGENTS.md: pnpm -r --include-workspace-root build pnpm -r --include-workspace-root test root 107/107, mcp 40/40, worker 33/33 — 180/180 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…fold @stupeterwilliams-ui instrumented the call sites on flop-labs#115 and found the case the original test missed: when every record fails verification, the unpatched fold still pays the full 3.00x. Each pass verifies before it can skip, and the hasDeadlineFrame probe's `some` returns false for an unverified record rather than short-circuiting, so it walks the entire array. Decode work drops to zero while the Ed25519 cost does not, which is why a wall-clock benchmark understates this corpus rather than exposing it. That is the input foldTranscript exists to handle, since its job is authenticating records from a supplier the caller does not trust, so it is worth pinning rather than leaving to the happy-path test. RED on base aedd1c0, GREEN here: verifies each record's signature exactly once 10 -> 4 verifies once per record even when every record fails 60 -> 20 does not re-verify a record whose line is not a TCLK frame 12 -> 5 Gates unchanged: pnpm -r --include-workspace-root build clean, root 108/108, mcp 40/40, worker 33/33. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Addresses @khenzarr's measurement on flop-labs#97: the warning pass verifies every record three times.
foldTranscriptwalks the records three times and callsverifyTranscriptRecordin each pass — once to build the per-room map for gap analysis, once inside thehasDeadlineFrameprobe, and once in the fold itself.tryDecodeFrameruns twice for the same reason. Ed25519 verification dominates the cost of a large retained export, so the warning feature roughly triples the price of a fold even for lines that never decode as TCLK frames.This patch builds on the verified-row gap analysis from flop-labs#94/flop-labs#97. Composing that pass with deadline detection and folding repeated signature verification, so this refactor caches verification and decoding once per record and shares the result across all three consumers.
Measurement
Same machine, same input, timing one
foldTranscriptcall after building 2000 deterministic signed records. The unpatched number comes from stashing onlysrc/transcript.tsand rebuilding, so both rows are measured under identical conditions:That is 3.01x, consistent with the 2.93x reported on the issue.
The test pins structure, not time
tests/transcript-cost.test.tsasserts one verification per record rather than a duration, because a timing assertion is fragile on shared CI.ed25519is a frozen object, sovi.spyOnfails withCannot redefine property: verify. The module is mocked throughvi.hoistedin an isolated file, and the real implementation is called behind a counter. Keeping it in its own file means that if the mock ever breaks it does not take the existing suites with it, and no injection seam is added to production code for the sake of a test.The test REDs on the unpatched base with
expected 12 to be 5. The 12 is itself informative: thehasDeadlineFrameprobe usesrecords.some, so it short-circuits at the second record rather than walking all five.Behavior
No protocol or state-machine behavior change; one warning index is corrected for aliased duplicate objects. Carrying the original index on each checked entry also removes the
records.indexOfscan inside the gap loop, which was O(N²) on the number of verified rows.Gates
Per
AGENTS.md, recursive with the workspace root:root 107/107, mcp 40/40, worker 33/33 — 180/180.
One conflict to flag
My other open patch against this branch, #1, appends the derived-room
firstSeqanchor to the samefor (const [room, list] of byRoom)loop. After this refactor the loop iterates checked entries rather than raw records, so that block needs one adjustment when both land:Merge in either order and I will rebase whichever lands second — say which you prefer and I will send it.