From fac703e05372b2dbac7e56a8898716feb6271b8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:41:16 +0000 Subject: [PATCH 1/4] Initial plan From 26b782ddfc5c317858d1184f7dfd7ffb406ee31c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:44:42 +0000 Subject: [PATCH 2/4] chore: outline plan for YAML generation regression fix Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3f8efa7b-625e-464f-8358-cbaff280512e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../copilot-token-optimizer.lock.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/copilot-token-optimizer.lock.yml b/.github/workflows/copilot-token-optimizer.lock.yml index f4cab22927c..ca063445018 100644 --- a/.github/workflows/copilot-token-optimizer.lock.yml +++ b/.github/workflows/copilot-token-optimizer.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"51f72231788ee21708bdccdd34b4db21d30f7eb9135d0216c7b64a7edd2addad","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"astral-sh/setup-uv","sha":"eac588ad8def6316056a12d4907a9d4d84ff7a3b","version":"eac588ad8def6316056a12d4907a9d4d84ff7a3b"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.24"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.24"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.24"},{"image":"ghcr.io/github/github-mcp-server:v1.0.0"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"astral-sh/setup-uv","sha":"eac588ad8def6316056a12d4907a9d4d84ff7a3b","version":"eac588ad8def6316056a12d4907a9d4d84ff7a3b"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.25"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.25"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.25"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.25"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.25"},{"image":"ghcr.io/github/github-mcp-server:v1.0.0"},{"image":"node:lts-alpine","digest":"sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b","pinned_image":"node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -46,11 +46,11 @@ # - astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # eac588ad8def6316056a12d4907a9d4d84ff7a3b # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.25.24 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24 -# - ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.24 -# - ghcr.io/github/gh-aw-firewall/squid:0.25.24 -# - ghcr.io/github/gh-aw-mcpg:v0.2.24 +# - ghcr.io/github/gh-aw-firewall/agent:0.25.25 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.25 +# - ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.25 +# - ghcr.io/github/gh-aw-firewall/squid:0.25.25 +# - ghcr.io/github/gh-aw-mcpg:v0.2.25 # - ghcr.io/github/github-mcp-server:v1.0.0 # - node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b @@ -469,7 +469,7 @@ jobs: const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.24 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24 ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.24 ghcr.io/github/gh-aw-firewall/squid:0.25.24 ghcr.io/github/gh-aw-mcpg:v0.2.24 ghcr.io/github/github-mcp-server:v1.0.0 node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.25 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.25 ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.25 ghcr.io/github/gh-aw-firewall/squid:0.25.25 ghcr.io/github/gh-aw-mcpg:v0.2.25 ghcr.io/github/github-mcp-server:v1.0.0 node:lts-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b - name: Write Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" @@ -732,7 +732,7 @@ jobs: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_SERVER_URL: ${{ github.server_url }} CLI_PROXY_POLICY: '{"allow-only":{"repos":"all","min-integrity":"none"}}' - CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.24' + CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.2.25' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" - name: Execute GitHub Copilot CLI @@ -746,7 +746,7 @@ jobs: export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/agent-stdio.log) # shellcheck disable=SC1003 - sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.24 --skip-pull --enable-api-proxy --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ + sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.25 --skip-pull --enable-api-proxy --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -1136,7 +1136,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.24 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.24 ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.24 ghcr.io/github/gh-aw-firewall/squid:0.25.24 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.25 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.25 ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.25 ghcr.io/github/gh-aw-firewall/squid:0.25.25 - name: Check if detection needed id: detection_guard if: always() @@ -1207,7 +1207,7 @@ jobs: export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) # shellcheck disable=SC1003 - sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.24 --skip-pull --enable-api-proxy --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ + sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --image-tag 0.25.25 --skip-pull --enable-api-proxy --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ -- /bin/bash -c 'GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE From f6d790a5e8ff77c16054bbdd960643df12992a69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:57:55 +0000 Subject: [PATCH 3/4] perf: cache frontmatter hash computation for repeated YAML generation Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3f8efa7b-625e-464f-8358-cbaff280512e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_types.go | 32 ++++++++----- pkg/workflow/compiler_yaml.go | 72 ++++++++++++++++++++++++++++-- pkg/workflow/compiler_yaml_test.go | 56 +++++++++++++++++++++++ 3 files changed, 145 insertions(+), 15 deletions(-) diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index 579552333fe..8aaf21f681d 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -99,6 +99,13 @@ type Compiler struct { inlinePrompt bool // If true, inline markdown content in YAML instead of using runtime-import macros (for Wasm builds) priorManifests map[string]*GHAWManifest // Pre-cached manifests keyed by lock file path; takes precedence over git HEAD / filesystem reads requireDocker bool // If true, fail validation when Docker is not available instead of silently skipping + frontmatterHashCache map[string]frontmatterHashCacheEntry +} + +type frontmatterHashCacheEntry struct { + fileSize int64 + fileModTimeNS int64 + frontmatterHash string } // NewCompiler creates a new workflow compiler with functional options. @@ -114,18 +121,19 @@ func NewCompiler(opts ...CompilerOption) *Compiler { // Create compiler with defaults c := &Compiler{ - verbose: false, - engineOverride: "", - version: version, - skipValidation: true, // Skip validation by default for now since existing workflows don't fully comply - actionMode: DetectActionMode(version), // Auto-detect action mode based on version - jobManager: NewJobManager(), - engineRegistry: GetGlobalEngineRegistry(), - engineCatalog: NewEngineCatalog(GetGlobalEngineRegistry()), - stepOrderTracker: NewStepOrderTracker(), - artifactManager: NewArtifactManager(), - actionPinWarnings: make(map[string]bool), // Initialize warning cache - gitRoot: gitRoot, // Auto-detected git root + verbose: false, + engineOverride: "", + version: version, + skipValidation: true, // Skip validation by default for now since existing workflows don't fully comply + actionMode: DetectActionMode(version), // Auto-detect action mode based on version + jobManager: NewJobManager(), + engineRegistry: GetGlobalEngineRegistry(), + engineCatalog: NewEngineCatalog(GetGlobalEngineRegistry()), + stepOrderTracker: NewStepOrderTracker(), + artifactManager: NewArtifactManager(), + actionPinWarnings: make(map[string]bool), // Initialize warning cache + gitRoot: gitRoot, // Auto-detected git root + frontmatterHashCache: make(map[string]frontmatterHashCacheEntry), } // Apply functional options diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index d2ab35ca8c0..4a045ad87d7 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -294,6 +294,74 @@ func (c *Compiler) generateWorkflowBody(yaml *strings.Builder, data *WorkflowDat yaml.WriteString(c.jobManager.RenderToYAML()) } +func canUseFrontmatterHashCache(rawFrontmatter map[string]any) bool { + importsValue, hasImports := rawFrontmatter["imports"] + if !hasImports { + return true + } + + switch imports := importsValue.(type) { + case []any: + return len(imports) == 0 + case []string: + return len(imports) == 0 + case string: + return strings.TrimSpace(imports) == "" + default: + return false + } +} + +func (c *Compiler) getCachedFrontmatterHash(markdownPath string) (string, bool) { + entry, ok := c.frontmatterHashCache[markdownPath] + if !ok { + return "", false + } + + info, err := os.Stat(markdownPath) + if err != nil { + return "", false + } + if info.Size() != entry.fileSize || info.ModTime().UnixNano() != entry.fileModTimeNS { + return "", false + } + + return entry.frontmatterHash, true +} + +func (c *Compiler) cacheFrontmatterHash(markdownPath string, hash string) { + info, err := os.Stat(markdownPath) + if err != nil { + return + } + + c.frontmatterHashCache[markdownPath] = frontmatterHashCacheEntry{ + fileSize: info.Size(), + fileModTimeNS: info.ModTime().UnixNano(), + frontmatterHash: hash, + } +} + +func (c *Compiler) computeFrontmatterHash(markdownPath string, rawFrontmatter map[string]any) (string, error) { + if canUseFrontmatterHashCache(rawFrontmatter) { + if cachedHash, ok := c.getCachedFrontmatterHash(markdownPath); ok { + return cachedHash, nil + } + } + + cache := c.getSharedImportCache() + hash, err := parser.ComputeFrontmatterHashFromFileWithParsedFrontmatter(markdownPath, rawFrontmatter, cache, parser.DefaultFileReader) + if err != nil { + return "", err + } + + if canUseFrontmatterHashCache(rawFrontmatter) { + c.cacheFrontmatterHash(markdownPath, hash) + } + + return hash, nil +} + func (c *Compiler) generateYAML(data *WorkflowData, markdownPath string) (string, []string, []string, error) { compilerYamlLog.Printf("Generating YAML for workflow: %s", data.Name) @@ -303,9 +371,7 @@ func (c *Compiler) generateYAML(data *WorkflowData, markdownPath string) (string // the compiled lock file identical across repeated compilations of the same workflow. var frontmatterHash string if markdownPath != "" { - baseDir := filepath.Dir(markdownPath) - cache := parser.NewImportCache(baseDir) - hash, err := parser.ComputeFrontmatterHashFromFileWithParsedFrontmatter(markdownPath, data.RawFrontmatter, cache, parser.DefaultFileReader) + hash, err := c.computeFrontmatterHash(markdownPath, data.RawFrontmatter) if err != nil { compilerYamlLog.Printf("Warning: failed to compute frontmatter hash: %v", err) // Continue without hash - non-fatal error diff --git a/pkg/workflow/compiler_yaml_test.go b/pkg/workflow/compiler_yaml_test.go index 451af36025c..f881591de2e 100644 --- a/pkg/workflow/compiler_yaml_test.go +++ b/pkg/workflow/compiler_yaml_test.go @@ -1438,3 +1438,59 @@ Test prompt. }) } } + +func TestFrontmatterHashCacheInvalidatesWhenWorkflowChanges(t *testing.T) { + tmpDir := testutil.TempDir(t, "frontmatter-hash-cache") + workflowPath := filepath.Join(tmpDir, "cache-test.md") + compiler := NewCompiler() + + writeWorkflow := func(t *testing.T, name string) { + t.Helper() + content := fmt.Sprintf(`--- +name: %s +on: push +engine: copilot +--- + +# Cache Test +`, name) + if err := os.WriteFile(workflowPath, []byte(content), 0o644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + } + + readFrontmatterHash := func(t *testing.T) string { + t.Helper() + lockPath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" + content, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + metadata, _, err := ExtractMetadataFromLockFile(string(content)) + if err != nil { + t.Fatalf("Failed to extract lock metadata: %v", err) + } + if metadata == nil || metadata.FrontmatterHash == "" { + t.Fatal("Expected lock metadata to include a frontmatter hash") + } + + return metadata.FrontmatterHash + } + + writeWorkflow(t, "first") + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("First compile failed: %v", err) + } + firstHash := readFrontmatterHash(t) + + writeWorkflow(t, "second") + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("Second compile failed: %v", err) + } + secondHash := readFrontmatterHash(t) + + if firstHash == secondHash { + t.Fatalf("Expected frontmatter hash to change after workflow update, but both were %q", firstHash) + } +} From c24048d0f4b912713ebf5bb1770fc24fd3a09be2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 08:06:33 +0000 Subject: [PATCH 4/4] perf: clean stale frontmatter hash cache entries Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3f8efa7b-625e-464f-8358-cbaff280512e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_yaml.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index 4a045ad87d7..644e8d9f71b 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -320,9 +320,11 @@ func (c *Compiler) getCachedFrontmatterHash(markdownPath string) (string, bool) info, err := os.Stat(markdownPath) if err != nil { + delete(c.frontmatterHashCache, markdownPath) return "", false } if info.Size() != entry.fileSize || info.ModTime().UnixNano() != entry.fileModTimeNS { + delete(c.frontmatterHashCache, markdownPath) return "", false } @@ -343,7 +345,8 @@ func (c *Compiler) cacheFrontmatterHash(markdownPath string, hash string) { } func (c *Compiler) computeFrontmatterHash(markdownPath string, rawFrontmatter map[string]any) (string, error) { - if canUseFrontmatterHashCache(rawFrontmatter) { + useCache := canUseFrontmatterHashCache(rawFrontmatter) + if useCache { if cachedHash, ok := c.getCachedFrontmatterHash(markdownPath); ok { return cachedHash, nil } @@ -355,7 +358,7 @@ func (c *Compiler) computeFrontmatterHash(markdownPath string, rawFrontmatter ma return "", err } - if canUseFrontmatterHashCache(rawFrontmatter) { + if useCache { c.cacheFrontmatterHash(markdownPath, hash) }