perf: a recipe is parsed once instead of twice, and the verdict on P4 - #60
Merged
Conversation
donislawdev
force-pushed
the
perf/recipe-parsed-once
branch
from
September 6, 2026 04:35
384158b to
3049aff
Compare
donislawdev
force-pushed
the
perf/recipe-parsed-once
branch
from
September 6, 2026 04:43
3049aff to
2a7a4ea
Compare
Reading a recipe walked the document three times: the lexer for flow depth, a full parse to check for a stray second document, and then the decoder, which parsed the whole file again from scratch. The decoder now works from the tree the document check already built. NOT the fix the report asked for. That one removed the parse and counted documents from the token stream, which would have reintroduced a defect this project already fixed: recipesIn counts documents with a BODY, and a comment before a leading "---" is a document without one. Counting raw separators refused files written in ordinary YAML house style, which is why TestOneRecipeIsAcceptedWhateverSeparatorsSurroundIt exists. The parse stays and the decoder's second one goes instead, so the counting semantics are untouched. Measured on the largest recipe the size limit allows, 900 kB and 20 000 targets, nine repetitions interleaved: validate 839 -> 754 ms, ranges disjoint. The report said 36% of the read; the discarded parse measured 13% and the change delivers 10%. On a few-kilobyte recipe none of this is visible - where it shows is the batch screen, which re-reads on every keystroke. A green suite is not evidence here, because this moves the WORDS of a refusal rather than the bytes of a file, and no byte guard can see that. The owner's condition was the refusal corpus before and after: tools/probes/refusalcorpus.py drives 48 malformed and awkward recipes through validate and generate --dry-run and compares stdout, stderr and the exit code. 96 of 96 identical. Five fuzz targets, 20s each, clean. Two mutation entries were stale afterwards and one of them was mine: the existing strictness entry quoted the call that was replaced, and the one I added beside it duplicated it while naming a guard about declared keys rather than about typos. It came back NOT CAUGHT, which was a statement about my entry and not about the code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
donislawdev
force-pushed
the
perf/recipe-parsed-once
branch
from
September 6, 2026 04:57
2a7a4ea to
11b5f96
Compare
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.
Seventh chunk:
P5done, andP4closed with a measurement instead of code. These were the last two open items in the report.P5- and deliberately not the fix the report asked forReading a recipe walked the document three times: the lexer for flow depth, a full parse to check for a stray second document, and the decoder, which parsed the whole file again.
🔴 The report's fix would have reintroduced a defect this project already fixed. It proposed dropping the parse and counting documents from the token stream. But
recipesIncounts documents with a body, and a comment before a leading---is a document without one. Counting raw separators refused files written in ordinary YAML house style - which is exactly whyTestOneRecipeIsAcceptedWhateverSeparatorsSurroundItexists, with a comment describing that bug.So the parse stays and the decoder's one goes: it now works from the tree already built. The document-counting semantics are untouched.
Measured on the largest recipe the size limit allows: 900 kB, 20 000 targets, nine repetitions interleaved. A few-kilobyte recipe was already instant; where this shows is the batch screen, which re-reads on every keystroke.
The evidence a green suite could not give
This moves the words of a refusal, not the bytes of a file, and no byte guard can see that. Your condition was the corpus before and after, so
tools/probes/refusalcorpus.pynow exists: 48 malformed and awkward recipes (syntax errors, unknown keys, two BOMs, tabs, unclosed brackets, tags with nothing after them, seven separator layouts) throughvalidateandgenerate --dry-run, comparing stdout, stderr and the exit code.96 of 96 identical. Plus five fuzz targets at 20s each, clean.
Two stale mutation entries, one of them mine
The existing strictness entry quoted the call that was replaced, so it reported
SKIPand had stopped proving anything. And the entry I added beside it duplicated it while naming a guard about declared keys rather than typos - it came backNOT CAUGHT, which was a statement about my entry, not about the code. Duplicate removed, original repaired, three mutations caught.P4- true as a proportion, below the threshold in effect. No code.🟢 The report is right that the hash is 26-39% - it measures 38%.
🔴 And it does not matter.
UX5puts the reaction threshold at 100 ms. At the batch count the report itself names, a keystroke costs 2.5 ms - forty times under - and the curve is linear, so the threshold falls around eight hundred batches. Same class asP8.Three reasons it stays without code, two of them older than this session (
O145, your decision of 2026-08-27): the fix would be a defence nothing can redden; throttling invalidates the live-validation guards; and the only real remaining fix touchesrecipe_hash, which goes into somebody else's manifest - untouchable rule 10, not a performance question, for 9 ms at two hundred batches.🟢
P5already took part of it for free:O145recorded 14.24 ms at 100 batches; it is now 11.93 ms, becauseParsestopped parsing twice.Verification
go test -tags "$(cat .github/build-tags)" ./...- greenpython tools/preflight.py --quick- all 12 checks passtry-named.pyon both recipe guards - 3 mutations, all caught🤖 Generated with Claude Code