perf: fix 60% YAMLGeneration regression — skip yaml.Unmarshal for clean compiled workflows#39097
Closed
Copilot wants to merge 3 commits into
Closed
perf: fix 60% YAMLGeneration regression — skip yaml.Unmarshal for clean compiled workflows#39097Copilot wants to merge 3 commits into
Copilot wants to merge 3 commits into
Conversation
…r allowed expressions
Root cause: validateTemplateInjection Path B (skipValidation=true, the default) used
hasAnyExpressionInRunContent as a pre-scan gate. Every compiled workflow contains
${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} in a run: block (an allowed
compiler-owned expression), so the gate always fired and triggered yaml.Unmarshal
on ~80KB of YAML every compilation.
Fix: replace the gate with hasNonAllowedExpressionInRunContent, which filters each
found expression against allowedRunScriptExpressionRegex and returns true only when
a non-allowed expression is present. For correctly compiled workflows this returns
false, skipping yaml.Unmarshal entirely.
Also apply ADR-28557 bufio.Scanner → strings.Lines/SplitSeq zero-allocation changes
in parser hot paths (include_processor, include_expander, frontmatter_content).
Result: BenchmarkYAMLGeneration ~1.55ms/op (was ~9ms, historical avg ~5.57ms)
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… hot paths Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix performance regression in YAML generation by 60%
perf: fix 60% YAMLGeneration regression — skip yaml.Unmarshal for clean compiled workflows
Jun 13, 2026
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.
Every compilation triggered a full
yaml.Unmarshalon ~80KB of generated YAML despite schema validation being disabled by default — causing a 60% slowdown vs. historical baseline (~9ms vs ~5.6ms/op).Root cause
validateTemplateInjectionPath B usedhasAnyExpressionInRunContentas a cheap pre-scan gate before callingyaml.Unmarshal. Since every compiled workflow contains${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}in arun:block — a compiler-owned, intentionally allowed expression — the gate always fired, making the expensive parse unconditional.Fix
pkg/workflow/template_injection_validation.go+compiler.goReplace the pre-scan gate with
hasNonAllowedExpressionInRunContent, which filters each found${{ … }}againstallowedRunScriptExpressionRegexand returnstrueonly when a non-allowed expression is present. For correctly compiled workflows this returnsfalse, skippingyaml.Unmarshalentirely.Remove the now-dead
hasAnyExpressionInRunContentandhasExpressionInRunContentfunctions.pkg/parser/{include_processor,include_expander,frontmatter_content}.go(ADR-28557)Replace
bufio.NewScannerwithstrings.Lines/strings.SplitSeqzero-allocation iterators (Go 1.24+) in parser hot paths. Consolidate sequentialTrimRight("\n")+TrimRight("\r")calls into a singleTrimRight("\n\r").Result
BenchmarkYAMLGeneration: ~1.55ms/op — down from ~9ms, well below the 5.6ms historical average (~83% improvement).