From 1812bc3e72d41b06397bc3550df56c64859ca67c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:13:29 +0000 Subject: [PATCH 1/8] Initial plan From ee0df32ec4816f41ed9274a629d4f42ce282ad68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:44:07 +0000 Subject: [PATCH 2/8] Restore memory stores before on.steps in pre-activation Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/daily-cli-performance.lock.yml | 10 +++ pkg/workflow/compiler_pre_activation_job.go | 32 ++++++++ pkg/workflow/on_steps_test.go | 79 +++++++++++++++++++ 3 files changed, 121 insertions(+) diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index a352c3960f6..1c7350bba7f 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -1859,6 +1859,16 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); await main(); + # Repo memory git-based storage configuration from frontmatter processed below + - name: Clone repo-memory branch (default) + env: + GH_TOKEN: ${{ github.token }} + GITHUB_SERVER_URL: ${{ github.server_url }} + BRANCH_NAME: memory/cli-performance + TARGET_REPO: ${{ github.repository }} + MEMORY_DIR: /tmp/gh-aw/repo-memory/default + CREATE_ORPHAN: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/clone_repo_memory_branch.sh" - name: Detect recent compilation-related changes id: changes uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index e40c0d14e05..1d0405f1cc5 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -47,6 +47,7 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec steps = c.buildPreActivationSkipIfQuerySteps(data, steps, skipIfToken) steps = c.buildPreActivationSkipIfCheckFailingStep(data, steps) steps = c.buildPreActivationRolesBotsCmdSteps(data, steps) + steps = c.buildPreActivationMemoryRestoreSteps(data, steps) steps, onStepIDs, err := c.injectPreActivationOnSteps(data, steps, customSteps) if err != nil { return nil, err @@ -230,6 +231,37 @@ func (c *Compiler) buildPreActivationRolesBotsCmdSteps(data *WorkflowData, steps return steps } +// buildPreActivationMemoryRestoreSteps restores memory stores before on.steps run in pre-activation. +// This is a read-only surface: it restores/loads memory data but does not emit write-back or commit steps. +func (c *Compiler) buildPreActivationMemoryRestoreSteps(data *WorkflowData, steps []string) []string { + if len(data.OnSteps) == 0 { + return steps + } + + var memorySteps strings.Builder + + generateCacheMemorySteps(&memorySteps, data) + generateRepoMemorySteps(&memorySteps, data) + + if data.SafeOutputs != nil && data.SafeOutputs.CommentMemory != nil { + memorySteps.WriteString(" - name: Prepare comment memory files\n") + fmt.Fprintf(&memorySteps, " uses: %s\n", getCachedActionPin("actions/github-script", data)) + memorySteps.WriteString(" with:\n") + fmt.Fprintf(&memorySteps, " github-token: %s\n", getEffectiveSafeOutputGitHubToken(data.SafeOutputs.CommentMemory.GitHubToken)) + memorySteps.WriteString(" script: |\n") + memorySteps.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n") + memorySteps.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n") + memorySteps.WriteString(" const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_comment_memory_files.cjs');\n") + memorySteps.WriteString(" await main();\n") + } + + if memorySteps.Len() > 0 { + steps = append(steps, memorySteps.String()) + } + + return steps +} + func (c *Compiler) appendPreActivationSkipRolesStep(data *WorkflowData, steps []string) []string { steps = append(steps, " - name: Check skip-roles\n") steps = append(steps, fmt.Sprintf(" id: %s\n", constants.CheckSkipRolesStepID)) diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index 482eb2b9992..5a262a2a9cb 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -225,6 +225,85 @@ Test on.steps are appended after built-in checks t.Error("Expected membership check step to appear before on.steps gate step in pre_activation job") } }) + + t.Run("on_steps_with_memory_stores_restored_in_pre_activation", func(t *testing.T) { + workflowContent := `--- +on: + workflow_dispatch: null + roles: all + steps: + - name: Gate check + id: gate + run: echo "checking..." +tools: + cache-memory: true + repo-memory: true + comment-memory: true +engine: copilot +--- + +Test on.steps with memory stores restored in pre-activation +` + workflowFile := filepath.Join(tmpDir, "test-on-steps-memory-pre-activation.md") + if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil { + t.Fatal(err) + } + + err := compiler.CompileWorkflow(workflowFile) + if err != nil { + t.Fatalf("CompileWorkflow() returned error: %v", err) + } + + lockFile := stringutil.MarkdownToLockFile(workflowFile) + lockContent, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + lockContentStr := string(lockContent) + var preActivationSectionBuilder strings.Builder + inPreActivation := false + for line := range strings.SplitSeq(lockContentStr, "\n") { + if strings.HasPrefix(line, " pre_activation:") { + inPreActivation = true + } + if inPreActivation { + // Next top-level job (2 spaces, not deeper indentation) ends pre_activation section. + if strings.HasPrefix(line, " ") && + !strings.HasPrefix(line, " ") && + strings.HasSuffix(line, ":") && + !strings.HasPrefix(line, " pre_activation:") { + break + } + preActivationSectionBuilder.WriteString(line) + preActivationSectionBuilder.WriteByte('\n') + } + } + preActivationSection := preActivationSectionBuilder.String() + if preActivationSection == "" { + t.Fatalf("Expected pre_activation section in lock file. Lock file:\n%s", lockContentStr) + } + + // Memory stores should be restored before on.steps run in pre-activation. + assert.Contains(t, preActivationSection, "Restore cache-memory file share data") + assert.Contains(t, preActivationSection, "Clone repo-memory branch (default)") + assert.Contains(t, preActivationSection, "Prepare comment memory files") + assert.Contains(t, preActivationSection, " id: gate") + + cacheRestoreIdx := strings.Index(preActivationSection, "Restore cache-memory file share data") + repoRestoreIdx := strings.Index(preActivationSection, "Clone repo-memory branch (default)") + commentMemoryIdx := strings.Index(preActivationSection, "Prepare comment memory files") + gateStepIdx := strings.Index(preActivationSection, " id: gate") + if cacheRestoreIdx == -1 || repoRestoreIdx == -1 || commentMemoryIdx == -1 || gateStepIdx == -1 { + t.Fatalf("Expected memory restore steps and gate step in pre_activation section. Section:\n%s", preActivationSection) + } + assert.Less(t, cacheRestoreIdx, gateStepIdx, "cache-memory should be restored before on.steps") + assert.Less(t, repoRestoreIdx, gateStepIdx, "repo-memory should be restored before on.steps") + assert.Less(t, commentMemoryIdx, gateStepIdx, "comment-memory should be restored before on.steps") + + // Pre-activation must not include write-back/commit steps. + assert.NotContains(t, preActivationSection, "Commit cache-memory changes") + assert.NotContains(t, preActivationSection, "Push repo-memory changes") + }) } // TestExtractOnSteps tests the extractOnSteps function directly From ab613047f287b680b06b25d955290470d6d5312d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:53:12 +0000 Subject: [PATCH 3/8] docs(adr): add draft ADR-44015 for memory store exposure to on.steps pre-activation --- ...emory-stores-to-on-steps-pre-activation.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/adr/44015-expose-memory-stores-to-on-steps-pre-activation.md diff --git a/docs/adr/44015-expose-memory-stores-to-on-steps-pre-activation.md b/docs/adr/44015-expose-memory-stores-to-on-steps-pre-activation.md new file mode 100644 index 00000000000..9b45fde9d11 --- /dev/null +++ b/docs/adr/44015-expose-memory-stores-to-on-steps-pre-activation.md @@ -0,0 +1,48 @@ +# ADR-44015: Expose Memory Stores to `on.steps` in Pre-Activation + +**Date**: 2026-07-07 +**Status**: Draft +**Deciders**: Unknown + +--- + +### Context + +The gh-aw workflow engine compiles workflows into a `pre_activation` job and an agent job. Custom dispatch-gating steps defined via `on.steps` execute in `pre_activation`, but all three memory stores (cache-memory, repo-memory, comment-memory) were previously only restored in the agent job. This meant that any `on.steps` logic requiring prior memory state — for example, a gate step that reads a cached value to decide whether to proceed — either had to forgo memory access or consume an additional LLM turn in the agent phase to hydrate state. The inability to access memory in pre-activation limited the expressiveness of deterministic, non-LLM dispatch gates. + +### Decision + +We will restore all configured memory stores (cache-memory, repo-memory, comment-memory) in the pre-activation job before `on.steps` gates execute. The restore is **read-only**: it reuses the existing restore/load paths for each memory type and does not add cache-commit or repo-push write-back steps to pre-activation. Hydration is gated on the presence of `on.steps` in the workflow definition so that workflows without custom steps are unaffected. + +### Alternatives Considered + +#### Alternative 1: Execute `on.steps` in the Agent Job Instead of Pre-Activation + +Move the `on.steps` execution point from `pre_activation` to the agent job, where memory is already available. + +This would avoid the need to restore memory in a second location, but it would break the architectural contract that `on.steps` runs before the LLM is invoked. The entire purpose of `on.steps` is to gate activation deterministically and cheaply; running it in the agent job would consume a full LLM turn even when the gate rejects the request, negating the cost benefit. + +#### Alternative 2: Require Workflows to Declare Their Own Memory Restoration Steps + +Leave pre-activation unchanged and let workflow authors manually add memory restore steps before their `on.steps` gates via explicit YAML. + +This avoids centralizing the restore logic in the compiler, but it places the burden on every workflow author to know the correct restore incantation for each memory type. It also creates drift risk: if the restore steps change, all affected workflows must be updated. The compiler already owns the pattern for generating memory steps; extending it is the consistent approach. + +### Consequences + +#### Positive +- `on.steps` gates can now read prior memory state to make deterministic dispatch decisions without spending an LLM turn. +- Reusing the existing restore/load paths ensures memory-type-specific details (branch names, cache keys, token handling) remain in a single authoritative location. +- The read-only constraint prevents pre-activation from accidentally mutating memory, preserving the invariant that only the agent job writes back. + +#### Negative +- Every pre-activation run for a workflow with `on.steps` now executes additional restore steps, adding latency even when the gate does not use memory. +- The pre-activation job compiler gains a new code path (`buildPreActivationMemoryRestoreSteps`), increasing the surface area that must be maintained as memory store implementations evolve. + +#### Neutral +- The change is scoped to workflows that define `on.steps`; workflows without `on.steps` are compiled identically to before. +- The `.github/workflows/daily-cli-performance.lock.yml` lock file is regenerated as a side effect of the compiler change, reflecting the new restore step. + +--- + +*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.* From 315c55932439f9fddb567d7388f55161fb8798d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:03:11 +0000 Subject: [PATCH 4/8] Fix pre-activation cache restore-only behavior for on.steps Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 19 ++++++++++++++++++- pkg/workflow/on_steps_test.go | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 1d0405f1cc5..ed25eb9639e 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -240,7 +240,7 @@ func (c *Compiler) buildPreActivationMemoryRestoreSteps(data *WorkflowData, step var memorySteps strings.Builder - generateCacheMemorySteps(&memorySteps, data) + generatePreActivationCacheMemoryRestoreSteps(&memorySteps, data) generateRepoMemorySteps(&memorySteps, data) if data.SafeOutputs != nil && data.SafeOutputs.CommentMemory != nil { @@ -262,6 +262,23 @@ func (c *Compiler) buildPreActivationMemoryRestoreSteps(data *WorkflowData, step return steps } +func generatePreActivationCacheMemoryRestoreSteps(builder *strings.Builder, data *WorkflowData) { + if data.CacheMemoryConfig == nil || len(data.CacheMemoryConfig.Caches) == 0 { + return + } + + preActivationData := *data + preActivationData.CacheMemoryConfig = &CacheMemoryConfig{ + Caches: make([]CacheMemoryEntry, len(data.CacheMemoryConfig.Caches)), + } + copy(preActivationData.CacheMemoryConfig.Caches, data.CacheMemoryConfig.Caches) + for i := range preActivationData.CacheMemoryConfig.Caches { + preActivationData.CacheMemoryConfig.Caches[i].RestoreOnly = true + } + + generateCacheMemorySteps(builder, &preActivationData) +} + func (c *Compiler) appendPreActivationSkipRolesStep(data *WorkflowData, steps []string) []string { steps = append(steps, " - name: Check skip-roles\n") steps = append(steps, fmt.Sprintf(" id: %s\n", constants.CheckSkipRolesStepID)) diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index 5a262a2a9cb..d38edb2b495 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -303,6 +303,8 @@ Test on.steps with memory stores restored in pre-activation // Pre-activation must not include write-back/commit steps. assert.NotContains(t, preActivationSection, "Commit cache-memory changes") assert.NotContains(t, preActivationSection, "Push repo-memory changes") + assert.Contains(t, preActivationSection, "uses: "+getActionPin("actions/cache/restore")) + assert.NotContains(t, preActivationSection, "uses: "+getActionPin("actions/cache")) }) } From d6faece1ad25bfc0cf480bd75b330d3e9b6674c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:16:29 +0000 Subject: [PATCH 5/8] Harden pre-activation cache restore-only copy and test assertions Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 32 +++++++++++++++------ pkg/workflow/on_steps_test.go | 1 + 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index ed25eb9639e..f2b672e90a0 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -267,16 +267,32 @@ func generatePreActivationCacheMemoryRestoreSteps(builder *strings.Builder, data return } - preActivationData := *data - preActivationData.CacheMemoryConfig = &CacheMemoryConfig{ - Caches: make([]CacheMemoryEntry, len(data.CacheMemoryConfig.Caches)), - } - copy(preActivationData.CacheMemoryConfig.Caches, data.CacheMemoryConfig.Caches) - for i := range preActivationData.CacheMemoryConfig.Caches { - preActivationData.CacheMemoryConfig.Caches[i].RestoreOnly = true + preActivationData := &WorkflowData{ + ParsedTools: data.ParsedTools, + SafeOutputs: data.SafeOutputs, + CacheMemoryConfig: &CacheMemoryConfig{ + Caches: make([]CacheMemoryEntry, len(data.CacheMemoryConfig.Caches)), + }, + } + for i, cache := range data.CacheMemoryConfig.Caches { + cacheCopy := CacheMemoryEntry{ + ID: cache.ID, + Key: cache.Key, + Description: cache.Description, + RestoreOnly: true, + Scope: cache.Scope, + } + if cache.AllowedExtensions != nil { + cacheCopy.AllowedExtensions = append([]string(nil), cache.AllowedExtensions...) + } + if cache.RetentionDays != nil { + retentionDays := *cache.RetentionDays + cacheCopy.RetentionDays = &retentionDays + } + preActivationData.CacheMemoryConfig.Caches[i] = cacheCopy } - generateCacheMemorySteps(builder, &preActivationData) + generateCacheMemorySteps(builder, preActivationData) } func (c *Compiler) appendPreActivationSkipRolesStep(data *WorkflowData, steps []string) []string { diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index d38edb2b495..6ad8db75409 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -304,6 +304,7 @@ Test on.steps with memory stores restored in pre-activation assert.NotContains(t, preActivationSection, "Commit cache-memory changes") assert.NotContains(t, preActivationSection, "Push repo-memory changes") assert.Contains(t, preActivationSection, "uses: "+getActionPin("actions/cache/restore")) + assert.NotContains(t, preActivationSection, "uses: actions/cache@") assert.NotContains(t, preActivationSection, "uses: "+getActionPin("actions/cache")) }) } From 85aabb5c8a2eabb4c547af2b18d613de9497b254 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:21:05 +0000 Subject: [PATCH 6/8] Address remaining pre-activation review follow-ups Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 37 ++++++++++--------- pkg/workflow/on_steps_test.go | 39 +++++++++++---------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index f2b672e90a0..09a2ab127b1 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -238,25 +238,30 @@ func (c *Compiler) buildPreActivationMemoryRestoreSteps(data *WorkflowData, step return steps } - var memorySteps strings.Builder + var cacheMemorySteps strings.Builder + generatePreActivationCacheMemoryRestoreSteps(&cacheMemorySteps, data) + if cacheMemorySteps.Len() > 0 { + steps = append(steps, cacheMemorySteps.String()) + } - generatePreActivationCacheMemoryRestoreSteps(&memorySteps, data) - generateRepoMemorySteps(&memorySteps, data) + var repoMemorySteps strings.Builder + generateRepoMemorySteps(&repoMemorySteps, data) + if repoMemorySteps.Len() > 0 { + steps = append(steps, repoMemorySteps.String()) + } if data.SafeOutputs != nil && data.SafeOutputs.CommentMemory != nil { - memorySteps.WriteString(" - name: Prepare comment memory files\n") - fmt.Fprintf(&memorySteps, " uses: %s\n", getCachedActionPin("actions/github-script", data)) - memorySteps.WriteString(" with:\n") - fmt.Fprintf(&memorySteps, " github-token: %s\n", getEffectiveSafeOutputGitHubToken(data.SafeOutputs.CommentMemory.GitHubToken)) - memorySteps.WriteString(" script: |\n") - memorySteps.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n") - memorySteps.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n") - memorySteps.WriteString(" const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_comment_memory_files.cjs');\n") - memorySteps.WriteString(" await main();\n") - } - - if memorySteps.Len() > 0 { - steps = append(steps, memorySteps.String()) + var commentMemorySteps strings.Builder + commentMemorySteps.WriteString(" - name: Prepare comment memory files\n") + fmt.Fprintf(&commentMemorySteps, " uses: %s\n", getCachedActionPin("actions/github-script", data)) + commentMemorySteps.WriteString(" with:\n") + fmt.Fprintf(&commentMemorySteps, " github-token: %s\n", getEffectiveSafeOutputGitHubToken(data.SafeOutputs.CommentMemory.GitHubToken)) + commentMemorySteps.WriteString(" script: |\n") + commentMemorySteps.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n") + commentMemorySteps.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n") + commentMemorySteps.WriteString(" const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_comment_memory_files.cjs');\n") + commentMemorySteps.WriteString(" await main();\n") + steps = append(steps, commentMemorySteps.String()) } return steps diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index 6ad8db75409..345144e73cd 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -5,6 +5,7 @@ package workflow import ( "os" "path/filepath" + "regexp" "strings" "testing" @@ -260,25 +261,27 @@ Test on.steps with memory stores restored in pre-activation t.Fatalf("Failed to read lock file: %v", err) } lockContentStr := string(lockContent) - var preActivationSectionBuilder strings.Builder - inPreActivation := false - for line := range strings.SplitSeq(lockContentStr, "\n") { - if strings.HasPrefix(line, " pre_activation:") { - inPreActivation = true - } - if inPreActivation { - // Next top-level job (2 spaces, not deeper indentation) ends pre_activation section. - if strings.HasPrefix(line, " ") && - !strings.HasPrefix(line, " ") && - strings.HasSuffix(line, ":") && - !strings.HasPrefix(line, " pre_activation:") { - break - } - preActivationSectionBuilder.WriteString(line) - preActivationSectionBuilder.WriteByte('\n') - } + preActivationStart := strings.Index(lockContentStr, "\n pre_activation:\n") + if preActivationStart == -1 { + preActivationStart = strings.Index(lockContentStr, " pre_activation:\n") + } + if preActivationStart == -1 { + t.Fatalf("Expected pre_activation section in lock file. Lock file:\n%s", lockContentStr) + } + + sectionBodyStart := preActivationStart + if strings.HasPrefix(lockContentStr[preActivationStart:], "\n") { + sectionBodyStart++ + } + jobStartPattern := regexp.MustCompile(`\n [A-Za-z0-9_-]+:\n`) + searchStart := sectionBodyStart + len(" pre_activation:\n") + nextJobLoc := jobStartPattern.FindStringIndex(lockContentStr[searchStart:]) + var preActivationSection string + if nextJobLoc == nil { + preActivationSection = lockContentStr[sectionBodyStart:] + } else { + preActivationSection = lockContentStr[sectionBodyStart : searchStart+nextJobLoc[0]+1] } - preActivationSection := preActivationSectionBuilder.String() if preActivationSection == "" { t.Fatalf("Expected pre_activation section in lock file. Lock file:\n%s", lockContentStr) } From 6167377f6013ef669f5222f84c5f73cd0151579c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 02:04:35 +0000 Subject: [PATCH 7/8] Add on.restore-memory toggle for pre_activation memory hydration Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/daily-cli-performance.lock.yml | 10 -- .../src/content/docs/reference/frontmatter.md | 1 + docs/src/content/docs/reference/triggers.md | 3 + pkg/parser/schemas/main_workflow_schema.json | 4 + .../compiler_orchestrator_workflow.go | 7 + pkg/workflow/compiler_pre_activation_job.go | 28 +++- pkg/workflow/on_steps_test.go | 140 ++++++++++++++++++ pkg/workflow/workflow_data.go | 1 + 8 files changed, 183 insertions(+), 11 deletions(-) diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index 305a8bbae82..792cfd0d305 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -1861,16 +1861,6 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); await main(); - # Repo memory git-based storage configuration from frontmatter processed below - - name: Clone repo-memory branch (default) - env: - GH_TOKEN: ${{ github.token }} - GITHUB_SERVER_URL: ${{ github.server_url }} - BRANCH_NAME: memory/cli-performance - TARGET_REPO: ${{ github.repository }} - MEMORY_DIR: /tmp/gh-aw/repo-memory/default - CREATE_ORPHAN: true - run: bash "${RUNNER_TEMP}/gh-aw/actions/clone_repo_memory_branch.sh" - name: Detect recent compilation-related changes id: changes uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md index 13a2588d934..79a2c5e11d1 100644 --- a/docs/src/content/docs/reference/frontmatter.md +++ b/docs/src/content/docs/reference/frontmatter.md @@ -79,6 +79,7 @@ The `on:` section uses standard GitHub Actions syntax to define workflow trigger - `skip-if-match:` - Skip execution when a search query has matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth) - `skip-if-no-match:` - Skip execution when a search query has no matches (supports `scope: none`; use top-level `on.github-token` / `on.github-app` for custom auth) - `steps:` - Inject custom deterministic steps into the pre-activation job (saves one workflow job vs. multi-job pattern) +- `restore-memory:` - Opt in to restoring memory stores before `on.steps` in pre-activation (default: `false`) - `permissions:` - Grant additional GitHub token scopes to the pre-activation job (for use with `on.steps:` API calls) - `needs:` - Add custom job dependencies that both `pre_activation` and `activation` must wait for - `github-token:` - Custom token for activation job reactions, status comments, and skip-if search queries diff --git a/docs/src/content/docs/reference/triggers.md b/docs/src/content/docs/reference/triggers.md index 4f4fba77b61..0434c97632c 100644 --- a/docs/src/content/docs/reference/triggers.md +++ b/docs/src/content/docs/reference/triggers.md @@ -682,6 +682,7 @@ Inject custom deterministic steps directly into the pre-activation job. Steps ru on: issues: types: [opened] + restore-memory: true steps: - name: Check issue label id: label_check @@ -708,6 +709,8 @@ if: needs.pre_activation.outputs.has_bug_label == 'true' Explicit outputs in `jobs.pre-activation.outputs` take precedence over auto-wired `_result` outputs on key collision. +Set `on.restore-memory: true` to restore memory stores (`cache-memory`, `repo-memory`, `comment-memory`) before `on.steps` run. The default is `false`. + ### Pre-Activation and Activation Dependencies (`on.needs:`) Add custom jobs that both `pre_activation` and `activation` should depend on. Use this when `on.github-app` credentials come from a job output (for example, a secret-manager fetch job). diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 453c5dd453c..cb11f05bdb2 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -2152,6 +2152,10 @@ "default": [], "examples": [["secrets_fetcher"]] }, + "restore-memory": { + "type": "boolean", + "description": "When true, restore cache-memory, repo-memory, and comment-memory stores before on.steps in pre_activation. Defaults to false." + }, "steps": { "type": "array", "description": "Steps to inject into the pre-activation job. These steps run after all built-in checks (membership, stop-time, skip-if, etc.) and their results are exposed as pre-activation outputs. Use 'id' on steps to reference their results via needs.pre_activation.outputs._result.", diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index e222a93ea2a..37bbfcf3260 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -710,5 +710,12 @@ func (c *Compiler) processOnSectionAndFilters( } workflowData.OnNeeds = onNeeds + // Extract on.restore-memory to opt in to pre-activation memory restore for on.steps. + onRestoreMemory, err := extractOnRestoreMemory(frontmatter) + if err != nil { + return err + } + workflowData.OnRestoreMemory = onRestoreMemory + return nil } diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 09a2ab127b1..4ce8886d125 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -234,7 +234,7 @@ func (c *Compiler) buildPreActivationRolesBotsCmdSteps(data *WorkflowData, steps // buildPreActivationMemoryRestoreSteps restores memory stores before on.steps run in pre-activation. // This is a read-only surface: it restores/loads memory data but does not emit write-back or commit steps. func (c *Compiler) buildPreActivationMemoryRestoreSteps(data *WorkflowData, steps []string) []string { - if len(data.OnSteps) == 0 { + if len(data.OnSteps) == 0 || !data.OnRestoreMemory { return steps } @@ -853,6 +853,32 @@ func extractOnNeeds(frontmatter map[string]any) ([]string, error) { return parseOnNeedsValues(onMap) } +// extractOnRestoreMemory extracts the optional 'restore-memory' field from the 'on:' section. +// Default is false when unset. +func extractOnRestoreMemory(frontmatter map[string]any) (bool, error) { + onValue, exists := frontmatter["on"] + if !exists || onValue == nil { + return false, nil + } + + onMap, ok := onValue.(map[string]any) + if !ok { + return false, nil + } + + restoreMemoryValue, exists := onMap["restore-memory"] + if !exists || restoreMemoryValue == nil { + return false, nil + } + + restoreMemory, ok := restoreMemoryValue.(bool) + if !ok { + return false, fmt.Errorf("on.restore-memory must be a boolean, got %T", restoreMemoryValue) + } + + return restoreMemory, nil +} + func parseOnNeedsValues(onMap map[string]any) ([]string, error) { if onMap == nil { return nil, nil diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index 345144e73cd..f1b25319196 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -232,6 +232,7 @@ Test on.steps are appended after built-in checks on: workflow_dispatch: null roles: all + restore-memory: true steps: - name: Gate check id: gate @@ -310,6 +311,71 @@ Test on.steps with memory stores restored in pre-activation assert.NotContains(t, preActivationSection, "uses: actions/cache@") assert.NotContains(t, preActivationSection, "uses: "+getActionPin("actions/cache")) }) + + t.Run("on_steps_without_restore_memory_does_not_restore_stores_in_pre_activation", func(t *testing.T) { + workflowContent := `--- +on: + workflow_dispatch: null + roles: all + steps: + - name: Gate check + id: gate + run: echo "checking..." +tools: + cache-memory: true + repo-memory: true + comment-memory: true +engine: copilot +--- + +Test on.steps without on.restore-memory +` + workflowFile := filepath.Join(tmpDir, "test-on-steps-memory-pre-activation-disabled.md") + if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil { + t.Fatal(err) + } + + err := compiler.CompileWorkflow(workflowFile) + if err != nil { + t.Fatalf("CompileWorkflow() returned error: %v", err) + } + + lockFile := stringutil.MarkdownToLockFile(workflowFile) + lockContent, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + lockContentStr := string(lockContent) + preActivationStart := strings.Index(lockContentStr, "\n pre_activation:\n") + if preActivationStart == -1 { + preActivationStart = strings.Index(lockContentStr, " pre_activation:\n") + } + if preActivationStart == -1 { + t.Fatalf("Expected pre_activation section in lock file. Lock file:\n%s", lockContentStr) + } + + sectionBodyStart := preActivationStart + if strings.HasPrefix(lockContentStr[preActivationStart:], "\n") { + sectionBodyStart++ + } + jobStartPattern := regexp.MustCompile(`\n [A-Za-z0-9_-]+:\n`) + searchStart := sectionBodyStart + len(" pre_activation:\n") + nextJobLoc := jobStartPattern.FindStringIndex(lockContentStr[searchStart:]) + var preActivationSection string + if nextJobLoc == nil { + preActivationSection = lockContentStr[sectionBodyStart:] + } else { + preActivationSection = lockContentStr[sectionBodyStart : searchStart+nextJobLoc[0]+1] + } + if preActivationSection == "" { + t.Fatalf("Expected pre_activation section in lock file. Lock file:\n%s", lockContentStr) + } + + assert.Contains(t, preActivationSection, " id: gate") + assert.NotContains(t, preActivationSection, "Restore cache-memory file share data") + assert.NotContains(t, preActivationSection, "Clone repo-memory branch (default)") + assert.NotContains(t, preActivationSection, "Prepare comment memory files") + }) } // TestExtractOnSteps tests the extractOnSteps function directly @@ -570,6 +636,80 @@ func TestExtractOnNeeds(t *testing.T) { } } +func TestExtractOnRestoreMemory(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + expected bool + expectError bool + errorContains string + }{ + { + name: "no_on_section", + frontmatter: map[string]any{}, + expected: false, + }, + { + name: "on_section_string", + frontmatter: map[string]any{ + "on": "push", + }, + expected: false, + }, + { + name: "on_without_restore_memory", + frontmatter: map[string]any{ + "on": map[string]any{ + "workflow_dispatch": nil, + }, + }, + expected: false, + }, + { + name: "restore_memory_true", + frontmatter: map[string]any{ + "on": map[string]any{ + "restore-memory": true, + }, + }, + expected: true, + }, + { + name: "restore_memory_false", + frontmatter: map[string]any{ + "on": map[string]any{ + "restore-memory": false, + }, + }, + expected: false, + }, + { + name: "restore_memory_wrong_type", + frontmatter: map[string]any{ + "on": map[string]any{ + "restore-memory": "true", + }, + }, + expectError: true, + errorContains: "on.restore-memory must be a boolean", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + restoreMemory, err := extractOnRestoreMemory(tt.frontmatter) + if tt.expectError { + require.Error(t, err, "expected extraction error") + assert.Contains(t, err.Error(), tt.errorContains, "error should contain expected text") + return + } + + require.NoError(t, err, "expected extraction to succeed") + assert.Equal(t, tt.expected, restoreMemory, "unexpected on.restore-memory result") + }) + } +} + // TestOnPermissionsAppliedToPreActivation tests that on.permissions are applied to the pre-activation job func TestOnPermissionsAppliedToPreActivation(t *testing.T) { tmpDir := testutil.TempDir(t, "on-permissions-test") diff --git a/pkg/workflow/workflow_data.go b/pkg/workflow/workflow_data.go index 488f9c96cb0..36a80786610 100644 --- a/pkg/workflow/workflow_data.go +++ b/pkg/workflow/workflow_data.go @@ -96,6 +96,7 @@ type WorkflowData struct { SkipAuthorAssociations map[string][]string // author associations to skip by event name (on.skip-author-associations) AllowBotAuthoredTriggerComment bool // allow bot-posted-menu / user-checks-box pattern (on.allow-bot-authored-trigger-comment) OnSteps []map[string]any // steps to inject into the pre-activation job from on.steps + OnRestoreMemory bool // enable memory restore in pre-activation for on.steps via on.restore-memory (default false) OnPermissions *Permissions // additional permissions for the pre-activation job from on.permissions OnNeeds []string // custom workflow jobs that pre_activation/activation should depend on from on.needs ManualApproval string // environment name for manual approval from on: section From caac6285368fdd1f291e4cce3b373aa345cf6184 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 02:45:14 +0000 Subject: [PATCH 8/8] Enable on.restore-memory in selected memory workflows Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/daily-cli-performance.lock.yml | 13 ++++++++++++- .github/workflows/daily-cli-performance.md | 1 + .github/workflows/smoke-codex.lock.yml | 3 ++- .github/workflows/smoke-codex.md | 1 + .github/workflows/smoke-copilot.lock.yml | 3 ++- .github/workflows/smoke-copilot.md | 3 ++- pkg/workflow/frontmatter_extraction_yaml.go | 5 ++++- pkg/workflow/on_steps_test.go | 1 + 8 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index 792cfd0d305..f7d1ce419c2 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"c8064ac6104913f244b3c1f16639c0ae6115b80f1d5852eac188043a1ced1eb3","body_hash":"041fcfe0d8ef38271b062dc73c5f63e55f7045353a54e9fddc4303ce5ecbed64","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"4ae81dde8c3fcd270ffa4818afede63294c7823ee5708b392793e081ddba341a","body_hash":"041fcfe0d8ef38271b062dc73c5f63e55f7045353a54e9fddc4303ce5ecbed64","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} # gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26","digest":"sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26","digest":"sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26","digest":"sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26","digest":"sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.33","digest":"sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -64,6 +64,7 @@ name: "Daily CLI Performance Agent" on: # permissions: # Permissions applied to pre-activation job # contents: read + # restore-memory: true # Restore-memory enables pre-activation memory restore schedule: - cron: "49 14 * * *" # Friendly format: daily (scattered) @@ -1861,6 +1862,16 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); await main(); + # Repo memory git-based storage configuration from frontmatter processed below + - name: Clone repo-memory branch (default) + env: + GH_TOKEN: ${{ github.token }} + GITHUB_SERVER_URL: ${{ github.server_url }} + BRANCH_NAME: memory/cli-performance + TARGET_REPO: ${{ github.repository }} + MEMORY_DIR: /tmp/gh-aw/repo-memory/default + CREATE_ORPHAN: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/clone_repo_memory_branch.sh" - name: Detect recent compilation-related changes id: changes uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/daily-cli-performance.md b/.github/workflows/daily-cli-performance.md index 28c75b829c1..a884a7f8d4a 100644 --- a/.github/workflows/daily-cli-performance.md +++ b/.github/workflows/daily-cli-performance.md @@ -3,6 +3,7 @@ private: true emoji: "⚡" description: Daily CLI Performance - Runs benchmarks, tracks performance trends, and reports regressions on: + restore-memory: true schedule: daily workflow_dispatch: permissions: diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index bf08d059eac..2ec9bd1b6e1 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f4c25dc4e3ebaa00201eec60464e37b5d059d7c2c14344ad95baab84851c84c8","body_hash":"fae212c4ff39a682e3254fae440084a2152282e383467ff3f0c15fb9d2d69411","strict":true,"agent_id":"codex","engine_versions":{"codex":"0.142.5"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"aa0ed119e72a5a80fca3653db96ef93a846a9fc08a00e2bdaf4d7a9a968e5639","body_hash":"fae212c4ff39a682e3254fae440084a2152282e383467ff3f0c15fb9d2d69411","strict":true,"agent_id":"codex","engine_versions":{"codex":"0.142.5"}} # gh-aw-manifest: {"version":1,"secrets":["CODEX_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"924ae3a1cded613372ab5595356fb5720e22ba16","version":"v6.5.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26","digest":"sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26","digest":"sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26","digest":"sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26","digest":"sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.33","digest":"sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -76,6 +76,7 @@ on: # - smoke # Label filtering applied via job conditions types: - labeled + # restore-memory: true # Restore-memory enables pre-activation memory restore workflow_dispatch: inputs: aw_context: diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md index e1f094c2846..8a6b028ebe9 100644 --- a/.github/workflows/smoke-codex.md +++ b/.github/workflows/smoke-codex.md @@ -3,6 +3,7 @@ private: true emoji: "🧪" description: Smoke test workflow that validates Codex engine functionality by reviewing recent PRs twice daily on: + restore-memory: true slash_command: name: smoke-codex strategy: centralized diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 1eef3383a43..bfd0973066f 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"cd1db4da83dbeb24e508e618607ddd371bdce795962c9e5d58257d2a911dfc81","body_hash":"ff1e979a13078c84305141cc3861a5a2a120a2c8497fa48e4fc18115ca12a1c3","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.68"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"3bd86338a89e0309c9e8a830f8468327f4360c143b3200bc881f1a41b2dcfe9d","body_hash":"ff1e979a13078c84305141cc3861a5a2a120a2c8497fa48e4fc18115ca12a1c3","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.68"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"924ae3a1cded613372ab5595356fb5720e22ba16","version":"v6.5.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"f9f3042f7e2789586610d6e8b85c8f03e5195baf","version":"v7.2.0"},{"repo":"docker/setup-buildx-action","sha":"d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5","version":"v4.1.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26","digest":"sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26","digest":"sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26","digest":"sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26","digest":"sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.33","digest":"sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -70,6 +70,7 @@ name: "Smoke Copilot" on: + # restore-memory: true # Restore-memory enables pre-activation memory restore workflow_dispatch: inputs: aw_context: diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index 32aa73e6766..72a52f0f37a 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -2,7 +2,8 @@ private: true emoji: "🧪" description: Smoke Copilot -on: +on: + restore-memory: true slash_command: name: smoke-copilot strategy: centralized diff --git a/pkg/workflow/frontmatter_extraction_yaml.go b/pkg/workflow/frontmatter_extraction_yaml.go index 1e9575ad9bc..da2b6494470 100644 --- a/pkg/workflow/frontmatter_extraction_yaml.go +++ b/pkg/workflow/frontmatter_extraction_yaml.go @@ -125,7 +125,7 @@ func (c *Compiler) extractTopLevelYAMLSection(frontmatter map[string]any, key st return yamlStr } -// commentOutProcessedFieldsInOnSection comments out draft, fork, forks, names, labels, manual-approval, stop-after, skip-if-match, skip-if-no-match, skip-roles, reaction, lock-for-agent, steps, permissions, needs, and stale-check fields in the on section +// commentOutProcessedFieldsInOnSection comments out draft, fork, forks, names, labels, manual-approval, stop-after, skip-if-match, skip-if-no-match, skip-roles, reaction, lock-for-agent, steps, permissions, needs, restore-memory, and stale-check fields in the on section // These fields are processed separately and should be commented for documentation // Exception: names fields in sections with __gh_aw_native_label_filter__ marker in frontmatter are NOT commented out func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmatter map[string]any) string { @@ -648,6 +648,9 @@ func (c *Compiler) commentOutProcessedFieldsInOnSection(yamlStr string, frontmat // Comment out array items in needs shouldComment = true commentReason = " # Needs processed as dependency in pre-activation job" + } else if strings.HasPrefix(trimmedLine, "restore-memory:") { + shouldComment = true + commentReason = " # Restore-memory enables pre-activation memory restore" } else if strings.HasPrefix(trimmedLine, "steps:") { shouldComment = true commentReason = " # Steps injected into pre-activation job" diff --git a/pkg/workflow/on_steps_test.go b/pkg/workflow/on_steps_test.go index f1b25319196..bfe5a11eb8c 100644 --- a/pkg/workflow/on_steps_test.go +++ b/pkg/workflow/on_steps_test.go @@ -288,6 +288,7 @@ Test on.steps with memory stores restored in pre-activation } // Memory stores should be restored before on.steps run in pre-activation. + assert.Contains(t, lockContentStr, "# restore-memory: true # Restore-memory enables pre-activation memory restore") assert.Contains(t, preActivationSection, "Restore cache-memory file share data") assert.Contains(t, preActivationSection, "Clone repo-memory branch (default)") assert.Contains(t, preActivationSection, "Prepare comment memory files")