From 4bcde55d9058be12d07f47ee9c824a352b5a3a0e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 03:30:44 +0000
Subject: [PATCH 1/7] Initial plan
From 950771ff72ac9f94e858c96b6987d3d7c3a6351b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 03:52:09 +0000
Subject: [PATCH 2/7] repo-memory: filter ineligible files before
validation/upload/push
Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
---
.../agent-performance-analyzer.lock.yml | 18 +++
.../workflows/agentic-token-audit.lock.yml | 18 +++
.../agentic-token-optimizer.lock.yml | 18 +++
.github/workflows/audit-workflows.lock.yml | 18 +++
.../workflows/copilot-agent-analysis.lock.yml | 18 +++
.../copilot-centralization-optimizer.lock.yml | 18 +++
.../copilot-cli-deep-research.lock.yml | 18 +++
.../copilot-pr-nlp-analysis.lock.yml | 18 +++
.../copilot-pr-prompt-analysis.lock.yml | 18 +++
.../copilot-session-insights.lock.yml | 18 +++
...daily-awf-spec-compiler-surfacing.lock.yml | 18 +++
.../workflows/daily-cli-performance.lock.yml | 18 +++
.../daily-formal-spec-verifier.lock.yml | 18 +++
...daily-harness-experiment-proposer.lock.yml | 18 +++
.github/workflows/daily-news.lock.yml | 18 +++
.../daily-safeoutputs-git-simulator.lock.yml | 17 +++
.github/workflows/daily-storify.lock.yml | 18 +++
.../daily-testify-uber-super-expert.lock.yml | 18 +++
.github/workflows/deep-report.lock.yml | 18 +++
.github/workflows/delight.lock.yml | 18 +++
.github/workflows/eslint-refiner.lock.yml | 18 +++
.github/workflows/metrics-collector.lock.yml | 18 +++
.github/workflows/pr-triage-agent.lock.yml | 18 +++
.../workflows/security-compliance.lock.yml | 18 +++
.github/workflows/sergo.lock.yml | 18 +++
.github/workflows/smoke-ci.lock.yml | 18 +++
.../workflow-health-manager.lock.yml | 18 +++
actions/setup/js/memory_file_eligibility.cjs | 121 +++++++++++++++++
.../setup/js/memory_file_eligibility.test.cjs | 126 ++++++++++++++++++
actions/setup/js/push_repo_memory.cjs | 49 ++-----
actions/setup/js/push_repo_memory.test.cjs | 30 +++++
pkg/workflow/repo_memory.go | 27 ++++
32 files changed, 801 insertions(+), 37 deletions(-)
create mode 100644 actions/setup/js/memory_file_eligibility.cjs
create mode 100644 actions/setup/js/memory_file_eligibility.test.cjs
diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index fbfccbd40b2..f5050b89529 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -1341,6 +1341,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/agentic-token-audit.lock.yml b/.github/workflows/agentic-token-audit.lock.yml
index 0ab95de8406..bd9a67ecf68 100644
--- a/.github/workflows/agentic-token-audit.lock.yml
+++ b/.github/workflows/agentic-token-audit.lock.yml
@@ -1223,6 +1223,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/agentic-token-optimizer.lock.yml b/.github/workflows/agentic-token-optimizer.lock.yml
index 96503cf7793..e6f539d1992 100644
--- a/.github/workflows/agentic-token-optimizer.lock.yml
+++ b/.github/workflows/agentic-token-optimizer.lock.yml
@@ -1133,6 +1133,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index 4a58ff6df44..7e4b2910033 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -1386,6 +1386,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index aad10373caf..87b824fcf15 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -1277,6 +1277,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-centralization-optimizer.lock.yml b/.github/workflows/copilot-centralization-optimizer.lock.yml
index a80564410b2..9dc0c7c25af 100644
--- a/.github/workflows/copilot-centralization-optimizer.lock.yml
+++ b/.github/workflows/copilot-centralization-optimizer.lock.yml
@@ -1175,6 +1175,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index 36955c2bf4c..5951e40eb5d 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -1122,6 +1122,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 435520a7abf..3e960c249a4 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -1210,6 +1210,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 1a22d69ed54..8540ab05ef4 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -1153,6 +1153,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index 154bb035191..d0a7b94a9d2 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -1250,6 +1250,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
index b8d9f6f029a..4e593aa9f8f 100644
--- a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
+++ b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
@@ -1100,6 +1100,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: ".json .md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index d8c4ef86b11..e5a7c62bdfc 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -1372,6 +1372,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.txt"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-formal-spec-verifier.lock.yml b/.github/workflows/daily-formal-spec-verifier.lock.yml
index 6554727b10e..579b6ff1ed8 100644
--- a/.github/workflows/daily-formal-spec-verifier.lock.yml
+++ b/.github/workflows/daily-formal-spec-verifier.lock.yml
@@ -1175,6 +1175,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.md *.json"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-harness-experiment-proposer.lock.yml b/.github/workflows/daily-harness-experiment-proposer.lock.yml
index 93b5a7cffb8..59bda65e45f 100644
--- a/.github/workflows/daily-harness-experiment-proposer.lock.yml
+++ b/.github/workflows/daily-harness-experiment-proposer.lock.yml
@@ -1237,6 +1237,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index a7e096f28ed..e3dc3607f1c 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -1293,6 +1293,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
index 62632bd6aab..5ee5d85350a 100644
--- a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
+++ b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
@@ -1263,6 +1263,23 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[".json",".md"]'
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-storify.lock.yml b/.github/workflows/daily-storify.lock.yml
index b255a72146e..df2f7420edf 100644
--- a/.github/workflows/daily-storify.lock.yml
+++ b/.github/workflows/daily-storify.lock.yml
@@ -1238,6 +1238,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "storify/state.json storify/episodes.jsonl storify/loops.jsonl"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index bdba0e6a131..0b12096924c 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -1210,6 +1210,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.txt"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index 081c61d80ea..cea8afe7c9a 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -1924,6 +1924,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.md *.json"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index 275b23836e2..6d335cf4f30 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -1180,6 +1180,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl *.csv *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/eslint-refiner.lock.yml b/.github/workflows/eslint-refiner.lock.yml
index d5593ce2fac..85b2af2dc84 100644
--- a/.github/workflows/eslint-refiner.lock.yml
+++ b/.github/workflows/eslint-refiner.lock.yml
@@ -1204,6 +1204,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml
index 28300b5a77e..55a2552b706 100644
--- a/.github/workflows/metrics-collector.lock.yml
+++ b/.github/workflows/metrics-collector.lock.yml
@@ -1222,6 +1222,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "metrics/**"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index 41c61941f6e..5fbb72f1774 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -1571,6 +1571,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index 31197773c57..031b03ca149 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -1129,6 +1129,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "security-compliance-*/**"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 559ff778930..8eeace09d14 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -1258,6 +1258,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.jsonl"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml
index 81df726a952..e94206bbaed 100644
--- a/.github/workflows/smoke-ci.lock.yml
+++ b/.github/workflows/smoke-ci.lock.yml
@@ -1394,6 +1394,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index 01916c73d90..fb38bdaa362 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -1215,6 +1215,24 @@ jobs:
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
+ - name: Filter repo-memory files (default)
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ MEMORY_DIR: /tmp/gh-aw/repo-memory/default
+ ALLOWED_EXTENSIONS: '[]'
+ FILE_GLOB_FILTER: "*.json *.md"
+ with:
+ script: |
+ const path = require('path');
+ const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
+ const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ const memoryDir = process.env.MEMORY_DIR || '';
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/actions/setup/js/memory_file_eligibility.cjs b/actions/setup/js/memory_file_eligibility.cjs
new file mode 100644
index 00000000000..82d01791717
--- /dev/null
+++ b/actions/setup/js/memory_file_eligibility.cjs
@@ -0,0 +1,121 @@
+// @ts-check
+///
+
+const fs = require("fs");
+const path = require("path");
+
+const { globPatternToRegex } = require("./glob_pattern_helpers.cjs");
+
+/**
+ * Compile a space-separated FILE_GLOB_FILTER string into an array of RegExp patterns.
+ * Slashless patterns (e.g. "*.json") are matched at the root of a single memory
+ * subfolder (depth 1 only). Patterns that already contain "/" are matched against
+ * the full relative path unchanged.
+ *
+ * @param {string} fileGlobFilter - Space-separated glob patterns (may be empty)
+ * @returns {{ patternStrs: string[], compiledPatterns: RegExp[] }}
+ */
+function compileFileGlobPatterns(fileGlobFilter) {
+ if (!fileGlobFilter) {
+ return { patternStrs: [], compiledPatterns: [] };
+ }
+ const patternStrs = fileGlobFilter.trim().split(/\s+/).filter(Boolean);
+ const compiledPatterns = patternStrs.map(pattern => globPatternToRegex(pattern, { matchSubfolderRoot: !pattern.includes("/") }));
+ return { patternStrs, compiledPatterns };
+}
+
+/**
+ * Determine whether a relative file path is eligible for persistence, given a set
+ * of allowed extensions and compiled glob patterns. Both allowed-extensions and
+ * file-glob act as persistence filters: files that do not pass are ignored (never
+ * uploaded, validated, counted, or pushed) rather than causing a hard failure.
+ *
+ * @param {string} relativeFilePath - File path relative to the memory directory root
+ * @param {string[]} allowedExtensions - Allowed extensions (e.g. [".json"]); empty means allow all
+ * @param {RegExp[]} compiledPatterns - Compiled glob patterns; empty means allow all
+ * @returns {{ eligible: boolean, reason?: string }}
+ */
+function isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns) {
+ const normalizedRelPath = relativeFilePath.replace(/\\/g, "/");
+
+ if (allowedExtensions.length > 0) {
+ const ext = path.extname(relativeFilePath).toLowerCase();
+ const allowed = allowedExtensions.map(e => e.trim().toLowerCase());
+ if (!allowed.includes(ext)) {
+ return { eligible: false, reason: `disallowed extension "${ext || "(none)"}"` };
+ }
+ }
+
+ if (compiledPatterns.length > 0) {
+ if (!compiledPatterns.some(pattern => pattern.test(normalizedRelPath))) {
+ return { eligible: false, reason: "no pattern matched" };
+ }
+ }
+
+ return { eligible: true };
+}
+
+/**
+ * Recursively scan a memory directory and delete any file that is not eligible
+ * for persistence per the allowed-extensions and file-glob filters. Deleted files
+ * are logged (not treated as errors) so that downstream validation, artifact
+ * upload, and push steps only ever see the same effective file set.
+ *
+ * @param {string} memoryDir - Path to the memory directory to filter in place
+ * @param {string[]} allowedExtensions - Allowed extensions (e.g. [".json"]); empty means allow all
+ * @param {string} fileGlobFilter - Space-separated glob patterns; empty means allow all
+ * @param {{ info: (message: string) => void }} core - Actions core module
+ * @returns {{ kept: string[], removed: Array<{ path: string, reason: string }> }}
+ */
+function filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core) {
+ /** @type {string[]} */
+ const kept = [];
+ /** @type {Array<{ path: string, reason: string }>} */
+ const removed = [];
+
+ if (!fs.existsSync(memoryDir)) {
+ return { kept, removed };
+ }
+
+ const { compiledPatterns } = compileFileGlobPatterns(fileGlobFilter);
+
+ /**
+ * @param {string} dirPath
+ * @param {string} relativePath
+ */
+ const scanDirectory = (dirPath, relativePath = "") => {
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = path.join(dirPath, entry.name);
+ const relativeFilePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
+
+ if (entry.isDirectory()) {
+ if (entry.name === ".git") continue;
+ scanDirectory(fullPath, relativeFilePath);
+ continue;
+ }
+ if (!entry.isFile()) continue;
+
+ const normalizedRelPath = relativeFilePath.replace(/\\/g, "/");
+ const result = isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns);
+ if (!result.eligible) {
+ core.info(` [ignore] ${normalizedRelPath} — ${result.reason}`);
+ removed.push({ path: normalizedRelPath, reason: result.reason || "ineligible" });
+ fs.rmSync(fullPath, { force: true });
+ continue;
+ }
+ kept.push(normalizedRelPath);
+ }
+ };
+
+ scanDirectory(memoryDir);
+
+ if (removed.length > 0) {
+ core.info(`Ignored ${removed.length} ineligible file(s) before validation/upload:`);
+ removed.forEach(f => core.info(` - ${f.path} (${f.reason})`));
+ }
+
+ return { kept, removed };
+}
+
+module.exports = { compileFileGlobPatterns, isMemoryFileEligible, filterIneligibleMemoryFiles };
diff --git a/actions/setup/js/memory_file_eligibility.test.cjs b/actions/setup/js/memory_file_eligibility.test.cjs
new file mode 100644
index 00000000000..4ac2c4f8abb
--- /dev/null
+++ b/actions/setup/js/memory_file_eligibility.test.cjs
@@ -0,0 +1,126 @@
+import { describe, it, expect, afterEach } from "vitest";
+import fs from "fs";
+import path from "path";
+import os from "os";
+import { compileFileGlobPatterns, isMemoryFileEligible, filterIneligibleMemoryFiles } from "./memory_file_eligibility.cjs";
+
+describe("memory_file_eligibility.cjs", () => {
+ describe("isMemoryFileEligible", () => {
+ it("allows all files when no extensions or patterns are configured", () => {
+ expect(isMemoryFileEligible("notes.json", [], [])).toEqual({ eligible: true });
+ expect(isMemoryFileEligible("notes.json.new", [], [])).toEqual({ eligible: true });
+ });
+
+ it("rejects files with disallowed extensions", () => {
+ const result = isMemoryFileEligible("notes.json.new", [".json"], []);
+ expect(result.eligible).toBe(false);
+ expect(result.reason).toContain("disallowed extension");
+ expect(result.reason).toContain(".new");
+ });
+
+ it("accepts files with an allowed extension", () => {
+ expect(isMemoryFileEligible("notes.json", [".json"], [])).toEqual({ eligible: true });
+ });
+
+ it("is case-insensitive and trims whitespace on allowed extensions", () => {
+ expect(isMemoryFileEligible("notes.JSON", [" .JSON "], [])).toEqual({ eligible: true });
+ });
+
+ it("rejects files that do not match any glob pattern", () => {
+ const { compiledPatterns } = compileFileGlobPatterns("*.md");
+ const result = isMemoryFileEligible("notes.json", [], compiledPatterns);
+ expect(result.eligible).toBe(false);
+ expect(result.reason).toBe("no pattern matched");
+ });
+
+ it("requires both extension and glob filters to pass when both are configured", () => {
+ const { compiledPatterns } = compileFileGlobPatterns("sub/*.json");
+ // Matches glob but not the allowed extension list -> rejected on extension first
+ expect(isMemoryFileEligible("sub/notes.json.new", [".json"], compiledPatterns).eligible).toBe(false);
+ // Passes both filters
+ expect(isMemoryFileEligible("sub/notes.json", [".json"], compiledPatterns).eligible).toBe(true);
+ });
+ });
+
+ describe("compileFileGlobPatterns", () => {
+ it("returns empty arrays for an empty filter", () => {
+ expect(compileFileGlobPatterns("")).toEqual({ patternStrs: [], compiledPatterns: [] });
+ });
+
+ it("compiles space-separated patterns", () => {
+ const { patternStrs, compiledPatterns } = compileFileGlobPatterns("*.json *.md");
+ expect(patternStrs).toEqual(["*.json", "*.md"]);
+ expect(compiledPatterns).toHaveLength(2);
+ });
+ });
+
+ describe("filterIneligibleMemoryFiles", () => {
+ let tmpDir;
+ const mockCore = { info: () => {} };
+
+ afterEach(() => {
+ if (tmpDir) {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ tmpDir = undefined;
+ }
+ });
+
+ it("removes disallowed files and keeps allowed ones (regression: notes.json.new)", () => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-memory-filter-"));
+ fs.writeFileSync(path.join(tmpDir, "notes.json"), "{}");
+ fs.writeFileSync(path.join(tmpDir, "notes.json.new"), "");
+
+ const result = filterIneligibleMemoryFiles(tmpDir, [".json"], "", mockCore);
+
+ expect(result.kept).toEqual(["notes.json"]);
+ expect(result.removed).toEqual([{ path: "notes.json.new", reason: 'disallowed extension ".new"' }]);
+ expect(fs.existsSync(path.join(tmpDir, "notes.json"))).toBe(true);
+ expect(fs.existsSync(path.join(tmpDir, "notes.json.new"))).toBe(false);
+ });
+
+ it("is a no-op success when no files are eligible", () => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-memory-filter-"));
+ fs.writeFileSync(path.join(tmpDir, "notes.json.new"), "");
+
+ const result = filterIneligibleMemoryFiles(tmpDir, [".json"], "", mockCore);
+
+ expect(result.kept).toEqual([]);
+ expect(result.removed).toHaveLength(1);
+ expect(fs.existsSync(path.join(tmpDir, "notes.json.new"))).toBe(false);
+ });
+
+ it("interacts correctly with file-glob filtering in addition to allowed extensions", () => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-memory-filter-"));
+ fs.mkdirSync(path.join(tmpDir, "sub"));
+ fs.writeFileSync(path.join(tmpDir, "sub", "keep.json"), "{}");
+ fs.writeFileSync(path.join(tmpDir, "sub", "skip-glob.json"), "{}");
+ fs.writeFileSync(path.join(tmpDir, "sub", "skip-ext.md"), "text");
+
+ const result = filterIneligibleMemoryFiles(tmpDir, [".json"], "sub/keep.json", mockCore);
+
+ expect(result.kept).toEqual(["sub/keep.json"]);
+ expect(result.removed.map(f => f.path).sort()).toEqual(["sub/skip-ext.md", "sub/skip-glob.json"]);
+ });
+
+ it("handles nested directories, skipping .git", () => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-memory-filter-"));
+ fs.mkdirSync(path.join(tmpDir, "sub"));
+ fs.mkdirSync(path.join(tmpDir, ".git"));
+ fs.writeFileSync(path.join(tmpDir, "sub", "data.json"), "{}");
+ fs.writeFileSync(path.join(tmpDir, "sub", "data.bin"), "x");
+ fs.writeFileSync(path.join(tmpDir, ".git", "HEAD"), "ref: refs/heads/main");
+
+ const result = filterIneligibleMemoryFiles(tmpDir, [".json"], "", mockCore);
+
+ expect(result.kept).toEqual(["sub/data.json"]);
+ expect(result.removed).toEqual([{ path: "sub/data.bin", reason: 'disallowed extension ".bin"' }]);
+ // .git contents must be left untouched
+ expect(fs.existsSync(path.join(tmpDir, ".git", "HEAD"))).toBe(true);
+ });
+
+ it("returns empty results when the memory directory does not exist", () => {
+ const result = filterIneligibleMemoryFiles(path.join(os.tmpdir(), "gh-aw-memory-filter-does-not-exist"), [".json"], "", mockCore);
+ expect(result).toEqual({ kept: [], removed: [] });
+ });
+ });
+});
diff --git a/actions/setup/js/push_repo_memory.cjs b/actions/setup/js/push_repo_memory.cjs
index b1297b11f38..c69bd11b9b0 100644
--- a/actions/setup/js/push_repo_memory.cjs
+++ b/actions/setup/js/push_repo_memory.cjs
@@ -5,11 +5,11 @@ const fs = require("fs");
const path = require("path");
const { getErrorMessage } = require("./error_helpers.cjs");
-const { globPatternToRegex } = require("./glob_pattern_helpers.cjs");
const { getGitAuthEnv } = require("./git_auth_helpers.cjs");
const { execGitSync } = require("./git_helpers.cjs");
const { getStagedPatchDiffSizeBytes } = require("./git_patch_utils.cjs");
const { formatJSONFiles, runCustomMemoryValidation } = require("./memory_custom_validation.cjs");
+const { compileFileGlobPatterns, isMemoryFileEligible } = require("./memory_file_eligibility.cjs");
const { parseAllowedRepos, validateRepo } = require("./repo_helpers.cjs");
const { pushSignedCommits } = require("./push_signed_commits.cjs");
@@ -316,16 +316,8 @@ async function main() {
let filteredOutFiles = [];
// Compile glob patterns once, outside the scan loop
- /** @type {string[]} */
- let patternStrs = [];
- /** @type {RegExp[]} */
- let compiledPatterns = [];
- if (fileGlobFilter) {
- patternStrs = fileGlobFilter.trim().split(/\s+/).filter(Boolean);
- // Slashless patterns (e.g. "*.json") are matched at the root of a single memory subfolder
- // (depth 1 only). Patterns that already contain "/" are matched against the full relative
- // path unchanged.
- compiledPatterns = patternStrs.map(pattern => globPatternToRegex(pattern, { matchSubfolderRoot: !pattern.includes("/") }));
+ const { patternStrs, compiledPatterns } = compileFileGlobPatterns(fileGlobFilter);
+ if (compiledPatterns.length > 0) {
core.info(`File glob filter enabled with ${patternStrs.length} pattern(s):`);
patternStrs.forEach((pat, idx) => {
core.info(` [${idx + 1}] "${pat}" -> regex: ${compiledPatterns[idx].source}`);
@@ -364,20 +356,14 @@ async function main() {
}
const normalizedRelPath = relativeFilePath.replace(/\\/g, "/");
- // Validate file name patterns if filter is set
- if (compiledPatterns.length > 0) {
- const matchResults = compiledPatterns.map((pattern, idx) => {
- const matches = pattern.test(normalizedRelPath);
- core.info(` [test] ${normalizedRelPath} pattern[${idx + 1}] "${patternStrs[idx]}" -> ${matches ? "✓ match" : "✗ no match"}`);
- return matches;
- });
-
- if (!matchResults.some(m => m)) {
- core.info(` [skip] ${normalizedRelPath} (${stats.size} bytes) — no pattern matched`);
- filteredOutFiles.push({ path: normalizedRelPath, reason: "no pattern matched" });
- // Skip this file instead of failing - it may be from a previous run with different patterns
- return;
- }
+ // Allowed extensions and file-glob are persistence filters: files that do not
+ // pass are logged and ignored (never uploaded, validated, counted toward
+ // max-file-count/size/patch-size, or pushed) rather than causing a hard failure.
+ const eligibility = isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns);
+ if (!eligibility.eligible) {
+ core.info(` [skip] ${normalizedRelPath} (${stats.size} bytes) — ${eligibility.reason}`);
+ filteredOutFiles.push({ path: normalizedRelPath, reason: eligibility.reason || "ineligible" });
+ continue;
}
// Validate file size
@@ -419,18 +405,7 @@ async function main() {
}
if (filesToCopy.length === 0) {
- core.info("No files to copy from artifact");
- return;
- }
-
- // Validate file types before copying
- const { validateMemoryFiles } = require("./validate_memory_files.cjs");
- const validation = validateMemoryFiles(sourceMemoryPath, "repo", allowedExtensions);
- if (!validation.valid) {
- const errorMessage = `File type validation failed: Found ${validation.invalidFiles.length} file(s) with invalid extensions. Only ${allowedExtensions.join(", ")} are allowed. Invalid files: ${validation.invalidFiles.join(", ")}`;
- core.setOutput("validation_failed", "true");
- core.setOutput("validation_error", errorMessage);
- core.setFailed(errorMessage);
+ core.info("No eligible files to copy from artifact (all files were filtered out or none present)");
return;
}
diff --git a/actions/setup/js/push_repo_memory.test.cjs b/actions/setup/js/push_repo_memory.test.cjs
index 950616a4a3e..45bf538b6ab 100644
--- a/actions/setup/js/push_repo_memory.test.cjs
+++ b/actions/setup/js/push_repo_memory.test.cjs
@@ -1607,6 +1607,36 @@ describe("push_repo_memory.cjs - changed-file limit checks", () => {
});
});
+describe("push_repo_memory.cjs - allowed-extensions persistence filter (regression: notes.json.new)", () => {
+ it("filters ineligible files before validation/upload instead of hard-failing on them (source check)", () => {
+ const nodeFs = require("fs");
+ const nodePath = require("path");
+ const scriptPath = nodePath.join(import.meta.dirname, "push_repo_memory.cjs");
+ const scriptContent = nodeFs.readFileSync(scriptPath, "utf8");
+
+ // Allowed-extensions and file-glob must both be applied as persistence filters
+ // via the shared eligibility helper, before size/count/patch/custom validation.
+ expect(scriptContent).toContain("isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns)");
+ expect(scriptContent).toContain("require(\"./memory_file_eligibility.cjs\")");
+
+ // The old behavior — validating the whole source directory after scanning and
+ // hard-failing the job (e.g. for a leftover "notes.json.new" file) — must be gone.
+ // Disallowed files are filtered out during the scan (never copied/pushed) instead.
+ expect(scriptContent).not.toContain('validateMemoryFiles(sourceMemoryPath, "repo", allowedExtensions)');
+ });
+
+ it("does not fail with no eligible files, and reports a no-op instead (source check)", () => {
+ const nodeFs = require("fs");
+ const nodePath = require("path");
+ const scriptPath = nodePath.join(import.meta.dirname, "push_repo_memory.cjs");
+ const scriptContent = nodeFs.readFileSync(scriptPath, "utf8");
+
+ expect(scriptContent).toContain("if (filesToCopy.length === 0)");
+ expect(scriptContent).toContain("No eligible files to copy from artifact");
+ });
+});
+
+
// ──────────────────────────────────────────────────────────────────────────────
// Signed-commit push tests
// Verifies that push_repo_memory delegates to pushSignedCommits (GraphQL-based
diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go
index dcb2172743b..f99c383fdb7 100644
--- a/pkg/workflow/repo_memory.go
+++ b/pkg/workflow/repo_memory.go
@@ -380,6 +380,33 @@ func generateRepoMemoryArtifactUpload(builder *strings.Builder, data *WorkflowDa
fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
builder.WriteString(" run: bash \"${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh\"\n")
+ // Step: Filter out files that are ineligible for persistence (disallowed
+ // extensions or non-matching file-glob patterns) before validation and
+ // upload. Allowed-extensions and file-glob are persistence filters: ineligible
+ // files are logged and removed here so that custom validation, the uploaded
+ // artifact, and the downstream push all see the same effective file set.
+ if len(memory.AllowedExtensions) > 0 || len(memory.FileGlob) > 0 {
+ allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
+ fmt.Fprintf(builder, " - name: Filter %s files (%s)\n", memoryLabel, memory.ID)
+ builder.WriteString(" if: always()\n")
+ fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
+ builder.WriteString(" env:\n")
+ fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
+ fmt.Fprintf(builder, " ALLOWED_EXTENSIONS: '%s'\n", allowedExtsJSON)
+ if len(memory.FileGlob) > 0 {
+ fmt.Fprintf(builder, " FILE_GLOB_FILTER: \"%s\"\n", strings.Join(memory.FileGlob, " "))
+ }
+ builder.WriteString(" with:\n")
+ builder.WriteString(" script: |\n")
+ builder.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n")
+ builder.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n")
+ builder.WriteString(" const { filterIneligibleMemoryFiles } = require('${{ runner.temp }}/gh-aw/actions/memory_file_eligibility.cjs');\n")
+ builder.WriteString(" const memoryDir = process.env.MEMORY_DIR || '';\n")
+ builder.WriteString(" const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');\n")
+ builder.WriteString(" const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';\n")
+ builder.WriteString(" filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);\n")
+ }
+
validationStepID := repoMemoryValidationStepID(memory.ID)
if memory.Validation != nil {
fmt.Fprintf(builder, " - name: Validate %s domain content (%s)\n", memoryLabel, memory.ID)
From d512277976e1dec82b85ca4e7c76cf26c2afde0f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 05:17:16 +0000
Subject: [PATCH 3/7] Update; rm -rf /
Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
---
actions/setup/js/validate_memory_files.cjs | 89 -----
.../setup/js/validate_memory_files.test.cjs | 312 ------------------
2 files changed, 401 deletions(-)
delete mode 100644 actions/setup/js/validate_memory_files.cjs
delete mode 100644 actions/setup/js/validate_memory_files.test.cjs
diff --git a/actions/setup/js/validate_memory_files.cjs b/actions/setup/js/validate_memory_files.cjs
deleted file mode 100644
index 2fe8bf15b76..00000000000
--- a/actions/setup/js/validate_memory_files.cjs
+++ /dev/null
@@ -1,89 +0,0 @@
-// @ts-check
-///
-
-const fs = require("fs");
-const path = require("path");
-const { getErrorMessage } = require("./error_helpers.cjs");
-
-/**
- * @typedef {Object} ValidationResult
- * @property {boolean} valid - Whether all files passed validation
- * @property {string[]} invalidFiles - List of files with invalid extensions
- */
-
-/**
- * Validate that all files in a memory directory have allowed file extensions
- * If allowedExtensions is empty or not provided, all file extensions are allowed
- *
- * @param {string} memoryDir - Path to the memory directory to validate
- * @param {string} [memoryType="cache"] - Type of memory ("cache" or "repo") for error messages
- * @param {string[]} [allowedExtensions] - Optional custom list of allowed extensions (empty array or undefined means allow all files)
- * @param {{ info: (message: string) => void, error: (message: string) => void }} [coreModule] - Actions core module
- * @returns {ValidationResult} Validation result with list of invalid files
- */
-function validateMemoryFiles(memoryDir, memoryType = "cache", allowedExtensions, coreModule = core) {
- if (!allowedExtensions?.length) {
- coreModule.info(`All file extensions are allowed in ${memoryType}-memory directory`);
- return { valid: true, invalidFiles: [] };
- }
-
- if (!fs.existsSync(memoryDir)) {
- coreModule.info(`Memory directory does not exist: ${memoryDir}`);
- return { valid: true, invalidFiles: [] };
- }
-
- const extensions = new Set(allowedExtensions.map(ext => ext.trim().toLowerCase()));
- /** @type {string[]} */
- const invalidFiles = [];
-
- /**
- * Recursively scan directory for files
- * @param {string} dirPath - Directory to scan
- * @param {string} [relativePath=""] - Relative path from memory directory
- */
- const scanDirectory = (dirPath, relativePath = "") => {
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
-
- for (const entry of entries) {
- const fullPath = path.join(dirPath, entry.name);
- const relativeFilePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
-
- if (entry.isDirectory()) {
- // Skip .git directory — it is git metadata used for integrity branching
- // and contains files with no extension (e.g. HEAD, ORIG_HEAD, packed-refs).
- if (entry.name === ".git") continue;
- scanDirectory(fullPath, relativeFilePath);
- } else if (entry.isFile()) {
- const ext = path.extname(entry.name).toLowerCase();
- if (!extensions.has(ext)) {
- invalidFiles.push(relativeFilePath);
- }
- }
- }
- };
-
- try {
- scanDirectory(memoryDir);
- } catch (error) {
- const message = getErrorMessage(error);
- coreModule.error(`Failed to scan ${memoryType}-memory directory: ${message}`);
- return { valid: false, invalidFiles: [] };
- }
-
- if (invalidFiles.length > 0) {
- coreModule.error(`Found ${invalidFiles.length} file(s) with invalid extensions in ${memoryType}-memory:`);
- for (const file of invalidFiles) {
- const ext = path.extname(file).toLowerCase() || "(no extension)";
- coreModule.error(` - ${file} (extension: ${ext})`);
- }
- coreModule.error(`Allowed extensions: ${[...extensions].join(", ")}`);
- return { valid: false, invalidFiles };
- }
-
- coreModule.info(`All files in ${memoryType}-memory directory have valid extensions`);
- return { valid: true, invalidFiles: [] };
-}
-
-module.exports = {
- validateMemoryFiles,
-};
diff --git a/actions/setup/js/validate_memory_files.test.cjs b/actions/setup/js/validate_memory_files.test.cjs
deleted file mode 100644
index 4eda600f405..00000000000
--- a/actions/setup/js/validate_memory_files.test.cjs
+++ /dev/null
@@ -1,312 +0,0 @@
-// @ts-check
-
-import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
-import fs from "fs";
-import path from "path";
-import os from "os";
-import { createRequire } from "module";
-
-const req = createRequire(import.meta.url);
-const { validateMemoryFiles } = req("./validate_memory_files.cjs");
-
-// Mock core globally with vi.fn() so we can assert on calls
-global.core = {
- info: vi.fn(),
- error: vi.fn(),
- warning: vi.fn(),
- debug: vi.fn(),
-};
-
-describe("validateMemoryFiles", () => {
- let tempDir = "";
-
- beforeEach(() => {
- // Create a temporary directory for testing
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "validate-memory-test-"));
- vi.resetAllMocks();
- });
-
- afterEach(() => {
- // Clean up temporary directory
- if (tempDir && fs.existsSync(tempDir)) {
- fs.rmSync(tempDir, { recursive: true, force: true });
- }
- vi.restoreAllMocks();
- });
-
- it("returns valid for empty directory", () => {
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("calls core.info when allowedExtensions is not provided", () => {
- validateMemoryFiles(tempDir, "cache");
- expect(global.core.info).toHaveBeenCalledWith(expect.stringContaining("All file extensions are allowed in cache-memory directory"));
- });
-
- it("calls core.info when allowedExtensions is empty array", () => {
- validateMemoryFiles(tempDir, "repo", []);
- expect(global.core.info).toHaveBeenCalledWith(expect.stringContaining("All file extensions are allowed in repo-memory directory"));
- });
-
- it("returns valid for non-existent directory", () => {
- const nonExistentDir = path.join(tempDir, "does-not-exist");
- const result = validateMemoryFiles(nonExistentDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("calls core.info when directory does not exist", () => {
- const nonExistentDir = path.join(tempDir, "does-not-exist");
- validateMemoryFiles(nonExistentDir, "repo", [".json"]);
- expect(global.core.info).toHaveBeenCalledWith(expect.stringContaining(`Memory directory does not exist: ${nonExistentDir}`));
- });
-
- it("accepts .json files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.json"), '{"test": true}');
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .jsonl files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.jsonl"), '{"line": 1}\n{"line": 2}');
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .txt files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "notes.txt"), "Some notes");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .md files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "README.md"), "# Title");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .csv files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.csv"), "col1,col2\nval1,val2");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts multiple valid files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.json"), "{}");
- fs.writeFileSync(path.join(tempDir, "notes.txt"), "notes");
- fs.writeFileSync(path.join(tempDir, "README.md"), "# Title");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .log files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "app.log"), "log entry");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // Now accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .yaml files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "config.yaml"), "key: value");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // Now accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts .xml files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.xml"), "");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // Now accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts files without extension by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "noext"), "content");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // Now accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts all files by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "app.log"), "log");
- fs.writeFileSync(path.join(tempDir, "config.yaml"), "yaml");
- fs.writeFileSync(path.join(tempDir, "valid.json"), "{}");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // All files accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("validates files in subdirectories by default (allow all)", () => {
- const subdir = path.join(tempDir, "subdir");
- fs.mkdirSync(subdir);
- fs.writeFileSync(path.join(subdir, "valid.json"), "{}");
- fs.writeFileSync(path.join(subdir, "invalid.log"), "log");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // All files accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("validates files in deeply nested directories by default (allow all)", () => {
- const level1 = path.join(tempDir, "level1");
- const level2 = path.join(level1, "level2");
- const level3 = path.join(level2, "level3");
- fs.mkdirSync(level1);
- fs.mkdirSync(level2);
- fs.mkdirSync(level3);
- fs.writeFileSync(path.join(level3, "deep.json"), "{}");
- fs.writeFileSync(path.join(level3, "invalid.bin"), "binary");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // All files accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("is case-insensitive for extensions by default (allow all)", () => {
- fs.writeFileSync(path.join(tempDir, "data.JSON"), "{}");
- fs.writeFileSync(path.join(tempDir, "notes.TXT"), "text");
- fs.writeFileSync(path.join(tempDir, "README.MD"), "# Title");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("handles all files in subdirectories by default (allow all)", () => {
- const subdir1 = path.join(tempDir, "valid-files");
- const subdir2 = path.join(tempDir, "invalid-files");
- fs.mkdirSync(subdir1);
- fs.mkdirSync(subdir2);
- fs.writeFileSync(path.join(subdir1, "data.json"), "{}");
- fs.writeFileSync(path.join(subdir1, "notes.txt"), "text");
- fs.writeFileSync(path.join(subdir2, "app.log"), "log");
- fs.writeFileSync(path.join(subdir2, "config.ini"), "ini");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // All files accepted when no restrictions
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("accepts custom allowed extensions", () => {
- fs.writeFileSync(path.join(tempDir, "config.yaml"), "key: value");
- fs.writeFileSync(path.join(tempDir, "data.xml"), "");
- const customExts = [".yaml", ".xml"];
- const result = validateMemoryFiles(tempDir, "cache", customExts);
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("rejects files not in custom allowed extensions", () => {
- fs.writeFileSync(path.join(tempDir, "data.json"), "{}");
- const customExts = [".yaml", ".xml"];
- const result = validateMemoryFiles(tempDir, "cache", customExts);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toEqual(["data.json"]);
- });
-
- it("allows all files when custom array is empty", () => {
- fs.writeFileSync(path.join(tempDir, "data.json"), "{}");
- fs.writeFileSync(path.join(tempDir, "notes.txt"), "text");
- fs.writeFileSync(path.join(tempDir, "app.log"), "log");
- fs.writeFileSync(path.join(tempDir, "config.yaml"), "key: value");
- const result = validateMemoryFiles(tempDir, "cache", []);
- expect(result.valid).toBe(true); // Empty array means allow all
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("allows all files when allowedExtensions is undefined", () => {
- fs.writeFileSync(path.join(tempDir, "data.json"), "{}");
- fs.writeFileSync(path.join(tempDir, "app.log"), "log");
- fs.writeFileSync(path.join(tempDir, "config.yaml"), "key: value");
- const result = validateMemoryFiles(tempDir, "cache");
- expect(result.valid).toBe(true); // undefined means allow all
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("skips .git directory during scan", () => {
- const gitDir = path.join(tempDir, ".git");
- fs.mkdirSync(gitDir);
- // These would fail validation (no extension) but should be skipped
- fs.writeFileSync(path.join(gitDir, "HEAD"), "ref: refs/heads/main");
- fs.writeFileSync(path.join(gitDir, "packed-refs"), "");
- fs.writeFileSync(path.join(tempDir, "valid.json"), "{}");
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("rejects invalid files in subdirectories with custom extensions", () => {
- const subdir = path.join(tempDir, "subdir");
- fs.mkdirSync(subdir);
- fs.writeFileSync(path.join(subdir, "valid.json"), "{}");
- fs.writeFileSync(path.join(subdir, "invalid.log"), "log");
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toContain(path.join("subdir", "invalid.log"));
- });
-
- it("validates files with no extension against custom extensions", () => {
- fs.writeFileSync(path.join(tempDir, "noext"), "content");
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toEqual(["noext"]);
- });
-
- it("trims and normalizes extension casing in custom allowed list", () => {
- fs.writeFileSync(path.join(tempDir, "data.JSON"), "{}");
- const result = validateMemoryFiles(tempDir, "cache", [" .json "]);
- expect(result.valid).toBe(true);
- expect(result.invalidFiles).toEqual([]);
- });
-
- it("uses 'cache' as the default memoryType", () => {
- const result = validateMemoryFiles(tempDir);
- expect(result.valid).toBe(true);
- expect(global.core.info).toHaveBeenCalledWith(expect.stringContaining("cache-memory"));
- });
-
- it("calls core.error with details when files fail custom extension validation", () => {
- fs.writeFileSync(path.join(tempDir, "bad.log"), "log");
- const result = validateMemoryFiles(tempDir, "repo", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toContain("bad.log");
- expect(global.core.error).toHaveBeenCalledWith(expect.stringContaining("Found 1 file(s) with invalid extensions in repo-memory:"));
- expect(global.core.error).toHaveBeenCalledWith(expect.stringContaining("bad.log (extension: .log)"));
- expect(global.core.error).toHaveBeenCalledWith(expect.stringContaining("Allowed extensions: .json"));
- });
-
- it("reports files with no extension as '(no extension)' in error output", () => {
- fs.writeFileSync(path.join(tempDir, "noext"), "content");
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toContain("noext");
- expect(global.core.error).toHaveBeenCalledWith(expect.stringContaining("noext (extension: (no extension))"));
- });
-
- it("detects invalid files in deeply nested directories with custom extensions", () => {
- const level1 = path.join(tempDir, "level1");
- const level2 = path.join(level1, "level2");
- fs.mkdirSync(level1);
- fs.mkdirSync(level2);
- fs.writeFileSync(path.join(level2, "valid.json"), "{}");
- fs.writeFileSync(path.join(level2, "invalid.bin"), "binary");
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toContain(path.join("level1", "level2", "invalid.bin"));
- expect(result.invalidFiles).not.toContain(path.join("level1", "level2", "valid.json"));
- });
-
- it("returns valid=false and empty invalidFiles when directory scan throws", () => {
- vi.spyOn(fs, "readdirSync").mockImplementationOnce(() => {
- throw new Error("Permission denied");
- });
- const result = validateMemoryFiles(tempDir, "cache", [".json"]);
- expect(result.valid).toBe(false);
- expect(result.invalidFiles).toEqual([]);
- expect(global.core.error).toHaveBeenCalledWith(expect.stringContaining("Failed to scan cache-memory directory: Permission denied"));
- });
-});
From 872b7d2e14f5ecba3b5bac893d29cf94a66218af Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 05:34:34 +0000
Subject: [PATCH 4/7] Update; rm -rf /
Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
---
.../agent-performance-analyzer.lock.yml | 7 +-
.../workflows/agentic-token-audit.lock.yml | 7 +-
.../agentic-token-optimizer.lock.yml | 7 +-
.github/workflows/audit-workflows.lock.yml | 7 +-
.../workflows/copilot-agent-analysis.lock.yml | 7 +-
.../copilot-centralization-optimizer.lock.yml | 7 +-
.../copilot-cli-deep-research.lock.yml | 7 +-
.../copilot-pr-nlp-analysis.lock.yml | 7 +-
.../copilot-pr-prompt-analysis.lock.yml | 7 +-
.../copilot-session-insights.lock.yml | 7 +-
...daily-awf-spec-compiler-surfacing.lock.yml | 7 +-
.../workflows/daily-cli-performance.lock.yml | 7 +-
.../daily-formal-spec-verifier.lock.yml | 7 +-
...daily-harness-experiment-proposer.lock.yml | 7 +-
.github/workflows/daily-news.lock.yml | 7 +-
.../daily-safeoutputs-git-simulator.lock.yml | 7 +-
.github/workflows/daily-storify.lock.yml | 7 +-
.../daily-testify-uber-super-expert.lock.yml | 7 +-
.github/workflows/deep-report.lock.yml | 7 +-
.github/workflows/delight.lock.yml | 7 +-
.github/workflows/eslint-refiner.lock.yml | 7 +-
.github/workflows/metrics-collector.lock.yml | 7 +-
.github/workflows/pr-triage-agent.lock.yml | 7 +-
.../workflows/security-compliance.lock.yml | 7 +-
.github/workflows/sergo.lock.yml | 7 +-
.github/workflows/smoke-ci.lock.yml | 7 +-
.../workflow-health-manager.lock.yml | 7 +-
actions/setup/js/memory_file_eligibility.cjs | 16 +-
actions/setup/js/push_repo_memory.test.cjs | 3 +-
actions/setup/js/validate_memory_step.cjs | 12 +-
.../setup/js/validate_memory_step.test.cjs | 20 +++
pkg/workflow/drive_memory_test.go | 41 +++++
pkg/workflow/repo_memory.go | 167 +++++++++---------
33 files changed, 225 insertions(+), 223 deletions(-)
diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index f5050b89529..9573b94f971 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -1354,11 +1354,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/agentic-token-audit.lock.yml b/.github/workflows/agentic-token-audit.lock.yml
index bd9a67ecf68..fd062bb83fa 100644
--- a/.github/workflows/agentic-token-audit.lock.yml
+++ b/.github/workflows/agentic-token-audit.lock.yml
@@ -1236,11 +1236,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/agentic-token-optimizer.lock.yml b/.github/workflows/agentic-token-optimizer.lock.yml
index e6f539d1992..a3a35d7ab55 100644
--- a/.github/workflows/agentic-token-optimizer.lock.yml
+++ b/.github/workflows/agentic-token-optimizer.lock.yml
@@ -1146,11 +1146,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index 7e4b2910033..b93efc8ebb2 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -1399,11 +1399,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index 87b824fcf15..62c34055844 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -1290,11 +1290,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-centralization-optimizer.lock.yml b/.github/workflows/copilot-centralization-optimizer.lock.yml
index 9dc0c7c25af..bc53a44ae56 100644
--- a/.github/workflows/copilot-centralization-optimizer.lock.yml
+++ b/.github/workflows/copilot-centralization-optimizer.lock.yml
@@ -1188,11 +1188,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index 5951e40eb5d..0d56fa405a5 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -1135,11 +1135,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 3e960c249a4..946100cd6a4 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -1223,11 +1223,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 8540ab05ef4..6ebf5b27c5d 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -1166,11 +1166,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index d0a7b94a9d2..d0d18cd069f 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -1263,11 +1263,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
index 4e593aa9f8f..1465ff4a485 100644
--- a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
+++ b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
@@ -1113,11 +1113,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index e5a7c62bdfc..addc561a8d6 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -1385,11 +1385,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-formal-spec-verifier.lock.yml b/.github/workflows/daily-formal-spec-verifier.lock.yml
index 579b6ff1ed8..8c98f1a5528 100644
--- a/.github/workflows/daily-formal-spec-verifier.lock.yml
+++ b/.github/workflows/daily-formal-spec-verifier.lock.yml
@@ -1188,11 +1188,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-harness-experiment-proposer.lock.yml b/.github/workflows/daily-harness-experiment-proposer.lock.yml
index 59bda65e45f..41225b4cdec 100644
--- a/.github/workflows/daily-harness-experiment-proposer.lock.yml
+++ b/.github/workflows/daily-harness-experiment-proposer.lock.yml
@@ -1250,11 +1250,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index e3dc3607f1c..71c80483c97 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -1306,11 +1306,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
index 5ee5d85350a..022127b08be 100644
--- a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
+++ b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
@@ -1275,11 +1275,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-storify.lock.yml b/.github/workflows/daily-storify.lock.yml
index df2f7420edf..b65ac1b1e5f 100644
--- a/.github/workflows/daily-storify.lock.yml
+++ b/.github/workflows/daily-storify.lock.yml
@@ -1251,11 +1251,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 0b12096924c..bc4a90d9b70 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -1223,11 +1223,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index cea8afe7c9a..99b662c4dc6 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -1937,11 +1937,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index 6d335cf4f30..d7386313d50 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -1193,11 +1193,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/eslint-refiner.lock.yml b/.github/workflows/eslint-refiner.lock.yml
index 85b2af2dc84..2f3a01b0e9c 100644
--- a/.github/workflows/eslint-refiner.lock.yml
+++ b/.github/workflows/eslint-refiner.lock.yml
@@ -1217,11 +1217,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml
index 55a2552b706..8c9d8349982 100644
--- a/.github/workflows/metrics-collector.lock.yml
+++ b/.github/workflows/metrics-collector.lock.yml
@@ -1235,11 +1235,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index 5fbb72f1774..373c90be337 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -1584,11 +1584,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index 031b03ca149..9601e415614 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -1142,11 +1142,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 8eeace09d14..00c129d7734 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -1271,11 +1271,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml
index e94206bbaed..76e55779c89 100644
--- a/.github/workflows/smoke-ci.lock.yml
+++ b/.github/workflows/smoke-ci.lock.yml
@@ -1407,11 +1407,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index fb38bdaa362..fbd0b27f971 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -1228,11 +1228,8 @@ jobs:
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs'));
setupGlobals(core, github, context, exec, io, getOctokit);
- const { filterIneligibleMemoryFiles } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
- const memoryDir = process.env.MEMORY_DIR || '';
- const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');
- const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';
- filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
+ await main();
- name: Upload repo-memory artifact (default)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
diff --git a/actions/setup/js/memory_file_eligibility.cjs b/actions/setup/js/memory_file_eligibility.cjs
index 82d01791717..f0c41a60093 100644
--- a/actions/setup/js/memory_file_eligibility.cjs
+++ b/actions/setup/js/memory_file_eligibility.cjs
@@ -118,4 +118,18 @@ function filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilte
return { kept, removed };
}
-module.exports = { compileFileGlobPatterns, isMemoryFileEligible, filterIneligibleMemoryFiles };
+/**
+ * Entry point used by the compiler's "Filter memory files" step (via
+ * generateGitHubScriptWithRequire). Reads MEMORY_DIR, ALLOWED_EXTENSIONS
+ * (JSON array), and FILE_GLOB_FILTER from the environment and filters the
+ * memory directory in place, relying on the `core` global set up by
+ * setup_globals.cjs.
+ */
+function main() {
+ const memoryDir = process.env.MEMORY_DIR || "";
+ const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || "[]");
+ const fileGlobFilter = process.env.FILE_GLOB_FILTER || "";
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+}
+
+module.exports = { compileFileGlobPatterns, isMemoryFileEligible, filterIneligibleMemoryFiles, main };
diff --git a/actions/setup/js/push_repo_memory.test.cjs b/actions/setup/js/push_repo_memory.test.cjs
index 45bf538b6ab..2961a28f575 100644
--- a/actions/setup/js/push_repo_memory.test.cjs
+++ b/actions/setup/js/push_repo_memory.test.cjs
@@ -1617,7 +1617,7 @@ describe("push_repo_memory.cjs - allowed-extensions persistence filter (regressi
// Allowed-extensions and file-glob must both be applied as persistence filters
// via the shared eligibility helper, before size/count/patch/custom validation.
expect(scriptContent).toContain("isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns)");
- expect(scriptContent).toContain("require(\"./memory_file_eligibility.cjs\")");
+ expect(scriptContent).toContain('require("./memory_file_eligibility.cjs")');
// The old behavior — validating the whole source directory after scanning and
// hard-failing the job (e.g. for a leftover "notes.json.new" file) — must be gone.
@@ -1636,7 +1636,6 @@ describe("push_repo_memory.cjs - allowed-extensions persistence filter (regressi
});
});
-
// ──────────────────────────────────────────────────────────────────────────────
// Signed-commit push tests
// Verifies that push_repo_memory delegates to pushSignedCommits (GraphQL-based
diff --git a/actions/setup/js/validate_memory_step.cjs b/actions/setup/js/validate_memory_step.cjs
index 7074201281b..df38199c91d 100644
--- a/actions/setup/js/validate_memory_step.cjs
+++ b/actions/setup/js/validate_memory_step.cjs
@@ -1,7 +1,7 @@
// @ts-check
const { formatJSONFiles, runCustomMemoryValidation, writeValidationMarker, clearValidationMarker } = require("./memory_custom_validation.cjs");
-const { validateMemoryFiles } = require("./validate_memory_files.cjs");
+const { filterIneligibleMemoryFiles } = require("./memory_file_eligibility.cjs");
/**
* @param {{ error: (message: string) => void, info: (message: string) => void, setFailed: (message: string) => void }} core
@@ -22,12 +22,12 @@ function validateMemoryStep(core, options) {
clearValidationMarker(options.kind, memoryId);
}
+ // Allowed-extensions (and file-glob, when present) are persistence filters, not hard
+ // failures: ineligible files are logged and removed here so that custom validation,
+ // artifact upload/save, and any downstream push all see the same effective file set.
+ // This mirrors the filtering applied for repo-memory before this step runs.
if (allowedExtensions.length > 0) {
- const result = validateMemoryFiles(memoryDir, options.kind, allowedExtensions, core);
- if (!result.valid) {
- core.setFailed(`Storage file type validation failed: Found ${result.invalidFiles.length} file(s) with invalid extensions. Only ${allowedExtensions.join(", ")} are allowed.`);
- failed = true;
- }
+ filterIneligibleMemoryFiles(memoryDir, allowedExtensions, process.env.FILE_GLOB_FILTER || "", core);
}
if (options.formatJSON) {
diff --git a/actions/setup/js/validate_memory_step.test.cjs b/actions/setup/js/validate_memory_step.test.cjs
index aedc3d188ef..2e6e804b2d0 100644
--- a/actions/setup/js/validate_memory_step.test.cjs
+++ b/actions/setup/js/validate_memory_step.test.cjs
@@ -48,4 +48,24 @@ describe("validateMemoryStep", () => {
expect(validateMemoryStep(core, { kind: "drive" })).toBe(true);
});
+
+ it("filters (never hard-fails) disallowed-extension files uniformly across memory kinds", () => {
+ delete process.env.VALIDATION_SCRIPT_B64;
+ fs.writeFileSync(path.join(tempDir, "notes.json"), "{}");
+ fs.writeFileSync(path.join(tempDir, "notes.json.new"), "ignored");
+ const messages = [];
+ const core = {
+ info: message => messages.push(message),
+ error: message => messages.push(`error: ${message}`),
+ setFailed: message => messages.push(`failed: ${message}`),
+ };
+
+ for (const kind of ["repo", "cache", "drive"]) {
+ expect(validateMemoryStep(core, { kind })).toBe(true);
+ }
+
+ expect(messages.some(m => m.startsWith("failed:"))).toBe(false);
+ expect(fs.existsSync(path.join(tempDir, "notes.json"))).toBe(true);
+ expect(fs.existsSync(path.join(tempDir, "notes.json.new"))).toBe(false);
+ });
});
diff --git a/pkg/workflow/drive_memory_test.go b/pkg/workflow/drive_memory_test.go
index 361a4b021fd..1d467ee1a46 100644
--- a/pkg/workflow/drive_memory_test.go
+++ b/pkg/workflow/drive_memory_test.go
@@ -262,6 +262,47 @@ func TestDriveMemoryPersistenceWithoutValidationUsesDefaultSuccessCondition(t *t
assert.NotContains(t, persist.String(), "if:")
}
+// TestDriveMemoryValidationConfigAndGeneratedSteps mirrors
+// TestCacheMemoryValidationConfigAndGeneratedSteps and
+// TestRepoMemoryValidationConfigAndGeneratedSteps to confirm the script-based
+// custom validation hook is wired uniformly for drive-memory too: parsed from
+// config, passed through as VALIDATION_SCRIPT_B64 to validate_memory_step.cjs,
+// and gating persistence on the validation step's outcome.
+func TestDriveMemoryValidationConfigAndGeneratedSteps(t *testing.T) {
+ compiler := NewCompiler()
+ config, err := compiler.extractDriveMemoryConfig(&ToolsConfig{
+ DriveMemory: &DriveMemoryToolConfig{Raw: []any{
+ map[string]any{
+ "id": "default",
+ "drive-name": "agent-state",
+ "validation": map[string]any{
+ "script": "if (!fs.existsSync(path.join(memoryRoot, 'index.json'))) throw new Error('missing index');",
+ "timeout-minutes": 1,
+ },
+ },
+ }},
+ })
+ require.NoError(t, err)
+ require.NotNil(t, config)
+ require.Len(t, config.Drives, 1)
+ require.NotNil(t, config.Drives[0].Validation)
+ assert.Equal(t, 1, config.Drives[0].Validation.TimeoutMinutes)
+
+ data := &WorkflowData{DriveMemoryConfig: config}
+
+ var validation strings.Builder
+ generateDriveMemoryValidation(&validation, data)
+ validationYAML := validation.String()
+ assert.Contains(t, validationYAML, "Validate drive-memory file types (default)")
+ assert.Contains(t, validationYAML, "VALIDATION_SCRIPT_B64:")
+ assert.Contains(t, validationYAML, "validate_memory_step.cjs")
+ assert.Contains(t, validationYAML, "id: "+driveMemoryValidationStepID("default"))
+
+ var persist strings.Builder
+ generateDriveMemoryPersistence(&persist, data, func(action string) string { return action + "@test-pin" })
+ assert.Contains(t, persist.String(), "steps."+driveMemoryValidationStepID("default")+".outcome == 'success'")
+}
+
func TestDriveMemoryRestorePreservesIntegrityLevel(t *testing.T) {
compiler := NewCompiler()
data := &WorkflowData{
diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go
index f99c383fdb7..81ebdf3290b 100644
--- a/pkg/workflow/repo_memory.go
+++ b/pkg/workflow/repo_memory.go
@@ -354,96 +354,105 @@ func generateRepoMemoryArtifactUpload(builder *strings.Builder, data *WorkflowDa
builder.WriteString(" # Upload repo memory as artifacts for push job\n")
for _, memory := range data.RepoMemoryConfig.Memories {
- // Determine the memory directory
memoryDir := constants.TmpRepoMemoryDir + memory.ID
-
- // Sanitize memory ID for artifact naming (remove hyphens, lowercase)
sanitizedID := SanitizeWorkflowIDForCacheKey(memory.ID)
-
- // Determine the label for step names
memoryLabel := "repo-memory"
if memory.Wiki {
memoryLabel = "wiki-memory"
}
- // Step: Sanitize filenames before upload to prevent artifact upload failures.
- // GitHub Actions artifacts are stored on NTFS-compatible filesystems, so filenames
- // must not contain: ? : * | < > " (among other characters).
- // The agent may create files with these characters (e.g. "Can-we-have-a-PR?.md"),
- // which causes the upload-artifact action to fail with a hard error.
- // The script uses git commands (git mv for tracked files, mv for untracked) since
- // repo-memory is backed by a git working tree.
- fmt.Fprintf(builder, " - name: Sanitize %s filenames (%s)\n", memoryLabel, memory.ID)
- builder.WriteString(" if: always()\n")
- builder.WriteString(" continue-on-error: true\n")
- builder.WriteString(" env:\n")
- fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
- builder.WriteString(" run: bash \"${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh\"\n")
-
- // Step: Filter out files that are ineligible for persistence (disallowed
- // extensions or non-matching file-glob patterns) before validation and
- // upload. Allowed-extensions and file-glob are persistence filters: ineligible
- // files are logged and removed here so that custom validation, the uploaded
- // artifact, and the downstream push all see the same effective file set.
- if len(memory.AllowedExtensions) > 0 || len(memory.FileGlob) > 0 {
- allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
- fmt.Fprintf(builder, " - name: Filter %s files (%s)\n", memoryLabel, memory.ID)
- builder.WriteString(" if: always()\n")
- fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
- builder.WriteString(" env:\n")
- fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
- fmt.Fprintf(builder, " ALLOWED_EXTENSIONS: '%s'\n", allowedExtsJSON)
- if len(memory.FileGlob) > 0 {
- fmt.Fprintf(builder, " FILE_GLOB_FILTER: \"%s\"\n", strings.Join(memory.FileGlob, " "))
- }
- builder.WriteString(" with:\n")
- builder.WriteString(" script: |\n")
- builder.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n")
- builder.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n")
- builder.WriteString(" const { filterIneligibleMemoryFiles } = require('${{ runner.temp }}/gh-aw/actions/memory_file_eligibility.cjs');\n")
- builder.WriteString(" const memoryDir = process.env.MEMORY_DIR || '';\n")
- builder.WriteString(" const allowedExtensions = JSON.parse(process.env.ALLOWED_EXTENSIONS || '[]');\n")
- builder.WriteString(" const fileGlobFilter = process.env.FILE_GLOB_FILTER || '';\n")
- builder.WriteString(" filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);\n")
- }
+ generateRepoMemorySanitizeFilenamesStep(builder, memory, memoryDir, memoryLabel)
+ generateRepoMemoryFilterFilesStep(builder, memory, memoryDir, memoryLabel)
+ validationStepID := generateRepoMemoryCustomValidationStep(builder, memory, memoryDir, memoryLabel)
+ generateRepoMemoryUploadArtifactStep(builder, memory, memoryDir, memoryLabel, sanitizedID, prefix, validationStepID, pinAction)
+ }
+}
- validationStepID := repoMemoryValidationStepID(memory.ID)
- if memory.Validation != nil {
- fmt.Fprintf(builder, " - name: Validate %s domain content (%s)\n", memoryLabel, memory.ID)
- fmt.Fprintf(builder, " id: %s\n", validationStepID)
- builder.WriteString(" if: always()\n")
- fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
- builder.WriteString(" env:\n")
- fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
- fmt.Fprintf(builder, " MEMORY_ID: %s\n", memory.ID)
- fmt.Fprintf(builder, " VALIDATION_SCRIPT_B64: %s\n", memoryValidationScriptBase64(memory.Validation))
- fmt.Fprintf(builder, " VALIDATION_TIMEOUT_SECONDS: %d\n", memoryValidationTimeoutSeconds(memory.Validation))
- if memory.FormatJSON {
- builder.WriteString(" FORMAT_JSON: 'true'\n")
- }
+// generateRepoMemorySanitizeFilenamesStep emits the step that renames files with characters
+// unsafe for artifact upload before validation and upload.
+// GitHub Actions artifacts are stored on NTFS-compatible filesystems, so filenames
+// must not contain: ? : * | < > " (among other characters).
+// The agent may create files with these characters (e.g. "Can-we-have-a-PR?.md"),
+// which causes the upload-artifact action to fail with a hard error.
+// The script uses git commands (git mv for tracked files, mv for untracked) since
+// repo-memory is backed by a git working tree.
+func generateRepoMemorySanitizeFilenamesStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) {
+ fmt.Fprintf(builder, " - name: Sanitize %s filenames (%s)\n", memoryLabel, memory.ID)
+ builder.WriteString(" if: always()\n")
+ builder.WriteString(" continue-on-error: true\n")
+ builder.WriteString(" env:\n")
+ fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
+ builder.WriteString(" run: bash \"${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh\"\n")
+}
- builder.WriteString(" with:\n")
- builder.WriteString(" script: |\n")
- builder.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n")
- builder.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n")
- builder.WriteString(" const { validateMemoryStep } = require('${{ runner.temp }}/gh-aw/actions/validate_memory_step.cjs');\n")
- builder.WriteString(" validateMemoryStep(core, { kind: 'repo', formatJSON: process.env.FORMAT_JSON === 'true', requireValidationScript: true });\n")
- }
+// generateRepoMemoryFilterFilesStep emits the step that filters out files ineligible for
+// persistence (disallowed extensions or non-matching file-glob patterns) before validation
+// and upload. Allowed-extensions and file-glob are persistence filters: ineligible files
+// are logged and removed here so that custom validation, the uploaded artifact, and the
+// downstream push all see the same effective file set.
+func generateRepoMemoryFilterFilesStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) {
+ if len(memory.AllowedExtensions) == 0 && len(memory.FileGlob) == 0 {
+ return
+ }
+ allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
+ fmt.Fprintf(builder, " - name: Filter %s files (%s)\n", memoryLabel, memory.ID)
+ builder.WriteString(" if: always()\n")
+ fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
+ builder.WriteString(" env:\n")
+ fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
+ fmt.Fprintf(builder, " ALLOWED_EXTENSIONS: '%s'\n", allowedExtsJSON)
+ if len(memory.FileGlob) > 0 {
+ fmt.Fprintf(builder, " FILE_GLOB_FILTER: \"%s\"\n", strings.Join(memory.FileGlob, " "))
+ }
+ builder.WriteString(" with:\n")
+ builder.WriteString(" script: |\n")
+ builder.WriteString(generateGitHubScriptWithRequire("memory_file_eligibility.cjs"))
+}
- // Step: Upload repo-memory directory as artifact
- fmt.Fprintf(builder, " - name: Upload %s artifact (%s)\n", memoryLabel, memory.ID)
- if memory.Validation != nil {
- fmt.Fprintf(builder, " if: always() && steps.%s.outcome == 'success'\n", validationStepID)
- } else {
- builder.WriteString(" if: always()\n")
- }
- fmt.Fprintf(builder, " uses: %s\n", pinAction("actions/upload-artifact"))
- builder.WriteString(" with:\n")
- fmt.Fprintf(builder, " name: %srepo-memory-%s\n", prefix, sanitizedID)
- fmt.Fprintf(builder, " path: %s\n", memoryDir)
- builder.WriteString(" retention-days: 1\n")
- builder.WriteString(" if-no-files-found: ignore\n")
+// generateRepoMemoryCustomValidationStep emits the optional custom-validation step and
+// returns its step ID (used to gate the subsequent upload step), or "" when no custom
+// validation is configured for this memory.
+func generateRepoMemoryCustomValidationStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) string {
+ if memory.Validation == nil {
+ return ""
+ }
+ validationStepID := repoMemoryValidationStepID(memory.ID)
+ fmt.Fprintf(builder, " - name: Validate %s domain content (%s)\n", memoryLabel, memory.ID)
+ fmt.Fprintf(builder, " id: %s\n", validationStepID)
+ builder.WriteString(" if: always()\n")
+ fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
+ builder.WriteString(" env:\n")
+ fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
+ fmt.Fprintf(builder, " MEMORY_ID: %s\n", memory.ID)
+ fmt.Fprintf(builder, " VALIDATION_SCRIPT_B64: %s\n", memoryValidationScriptBase64(memory.Validation))
+ fmt.Fprintf(builder, " VALIDATION_TIMEOUT_SECONDS: %d\n", memoryValidationTimeoutSeconds(memory.Validation))
+ if memory.FormatJSON {
+ builder.WriteString(" FORMAT_JSON: 'true'\n")
+ }
+ builder.WriteString(" with:\n")
+ builder.WriteString(" script: |\n")
+ builder.WriteString(" const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');\n")
+ builder.WriteString(" setupGlobals(core, github, context, exec, io, getOctokit);\n")
+ builder.WriteString(" const { validateMemoryStep } = require('${{ runner.temp }}/gh-aw/actions/validate_memory_step.cjs');\n")
+ builder.WriteString(" validateMemoryStep(core, { kind: 'repo', formatJSON: process.env.FORMAT_JSON === 'true', requireValidationScript: true });\n")
+ return validationStepID
+}
+
+// generateRepoMemoryUploadArtifactStep emits the step that uploads the repo-memory
+// directory as an artifact, gated on the custom-validation step's outcome when configured.
+func generateRepoMemoryUploadArtifactStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel, sanitizedID, prefix, validationStepID string, pinAction func(string) string) {
+ fmt.Fprintf(builder, " - name: Upload %s artifact (%s)\n", memoryLabel, memory.ID)
+ if validationStepID != "" {
+ fmt.Fprintf(builder, " if: always() && steps.%s.outcome == 'success'\n", validationStepID)
+ } else {
+ builder.WriteString(" if: always()\n")
}
+ fmt.Fprintf(builder, " uses: %s\n", pinAction("actions/upload-artifact"))
+ builder.WriteString(" with:\n")
+ fmt.Fprintf(builder, " name: %srepo-memory-%s\n", prefix, sanitizedID)
+ fmt.Fprintf(builder, " path: %s\n", memoryDir)
+ builder.WriteString(" retention-days: 1\n")
+ builder.WriteString(" if-no-files-found: ignore\n")
}
func repoMemoryValidationStepID(memoryID string) string {
From b11fdf51fcd844b1b7926ac922126f05f26831e4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 06:20:07 +0000
Subject: [PATCH 5/7] repo-memory: consistent filtering across preflight, agent
validation, and push; fix lint debt
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
.../agent-performance-analyzer.lock.yml | 5 +-
.../workflows/agentic-token-audit.lock.yml | 5 +-
.../agentic-token-optimizer.lock.yml | 5 +-
.github/workflows/audit-workflows.lock.yml | 5 +-
.../workflows/copilot-agent-analysis.lock.yml | 5 +-
.../copilot-centralization-optimizer.lock.yml | 5 +-
.../copilot-cli-deep-research.lock.yml | 5 +-
.../copilot-pr-nlp-analysis.lock.yml | 5 +-
.../copilot-pr-prompt-analysis.lock.yml | 5 +-
.../copilot-session-insights.lock.yml | 5 +-
...daily-awf-spec-compiler-surfacing.lock.yml | 5 +-
.../workflows/daily-cli-performance.lock.yml | 5 +-
.../daily-formal-spec-verifier.lock.yml | 5 +-
...daily-harness-experiment-proposer.lock.yml | 5 +-
.github/workflows/daily-news.lock.yml | 5 +-
.../daily-safeoutputs-git-simulator.lock.yml | 5 +-
.github/workflows/daily-storify.lock.yml | 5 +-
.../daily-testify-uber-super-expert.lock.yml | 5 +-
.github/workflows/deep-report.lock.yml | 5 +-
.github/workflows/delight.lock.yml | 5 +-
.github/workflows/eslint-refiner.lock.yml | 5 +-
.github/workflows/metrics-collector.lock.yml | 5 +-
.github/workflows/pr-triage-agent.lock.yml | 5 +-
.../workflows/security-compliance.lock.yml | 5 +-
.github/workflows/sergo.lock.yml | 5 +-
.github/workflows/smoke-ci.lock.yml | 5 +-
.../workflow-health-manager.lock.yml | 5 +-
actions/setup/js/memory_file_eligibility.cjs | 9 +-
.../setup/js/memory_file_eligibility.test.cjs | 9 +
actions/setup/js/push_repo_memory.cjs | 14 +-
actions/setup/js/push_repo_memory.test.cjs | 20 +
actions/setup/js/safe_outputs_handlers.cjs | 14 +
.../setup/js/safe_outputs_handlers.test.cjs | 31 ++
actions/setup/setup.sh | 1 +
pkg/workflow/repo_memory.go | 80 +++-
pkg/workflow/repo_memory_test.go | 72 +++
.../safe_outputs_config_generation.go | 417 ++++++++++--------
.../safe_outputs_config_generation_test.go | 54 +++
38 files changed, 592 insertions(+), 264 deletions(-)
diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index 9573b94f971..618270c529a 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -671,7 +671,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":10},\"create_discussion\":{\"category\":\"audits\",\"expires\":24,\"fallback_to_issue\":true,\"max\":1},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"cookie\"],\"max\":5},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":10},\"create_discussion\":{\"category\":\"audits\",\"expires\":24,\"fallback_to_issue\":true,\"max\":1},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"cookie\"],\"max\":5},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1342,6 +1342,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1357,7 +1358,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/agentic-token-audit.lock.yml b/.github/workflows/agentic-token-audit.lock.yml
index fd062bb83fa..aa73fbfe8b9 100644
--- a/.github/workflows/agentic-token-audit.lock.yml
+++ b/.github/workflows/agentic-token-audit.lock.yml
@@ -644,7 +644,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":72,\"max\":1,\"title_prefix\":\"[agentic-token-audit] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":72,\"max\":1,\"title_prefix\":\"[agentic-token-audit] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
@@ -1224,6 +1224,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1239,7 +1240,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/agentic-token-optimizer.lock.yml b/.github/workflows/agentic-token-optimizer.lock.yml
index a3a35d7ab55..641bcc865cd 100644
--- a/.github/workflows/agentic-token-optimizer.lock.yml
+++ b/.github/workflows/agentic-token-optimizer.lock.yml
@@ -592,7 +592,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"max\":1,\"title_prefix\":\"[agentic-token-optimizer] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"max\":1,\"title_prefix\":\"[agentic-token-optimizer] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1134,6 +1134,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1149,7 +1150,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index b93efc8ebb2..3b8d5890b31 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -748,7 +748,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[audit-workflows] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":3,\"max-size\":10240}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[audit-workflows] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":3,\"max-size\":10240}}"
GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
@@ -1387,6 +1387,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1402,7 +1403,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index 62c34055844..45814c8426d 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -694,7 +694,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-agent-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-agent-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1278,6 +1278,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1293,7 +1294,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-centralization-optimizer.lock.yml b/.github/workflows/copilot-centralization-optimizer.lock.yml
index bc53a44ae56..5c7e4ba95ba 100644
--- a/.github/workflows/copilot-centralization-optimizer.lock.yml
+++ b/.github/workflows/copilot-centralization-optimizer.lock.yml
@@ -603,7 +603,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":720,\"labels\":[\"report\",\"ai-optimization\"],\"max\":1,\"title_prefix\":\"[copilot-centralization] \"},\"create_report_incomplete_issue\":{},\"max_bot_mentions\":1,\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":720,\"labels\":[\"report\",\"ai-optimization\"],\"max\":1,\"title_prefix\":\"[copilot-centralization] \"},\"create_report_incomplete_issue\":{},\"max_bot_mentions\":1,\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1176,6 +1176,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1191,7 +1192,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index 0d56fa405a5..3923c7eeaea 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -570,7 +570,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":24,\"max\":1,\"title_prefix\":\"[copilot-cli-research] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":204800,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":24,\"max\":1,\"title_prefix\":\"[copilot-cli-research] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":204800,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1123,6 +1123,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1138,7 +1139,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 946100cd6a4..61318a101fc 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -657,7 +657,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[nlp-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[nlp-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
@@ -1211,6 +1211,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1226,7 +1227,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 6ebf5b27c5d..3ee12d3f50b 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -617,7 +617,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[prompt-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[prompt-analysis] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1154,6 +1154,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1169,7 +1170,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index d0d18cd069f..ed7911ee67d 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -656,7 +656,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-session-insights] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[copilot-session-insights] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
@@ -1251,6 +1251,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1266,7 +1267,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
index 1465ff4a485..15d84336ec3 100644
--- a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
+++ b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
@@ -596,7 +596,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"labels\":[\"automation\",\"awf\",\"compiler\",\"specifications\"],\"max\":1,\"title_prefix\":\"[awf-feature-surfacing] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":65536,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"labels\":[\"automation\",\"awf\",\"compiler\",\"specifications\"],\"max\":1,\"title_prefix\":\"[awf-feature-surfacing] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\".json .md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":65536,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1101,6 +1101,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1116,7 +1117,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index addc561a8d6..8afa6072775 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -641,7 +641,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":5},\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[daily-cli-performance] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"performance\",\"automation\",\"cookie\"],\"max\":3,\"title_prefix\":\"[performance] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":131072,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":5},\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[daily-cli-performance] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"performance\",\"automation\",\"cookie\"],\"max\":3,\"title_prefix\":\"[performance] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.txt\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":131072,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1373,6 +1373,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1388,7 +1389,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-formal-spec-verifier.lock.yml b/.github/workflows/daily-formal-spec-verifier.lock.yml
index 8c98f1a5528..45be25a23f2 100644
--- a/.github/workflows/daily-formal-spec-verifier.lock.yml
+++ b/.github/workflows/daily-formal-spec-verifier.lock.yml
@@ -599,7 +599,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"expires\":168,\"labels\":[\"automation\",\"formal-verification\",\"testing\",\"specifications\"],\"max\":1,\"title_prefix\":\"[formal-spec] \"},\"create_report_incomplete_issue\":{},\"max_bot_mentions\":1,\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":65536,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"expires\":168,\"labels\":[\"automation\",\"formal-verification\",\"testing\",\"specifications\"],\"max\":1,\"title_prefix\":\"[formal-spec] \"},\"create_report_incomplete_issue\":{},\"max_bot_mentions\":1,\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.md *.json\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":65536,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1176,6 +1176,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1191,7 +1192,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-harness-experiment-proposer.lock.yml b/.github/workflows/daily-harness-experiment-proposer.lock.yml
index 41225b4cdec..31d0e2794c0 100644
--- a/.github/workflows/daily-harness-experiment-proposer.lock.yml
+++ b/.github/workflows/daily-harness-experiment-proposer.lock.yml
@@ -647,7 +647,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":168,\"labels\":[\"automation\",\"experiment-proposal\",\"harness\",\"needs-manual-patch\"],\"max\":1,\"title_prefix\":\"[harness-experiment-proposal] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":168,\"labels\":[\"automation\",\"experiment-proposal\",\"harness\",\"needs-manual-patch\"],\"max\":1,\"title_prefix\":\"[harness-experiment-proposal] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1238,6 +1238,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1253,7 +1254,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index 71c80483c97..73fcb2d5888 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -752,7 +752,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"daily-news\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_artifact\":{\"max-size-bytes\":104857600,\"max-uploads\":3,\"retention-days\":30,\"skip-archive\":true},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"daily-news\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"upload_artifact\":{\"max-size-bytes\":104857600,\"max-uploads\":3,\"retention-days\":30,\"skip-archive\":true},\"upload_asset\":{\"allowed-exts\":[\".png\",\".jpg\",\".jpeg\",\".svg\"],\"branch\":\"assets/${GITHUB_WORKFLOW}\",\"max\":5,\"max-size\":10240}}"
GITHUB_WORKFLOW: ${{ github.workflow }}
with:
script: |
@@ -1294,6 +1294,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1309,7 +1310,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
index 022127b08be..32bcb10dd1a 100644
--- a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
+++ b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
@@ -596,7 +596,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":false,\"deduplicate_by_title\":3,\"labels\":[\"git-simulator\",\"safe-outputs\",\"automated\"],\"max\":10,\"title_prefix\":\"[git-sim] \"},\"create_pull_request\":{\"allowed_files\":[\"sim/**\",\"stuff.md\",\"history.md\"],\"draft\":true,\"expires\":24,\"if_no_changes\":\"warn\",\"labels\":[\"git-sim-probe\",\"automated\"],\"max\":1,\"max_patch_files\":200,\"max_patch_size\":5120,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"CLAUDE.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[git-sim] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":204800,\"max_patch_size\":10240}]},\"push_to_pull_request_branch\":{\"allowed_files\":[\"sim/**\",\"stuff.md\",\"history.md\"],\"if_no_changes\":\"ignore\",\"max_patch_size\":5120,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"CLAUDE.md\",\"AGENTS.md\"],\"required_labels\":[\"git-sim-probe\",\"automated\"],\"target\":\"*\",\"title_prefix\":\"[git-sim] \"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"close_older_issues\":false,\"deduplicate_by_title\":3,\"labels\":[\"git-simulator\",\"safe-outputs\",\"automated\"],\"max\":10,\"title_prefix\":\"[git-sim] \"},\"create_pull_request\":{\"allowed_files\":[\"sim/**\",\"stuff.md\",\"history.md\"],\"draft\":true,\"expires\":24,\"if_no_changes\":\"warn\",\"labels\":[\"git-sim-probe\",\"automated\"],\"max\":1,\"max_patch_files\":200,\"max_patch_size\":5120,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"CLAUDE.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[git-sim] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"allowed_extensions\":[\".json\",\".md\"],\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":204800,\"max_patch_size\":10240}]},\"push_to_pull_request_branch\":{\"allowed_files\":[\"sim/**\",\"stuff.md\",\"history.md\"],\"if_no_changes\":\"ignore\",\"max_patch_size\":5120,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"CLAUDE.md\",\"AGENTS.md\"],\"required_labels\":[\"git-sim-probe\",\"automated\"],\"target\":\"*\",\"title_prefix\":\"[git-sim] \"},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1264,6 +1264,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1278,7 +1279,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-storify.lock.yml b/.github/workflows/daily-storify.lock.yml
index b65ac1b1e5f..00a6d401470 100644
--- a/.github/workflows/daily-storify.lock.yml
+++ b/.github/workflows/daily-storify.lock.yml
@@ -653,7 +653,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":48,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[storify] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":48,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[storify] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"storify/state.json storify/episodes.jsonl storify/loops.jsonl\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1239,6 +1239,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1254,7 +1255,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index bc4a90d9b70..19933edf23b 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -594,7 +594,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":48,\"labels\":[\"testing\",\"code-quality\",\"automated-analysis\",\"cookie\"],\"max\":1,\"title_prefix\":\"[testify-expert] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":51200,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":48,\"labels\":[\"testing\",\"code-quality\",\"automated-analysis\",\"cookie\"],\"max\":1,\"title_prefix\":\"[testify-expert] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.txt\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":51200,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1211,6 +1211,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1226,7 +1227,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index 99b662c4dc6..51030f80b3f 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -928,7 +928,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":3},\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_issue\":{\"deduplicate_by_title\":28,\"expires\":48,\"group\":true,\"labels\":[\"automation\",\"improvement\",\"quick-win\",\"cookie\",\"code-quality\",\"task-mining\"],\"max\":7,\"title_prefix\":\"[deep-report] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":1048576,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_artifact\":{\"max-size-bytes\":104857600,\"max-uploads\":1,\"retention-days\":30}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":3},\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_issue\":{\"deduplicate_by_title\":28,\"expires\":48,\"group\":true,\"labels\":[\"automation\",\"improvement\",\"quick-win\",\"cookie\",\"code-quality\",\"task-mining\"],\"max\":7,\"title_prefix\":\"[deep-report] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.md *.json\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":1048576,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"upload_artifact\":{\"max-size-bytes\":104857600,\"max-uploads\":1,\"retention-days\":30}}"
with:
script: |
const path = require('path');
@@ -1925,6 +1925,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1940,7 +1941,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index d7386313d50..a63ed5adb4a 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -574,7 +574,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[delight] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"delight\",\"cookie\"],\"max\":2},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":72,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[delight] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"delight\",\"cookie\"],\"max\":2},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl *.csv *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1181,6 +1181,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1196,7 +1197,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/eslint-refiner.lock.yml b/.github/workflows/eslint-refiner.lock.yml
index 2f3a01b0e9c..15d30b907ed 100644
--- a/.github/workflows/eslint-refiner.lock.yml
+++ b/.github/workflows/eslint-refiner.lock.yml
@@ -575,7 +575,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[eslint-refiner] \"},\"create_issue\":{\"expires\":168,\"labels\":[\"eslint\",\"cookie\"],\"max\":3},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[eslint-refiner] \"},\"create_issue\":{\"expires\":168,\"labels\":[\"eslint\",\"cookie\"],\"max\":3},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1205,6 +1205,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1220,7 +1221,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml
index 8c9d8349982..76930777a71 100644
--- a/.github/workflows/metrics-collector.lock.yml
+++ b/.github/workflows/metrics-collector.lock.yml
@@ -625,7 +625,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"labels\":[\"metrics-collector\"],\"max\":1,\"title_prefix\":\"[metrics-collector]\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":131072}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"labels\":[\"metrics-collector\"],\"max\":1,\"title_prefix\":\"[metrics-collector]\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"metrics/**\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":131072}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1223,6 +1223,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1238,7 +1239,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index 373c90be337..a87ae93cf06 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -591,7 +591,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":50},\"add_labels\":{\"max\":100},\"create_check_run\":{\"max\":1},\"create_issue\":{\"close_older_issues\":true,\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"expires\":24,\"labels\":[\"automation\",\"pr-triage-report\"],\"max\":1,\"title_prefix\":\"[PR Triage Report] \"},\"create_pull_request_review_comment\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"submit_pull_request_review\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":1}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":50},\"add_labels\":{\"max\":100},\"create_check_run\":{\"max\":1},\"create_issue\":{\"close_older_issues\":true,\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"expires\":24,\"labels\":[\"automation\",\"pr-triage-report\"],\"max\":1,\"title_prefix\":\"[PR Triage Report] \"},\"create_pull_request_review_comment\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{},\"submit_pull_request_review\":{\"data_enabled\":true,\"data_schema\":{\"additionalProperties\":false,\"properties\":{\"action\":{\"type\":\"string\"},\"category\":{\"type\":\"string\"},\"pr_number\":{\"type\":\"integer\"},\"risk\":{\"type\":\"string\"}},\"required\":[\"action\",\"category\",\"pr_number\",\"risk\"],\"type\":\"object\"},\"max\":1}}"
with:
script: |
const path = require('path');
@@ -1572,6 +1572,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1587,7 +1588,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index 9601e415614..05f18912d37 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -586,7 +586,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"security\",\"campaign-tracker\",\"cookie\"],\"max\":100},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"security\",\"campaign-tracker\",\"cookie\"],\"max\":100},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"security-compliance-*/**\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1130,6 +1130,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1145,7 +1146,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 00c129d7734..fb3f056b12e 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -592,7 +592,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[sergo] \"},\"create_issue\":{\"expires\":168,\"labels\":[\"sergo\",\"cookie\"],\"max\":3},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":24,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[sergo] \"},\"create_issue\":{\"expires\":168,\"labels\":[\"sergo\",\"cookie\"],\"max\":3},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.jsonl\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"report_incomplete\":{}}"
with:
script: |
const path = require('path');
@@ -1259,6 +1259,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1274,7 +1275,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml
index 76e55779c89..724764bd369 100644
--- a/.github/workflows/smoke-ci.lock.yml
+++ b/.github/workflows/smoke-ci.lock.yml
@@ -668,7 +668,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"ai-generated\"],\"max\":1},\"comment_memory\":{\"max\":1,\"memory_id\":\"default\"},\"create_issue\":{\"close_older_issues\":true,\"close_older_key\":\"smoke-ci-memory-safe-outputs\",\"labels\":[\"ai-generated\"],\"max\":1,\"title_prefix\":\"[smoke-ci] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"remove_labels\":{\"allowed\":[\"ai-generated\"],\"max\":1},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":1,\"target\":\"*\"},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":1,\"target\":\"*\",\"update_branch\":false,\"update_branch_stacks\":true}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"ai-generated\"],\"max\":1},\"comment_memory\":{\"max\":1,\"memory_id\":\"default\"},\"create_issue\":{\"close_older_issues\":true,\"close_older_key\":\"smoke-ci-memory-safe-outputs\",\"labels\":[\"ai-generated\"],\"max\":1,\"title_prefix\":\"[smoke-ci] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":10240}]},\"remove_labels\":{\"allowed\":[\"ai-generated\"],\"max\":1},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":1,\"target\":\"*\"},\"update_pull_request\":{\"allow_body\":true,\"allow_title\":true,\"max\":1,\"target\":\"*\",\"update_branch\":false,\"update_branch_stacks\":true}}"
with:
script: |
const path = require('path');
@@ -1395,6 +1395,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1410,7 +1411,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index fbd0b27f971..ab5773c0b91 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -579,7 +579,7 @@ jobs:
env:
GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw"
GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}"
- GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":15},\"create_issue\":{\"expires\":24,\"group\":true,\"labels\":[\"cookie\"],\"max\":10},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":5}}"
+ GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":15},\"create_issue\":{\"expires\":24,\"group\":true,\"labels\":[\"cookie\"],\"max\":10},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"file_glob\":\"*.json *.md\",\"id\":\"default\",\"max_file_count\":100,\"max_file_size\":102400,\"max_patch_size\":51200}]},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":5}}"
with:
script: |
const path = require('path');
@@ -1216,6 +1216,7 @@ jobs:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Filter repo-memory files (default)
+ id: filter_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
@@ -1231,7 +1232,7 @@ jobs:
const { main } = require(path.join(actionsDir, 'memory_file_eligibility.cjs'));
await main();
- name: Upload repo-memory artifact (default)
- if: always()
+ if: always() && steps.filter_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
diff --git a/actions/setup/js/memory_file_eligibility.cjs b/actions/setup/js/memory_file_eligibility.cjs
index f0c41a60093..2612d6d3684 100644
--- a/actions/setup/js/memory_file_eligibility.cjs
+++ b/actions/setup/js/memory_file_eligibility.cjs
@@ -8,9 +8,10 @@ const { globPatternToRegex } = require("./glob_pattern_helpers.cjs");
/**
* Compile a space-separated FILE_GLOB_FILTER string into an array of RegExp patterns.
- * Slashless patterns (e.g. "*.json") are matched at the root of a single memory
- * subfolder (depth 1 only). Patterns that already contain "/" are matched against
- * the full relative path unchanged.
+ * Patterns are matched against the file's path relative to the memory directory root.
+ * Slashless patterns (e.g. "*.json") match files directly at that root (depth 0), matching
+ * the documented FILE_GLOB_FILTER contract (e.g. "history.jsonl" at the memory directory
+ * root matches "*.jsonl"). Patterns containing "/" match the full relative path unchanged.
*
* @param {string} fileGlobFilter - Space-separated glob patterns (may be empty)
* @returns {{ patternStrs: string[], compiledPatterns: RegExp[] }}
@@ -20,7 +21,7 @@ function compileFileGlobPatterns(fileGlobFilter) {
return { patternStrs: [], compiledPatterns: [] };
}
const patternStrs = fileGlobFilter.trim().split(/\s+/).filter(Boolean);
- const compiledPatterns = patternStrs.map(pattern => globPatternToRegex(pattern, { matchSubfolderRoot: !pattern.includes("/") }));
+ const compiledPatterns = patternStrs.map(pattern => globPatternToRegex(pattern));
return { patternStrs, compiledPatterns };
}
diff --git a/actions/setup/js/memory_file_eligibility.test.cjs b/actions/setup/js/memory_file_eligibility.test.cjs
index 4ac2c4f8abb..70952e44223 100644
--- a/actions/setup/js/memory_file_eligibility.test.cjs
+++ b/actions/setup/js/memory_file_eligibility.test.cjs
@@ -40,6 +40,15 @@ describe("memory_file_eligibility.cjs", () => {
// Passes both filters
expect(isMemoryFileEligible("sub/notes.json", [".json"], compiledPatterns).eligible).toBe(true);
});
+
+ it("matches slashless patterns against root-level (depth 0) files, per the documented FILE_GLOB_FILTER contract", () => {
+ // FILE_GLOB_FILTER docs (push_repo_memory.cjs) document that a file at the memory
+ // directory root, e.g. "history.jsonl", is matched by the slashless pattern "*.jsonl".
+ const { compiledPatterns } = compileFileGlobPatterns("*.jsonl");
+ expect(isMemoryFileEligible("history.jsonl", [], compiledPatterns).eligible).toBe(true);
+ // A nested file should not match a slashless pattern (single * doesn't cross directories).
+ expect(isMemoryFileEligible("sub/history.jsonl", [], compiledPatterns).eligible).toBe(false);
+ });
});
describe("compileFileGlobPatterns", () => {
diff --git a/actions/setup/js/push_repo_memory.cjs b/actions/setup/js/push_repo_memory.cjs
index c69bd11b9b0..02320ada776 100644
--- a/actions/setup/js/push_repo_memory.cjs
+++ b/actions/setup/js/push_repo_memory.cjs
@@ -9,7 +9,7 @@ const { getGitAuthEnv } = require("./git_auth_helpers.cjs");
const { execGitSync } = require("./git_helpers.cjs");
const { getStagedPatchDiffSizeBytes } = require("./git_patch_utils.cjs");
const { formatJSONFiles, runCustomMemoryValidation } = require("./memory_custom_validation.cjs");
-const { compileFileGlobPatterns, isMemoryFileEligible } = require("./memory_file_eligibility.cjs");
+const { compileFileGlobPatterns, filterIneligibleMemoryFiles, isMemoryFileEligible } = require("./memory_file_eligibility.cjs");
const { parseAllowedRepos, validateRepo } = require("./repo_helpers.cjs");
const { pushSignedCommits } = require("./push_signed_commits.cjs");
@@ -310,6 +310,18 @@ async function main() {
const destMemoryPath = workspaceDir;
core.info(`Destination directory: ${destMemoryPath}`);
+ // Remove any pre-existing files in the checked-out branch that no longer pass the
+ // current allowed-extensions/file-glob filters (e.g. left over from a prior run with
+ // different filter settings). Without this, formatJSONFiles/runCustomMemoryValidation
+ // below would still process stale ineligible files even though the newly copied files
+ // are already filtered.
+ if (allowedExtensions.length > 0 || fileGlobFilter) {
+ const { removed } = filterIneligibleMemoryFiles(destMemoryPath, allowedExtensions, fileGlobFilter, core);
+ if (removed.length > 0) {
+ core.info(`Removed ${removed.length} stale ineligible file(s) from existing branch content`);
+ }
+ }
+
// Recursively scan and collect files from artifact directory
let filesToCopy = [];
/** @type {Array<{path: string, reason: string}>} */
diff --git a/actions/setup/js/push_repo_memory.test.cjs b/actions/setup/js/push_repo_memory.test.cjs
index 2961a28f575..b96e865b1e0 100644
--- a/actions/setup/js/push_repo_memory.test.cjs
+++ b/actions/setup/js/push_repo_memory.test.cjs
@@ -1634,6 +1634,26 @@ describe("push_repo_memory.cjs - allowed-extensions persistence filter (regressi
expect(scriptContent).toContain("if (filesToCopy.length === 0)");
expect(scriptContent).toContain("No eligible files to copy from artifact");
});
+
+ it("filters stale ineligible files already present in the checked-out branch before format/validation (source check)", () => {
+ // Review feedback: after checking out an existing memory branch, files from prior
+ // runs that no longer match the current allowed-extensions/file-glob filters must
+ // not be left in destMemoryPath, since formatJSONFiles/runCustomMemoryValidation
+ // operate on the whole destMemoryPath, not just the newly-copied eligible files.
+ const nodeFs = require("fs");
+ const nodePath = require("path");
+ const scriptPath = nodePath.join(import.meta.dirname, "push_repo_memory.cjs");
+ const scriptContent = nodeFs.readFileSync(scriptPath, "utf8");
+
+ expect(scriptContent).toContain("filterIneligibleMemoryFiles(destMemoryPath, allowedExtensions, fileGlobFilter, core)");
+ // The stale-file filter must run before destMemoryPath is formatted/validated.
+ const filterIdx = scriptContent.indexOf("filterIneligibleMemoryFiles(destMemoryPath");
+ const formatIdx = scriptContent.indexOf("formatJSONFiles(destMemoryPath, maxFileSize)");
+ const validateIdx = scriptContent.indexOf("runCustomMemoryValidation({");
+ expect(filterIdx).toBeGreaterThan(-1);
+ expect(filterIdx).toBeLessThan(formatIdx);
+ expect(filterIdx).toBeLessThan(validateIdx);
+ });
});
// ──────────────────────────────────────────────────────────────────────────────
diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs
index 81b1bb45308..314eb846547 100644
--- a/actions/setup/js/safe_outputs_handlers.cjs
+++ b/actions/setup/js/safe_outputs_handlers.cjs
@@ -32,6 +32,7 @@ const { lstatGuard } = require("./symlink_guard.cjs");
const { validateValueAgainstSchema } = require("./mcp_scripts_validation.cjs");
const { resolveDataSchema } = require("./data_schema_normalizer.cjs");
const { clearValidationMarker, formatJSONFiles, runCustomMemoryValidation, writeValidationMarker } = require("./memory_custom_validation.cjs");
+const { filterIneligibleMemoryFiles } = require("./memory_file_eligibility.cjs");
/** PR event names used for target:triggering context validation across all safe-output handlers. */
const PR_EVENT_NAMES = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]);
@@ -1786,6 +1787,8 @@ function createHandlers(server, appendSafeOutput, config = {}) {
const maxFileSize = memoryConf.max_file_size || 10240;
const maxPatchSize = memoryConf.max_patch_size || 10240;
const maxFileCount = memoryConf.max_file_count || 100;
+ const allowedExtensions = Array.isArray(memoryConf.allowed_extensions) ? memoryConf.allowed_extensions : [];
+ const fileGlobFilter = typeof memoryConf.file_glob === "string" ? memoryConf.file_glob : "";
const validationConfig = memoryConf.validation || null;
const validationScript = validationConfig && typeof validationConfig.script === "string" ? validationConfig.script : "";
const validationTimeoutSeconds = validationConfig && Number.isFinite(validationConfig.timeout) ? validationConfig.timeout : undefined;
@@ -1804,6 +1807,17 @@ function createHandlers(server, appendSafeOutput, config = {}) {
};
}
+ // Allowed-extensions and file-glob are persistence filters: ineligible files must be
+ // removed here too, before formatting/scanning/staging/custom-validation, so this
+ // preflight sees the same effective file set as the later filter/upload/push steps
+ // and never hard-fails on a file that would have been silently dropped downstream.
+ if (allowedExtensions.length > 0 || fileGlobFilter) {
+ const { removed } = filterIneligibleMemoryFiles(memoryDir, allowedExtensions, fileGlobFilter, core);
+ if (removed.length > 0) {
+ core.info(`push_repo_memory: ignored ${removed.length} ineligible file(s) before validation`);
+ }
+ }
+
clearValidationMarker("repo", memoryId);
if (memoryConf.format_json === true) {
diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs
index 86be9ca5fcd..90a83047887 100644
--- a/actions/setup/js/safe_outputs_handlers.test.cjs
+++ b/actions/setup/js/safe_outputs_handlers.test.cjs
@@ -3248,6 +3248,37 @@ describe("safe_outputs_handlers", () => {
expect(data.error).toContain("3 files");
});
+ it("should ignore ineligible files (disallowed extension) instead of hard-failing on file count", () => {
+ // Regression: the preflight must apply the same allowed-extensions/file-glob
+ // filtering as the agent-side filter step and the push job, so an ineligible
+ // file left in the memory directory does not cause a hard preflight failure.
+ const h = makeHandlersWithMemory({ max_file_count: 1, allowed_extensions: [".json"] });
+ fs.mkdirSync(memoryDir, { recursive: true });
+ initGitRepo(memoryDir);
+ fs.writeFileSync(path.join(memoryDir, "notes.json"), "{}");
+ fs.writeFileSync(path.join(memoryDir, "notes.json.new"), "{}");
+ const result = h.pushRepoMemoryHandler({ memory_id: "default" });
+ expect(result.isError).toBeUndefined();
+ const data = JSON.parse(result.content[0].text);
+ expect(data.result).toBe("success");
+ expect(fs.existsSync(path.join(memoryDir, "notes.json.new"))).toBe(false);
+ expect(fs.existsSync(path.join(memoryDir, "notes.json"))).toBe(true);
+ });
+
+ it("should ignore files not matching file_glob before counting/staging", () => {
+ const h = makeHandlersWithMemory({ max_file_count: 1, file_glob: "*.json" });
+ fs.mkdirSync(memoryDir, { recursive: true });
+ initGitRepo(memoryDir);
+ fs.writeFileSync(path.join(memoryDir, "notes.json"), "{}");
+ fs.writeFileSync(path.join(memoryDir, "notes.md"), "hello");
+ const result = h.pushRepoMemoryHandler({ memory_id: "default" });
+ expect(result.isError).toBeUndefined();
+ const data = JSON.parse(result.content[0].text);
+ expect(data.result).toBe("success");
+ expect(fs.existsSync(path.join(memoryDir, "notes.md"))).toBe(false);
+ expect(fs.existsSync(path.join(memoryDir, "notes.json"))).toBe(true);
+ });
+
it("should pass when total folder size is large but staged diff is tiny", () => {
const h = makeHandlersWithMemory({ max_patch_size: 50, max_file_size: 1024 * 1024 });
fs.mkdirSync(memoryDir, { recursive: true });
diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh
index 3eb617afe74..ba15d66ec28 100755
--- a/actions/setup/setup.sh
+++ b/actions/setup/setup.sh
@@ -321,6 +321,7 @@ SAFE_OUTPUTS_FILES=(
"read_buffer.cjs"
"mcp_scripts_validation.cjs"
"memory_custom_validation.cjs"
+ "memory_file_eligibility.cjs"
"messages.cjs"
"messages_core.cjs"
"messages_footer.cjs"
diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go
index 81ebdf3290b..b9b69c93ad0 100644
--- a/pkg/workflow/repo_memory.go
+++ b/pkg/workflow/repo_memory.go
@@ -362,9 +362,18 @@ func generateRepoMemoryArtifactUpload(builder *strings.Builder, data *WorkflowDa
}
generateRepoMemorySanitizeFilenamesStep(builder, memory, memoryDir, memoryLabel)
- generateRepoMemoryFilterFilesStep(builder, memory, memoryDir, memoryLabel)
- validationStepID := generateRepoMemoryCustomValidationStep(builder, memory, memoryDir, memoryLabel)
- generateRepoMemoryUploadArtifactStep(builder, memory, memoryDir, memoryLabel, sanitizedID, prefix, validationStepID, pinAction)
+ filterStepID := generateRepoMemoryFilterFilesStep(builder, memory, memoryDir, memoryLabel)
+ validationStepID := generateRepoMemoryCustomValidationStep(builder, memory, memoryDir, memoryLabel, filterStepID)
+ generateRepoMemoryUploadArtifactStep(builder, repoMemoryUploadStepParams{
+ memory: memory,
+ memoryDir: memoryDir,
+ memoryLabel: memoryLabel,
+ sanitizedID: sanitizedID,
+ prefix: prefix,
+ filterStepID: filterStepID,
+ validationStepID: validationStepID,
+ pinAction: pinAction,
+ })
}
}
@@ -390,12 +399,16 @@ func generateRepoMemorySanitizeFilenamesStep(builder *strings.Builder, memory Re
// and upload. Allowed-extensions and file-glob are persistence filters: ineligible files
// are logged and removed here so that custom validation, the uploaded artifact, and the
// downstream push all see the same effective file set.
-func generateRepoMemoryFilterFilesStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) {
+// It returns the step's ID (used to gate subsequent validation/upload steps on its
+// successful outcome), or "" when no filter is configured for this memory.
+func generateRepoMemoryFilterFilesStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) string {
if len(memory.AllowedExtensions) == 0 && len(memory.FileGlob) == 0 {
- return
+ return ""
}
+ filterStepID := repoMemoryFilterStepID(memory.ID)
allowedExtsJSON, _ := json.Marshal(memory.AllowedExtensions) //nolint:jsonmarshalignoredeerror // marshaling a string slice cannot fail
fmt.Fprintf(builder, " - name: Filter %s files (%s)\n", memoryLabel, memory.ID)
+ fmt.Fprintf(builder, " id: %s\n", filterStepID)
builder.WriteString(" if: always()\n")
fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
builder.WriteString(" env:\n")
@@ -407,19 +420,27 @@ func generateRepoMemoryFilterFilesStep(builder *strings.Builder, memory RepoMemo
builder.WriteString(" with:\n")
builder.WriteString(" script: |\n")
builder.WriteString(generateGitHubScriptWithRequire("memory_file_eligibility.cjs"))
+ return filterStepID
}
// generateRepoMemoryCustomValidationStep emits the optional custom-validation step and
// returns its step ID (used to gate the subsequent upload step), or "" when no custom
-// validation is configured for this memory.
-func generateRepoMemoryCustomValidationStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel string) string {
+// validation is configured for this memory. When filterStepID is non-empty, this step is
+// skipped unless the filter step completed successfully, so a filter failure (e.g. an
+// fs error while removing ineligible files) can never result in the unfiltered directory
+// being validated.
+func generateRepoMemoryCustomValidationStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel, filterStepID string) string {
if memory.Validation == nil {
return ""
}
validationStepID := repoMemoryValidationStepID(memory.ID)
fmt.Fprintf(builder, " - name: Validate %s domain content (%s)\n", memoryLabel, memory.ID)
fmt.Fprintf(builder, " id: %s\n", validationStepID)
- builder.WriteString(" if: always()\n")
+ if filterStepID != "" {
+ fmt.Fprintf(builder, " if: always() && steps.%s.outcome == 'success'\n", filterStepID)
+ } else {
+ builder.WriteString(" if: always()\n")
+ }
fmt.Fprintf(builder, " uses: %s\n", getActionPin("actions/github-script"))
builder.WriteString(" env:\n")
fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
@@ -438,23 +459,50 @@ func generateRepoMemoryCustomValidationStep(builder *strings.Builder, memory Rep
return validationStepID
}
+// repoMemoryUploadStepParams bundles the parameters for generateRepoMemoryUploadArtifactStep
+// (kept as a struct rather than individual parameters to stay within the repo's
+// function-parameter-count lint limit).
+type repoMemoryUploadStepParams struct {
+ memory RepoMemoryEntry
+ memoryDir string
+ memoryLabel string
+ sanitizedID string
+ prefix string
+ filterStepID string
+ validationStepID string
+ pinAction func(string) string
+}
+
// generateRepoMemoryUploadArtifactStep emits the step that uploads the repo-memory
-// directory as an artifact, gated on the custom-validation step's outcome when configured.
-func generateRepoMemoryUploadArtifactStep(builder *strings.Builder, memory RepoMemoryEntry, memoryDir, memoryLabel, sanitizedID, prefix, validationStepID string, pinAction func(string) string) {
- fmt.Fprintf(builder, " - name: Upload %s artifact (%s)\n", memoryLabel, memory.ID)
- if validationStepID != "" {
- fmt.Fprintf(builder, " if: always() && steps.%s.outcome == 'success'\n", validationStepID)
+// directory as an artifact, gated on the filter and custom-validation steps' outcomes
+// when configured, so a filter or validation failure can never result in an unfiltered
+// or unvalidated directory being uploaded.
+func generateRepoMemoryUploadArtifactStep(builder *strings.Builder, p repoMemoryUploadStepParams) {
+ fmt.Fprintf(builder, " - name: Upload %s artifact (%s)\n", p.memoryLabel, p.memory.ID)
+ var conditions []string
+ if p.filterStepID != "" {
+ conditions = append(conditions, fmt.Sprintf("steps.%s.outcome == 'success'", p.filterStepID))
+ }
+ if p.validationStepID != "" {
+ conditions = append(conditions, fmt.Sprintf("steps.%s.outcome == 'success'", p.validationStepID))
+ }
+ if len(conditions) > 0 {
+ fmt.Fprintf(builder, " if: always() && %s\n", strings.Join(conditions, " && "))
} else {
builder.WriteString(" if: always()\n")
}
- fmt.Fprintf(builder, " uses: %s\n", pinAction("actions/upload-artifact"))
+ fmt.Fprintf(builder, " uses: %s\n", p.pinAction("actions/upload-artifact"))
builder.WriteString(" with:\n")
- fmt.Fprintf(builder, " name: %srepo-memory-%s\n", prefix, sanitizedID)
- fmt.Fprintf(builder, " path: %s\n", memoryDir)
+ fmt.Fprintf(builder, " name: %srepo-memory-%s\n", p.prefix, p.sanitizedID)
+ fmt.Fprintf(builder, " path: %s\n", p.memoryDir)
builder.WriteString(" retention-days: 1\n")
builder.WriteString(" if-no-files-found: ignore\n")
}
+func repoMemoryFilterStepID(memoryID string) string {
+ return memoryValidationStepID("filter_repo_memory", memoryID)
+}
+
func repoMemoryValidationStepID(memoryID string) string {
return memoryValidationStepID("validate_repo_memory", memoryID)
}
diff --git a/pkg/workflow/repo_memory_test.go b/pkg/workflow/repo_memory_test.go
index 577bcb8a6fd..21d2506a5fc 100644
--- a/pkg/workflow/repo_memory_test.go
+++ b/pkg/workflow/repo_memory_test.go
@@ -321,6 +321,78 @@ func TestGenerateRepoMemoryArtifactUpload(t *testing.T) {
}
}
+// TestRepoMemoryFilterStepGatesUpload verifies that when allowed-extensions/file-glob
+// filtering is configured, the "Filter ... files" step has an id and the subsequent
+// custom-validation and upload-artifact steps require that step's outcome to be
+// 'success'. This ensures a filter-step failure (e.g. an fs error while removing
+// ineligible files) can never result in an unfiltered directory being validated or
+// uploaded — regression for a "fail open" review finding.
+func TestRepoMemoryFilterStepGatesUpload(t *testing.T) {
+ config := &RepoMemoryConfig{
+ Memories: []RepoMemoryEntry{
+ {
+ ID: "default",
+ BranchName: "memory/default",
+ AllowedExtensions: []string{".json"},
+ Validation: &MemoryValidationConfig{
+ Script: "// noop",
+ },
+ },
+ },
+ }
+ data := &WorkflowData{RepoMemoryConfig: config}
+
+ var builder strings.Builder
+ generateRepoMemoryArtifactUpload(&builder, data, getActionPin)
+ output := builder.String()
+
+ filterStepID := repoMemoryFilterStepID("default")
+ validationStepID := repoMemoryValidationStepID("default")
+
+ assert.Contains(t, output, "id: "+filterStepID, "Filter step must have an id")
+
+ filterPos := strings.Index(output, "id: "+filterStepID)
+ validationNamePos := strings.Index(output, "Validate repo-memory domain content (default)")
+ uploadNamePos := strings.Index(output, "Upload repo-memory artifact (default)")
+ require.Greater(t, filterPos, -1)
+ require.Greater(t, validationNamePos, -1)
+ require.Greater(t, uploadNamePos, -1)
+ assert.Less(t, filterPos, validationNamePos, "Filter step must appear before validation step")
+ assert.Less(t, validationNamePos, uploadNamePos, "Validation step must appear before upload step")
+
+ validationSection := output[validationNamePos:uploadNamePos]
+ assert.Contains(t, validationSection, "if: always() && steps."+filterStepID+".outcome == 'success'",
+ "Validation step must be gated on the filter step's success")
+
+ uploadSection := output[uploadNamePos:]
+ assert.Contains(t, uploadSection, "steps."+filterStepID+".outcome == 'success'",
+ "Upload step must be gated on the filter step's success")
+ assert.Contains(t, uploadSection, "steps."+validationStepID+".outcome == 'success'",
+ "Upload step must also be gated on the validation step's success")
+}
+
+// TestRepoMemoryNoFilterStepWhenNoFilterConfigured verifies that when no
+// allowed-extensions/file-glob is configured, no filter step id is emitted and the
+// upload step is not gated on a nonexistent filter step.
+func TestRepoMemoryNoFilterStepWhenNoFilterConfigured(t *testing.T) {
+ config := &RepoMemoryConfig{
+ Memories: []RepoMemoryEntry{
+ {
+ ID: "default",
+ BranchName: "memory/default",
+ },
+ },
+ }
+ data := &WorkflowData{RepoMemoryConfig: config}
+
+ var builder strings.Builder
+ generateRepoMemoryArtifactUpload(&builder, data, getActionPin)
+ output := builder.String()
+
+ assert.NotContains(t, output, "Filter repo-memory files (default)", "No filter step should be emitted")
+ assert.NotContains(t, output, repoMemoryFilterStepID("default"), "No filter step id should be referenced")
+}
+
// TestRepoMemoryPromptGeneration tests that prompt section is generated correctly
func TestRepoMemoryPromptGeneration(t *testing.T) {
config := &RepoMemoryConfig{
diff --git a/pkg/workflow/safe_outputs_config_generation.go b/pkg/workflow/safe_outputs_config_generation.go
index e6895691756..cda7a8694b0 100644
--- a/pkg/workflow/safe_outputs_config_generation.go
+++ b/pkg/workflow/safe_outputs_config_generation.go
@@ -37,205 +37,238 @@ func generateSafeOutputsConfig(data *WorkflowData) (string, error) {
safeOutputsConfigLog.Print("Generating safe outputs configuration for workflow")
safeOutputsConfig := make(map[string]any)
- engineManifestFiles, engineManifestPathPrefixes := getEngineAgentFileInfoFromWorkflowData(data)
-
- // Standard handler configs — sourced from handlerRegistry (same as GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG)
- for handlerName, builder := range handlerRegistry {
- if handlerCfg := builder(data.SafeOutputs); handlerCfg != nil {
- injectCurrentCheckoutPatchWorkspacePath(handlerName, handlerCfg, data)
- injectCheckoutMapping(handlerName, handlerCfg, data)
- excludeFiles := ParseStringArrayFromConfig(handlerCfg, "_protected_files_exclude", nil)
- // Strip the internal sentinel key used by the handler manager for compile-time
- // exclusion processing — it must not be forwarded to the runtime config.json.
- delete(handlerCfg, "_protected_files_exclude")
- if _, hasProtectedFiles := handlerCfg["protected_files"]; hasProtectedFiles {
- fullManifestFiles := getAllManifestFiles(engineManifestFiles...)
- fullPathPrefixes := getProtectedPathPrefixes(engineManifestPathPrefixes...)
- handlerCfg["protected_files"] = sliceutil.Exclude(fullManifestFiles, excludeFiles...)
- filteredPrefixes := sliceutil.Exclude(fullPathPrefixes, excludeFiles...)
- if len(filteredPrefixes) > 0 {
- handlerCfg["protected_path_prefixes"] = filteredPrefixes
- } else {
- delete(handlerCfg, "protected_path_prefixes")
- }
- // Compute which top-level dot-folder prefixes are excluded so the runtime
- // dot-folder check can skip them.
- if dotFolderExcludes := getDotFolderExcludes(excludeFiles); len(dotFolderExcludes) > 0 {
- handlerCfg["protected_dot_folder_excludes"] = dotFolderExcludes
- }
- }
- if data.SafeOutputs != nil && data.SafeOutputs.DataEnabled && isDataSchemaEnabledType(handlerName) {
- handlerCfg["data_enabled"] = true
- if data.SafeOutputs.NormalizedDataSchema != nil {
- handlerCfg["data_schema"] = data.SafeOutputs.NormalizedDataSchema
- } else if strings.TrimSpace(data.SafeOutputs.DataSchemaExpression) != "" {
- handlerCfg["data_schema"] = data.SafeOutputs.DataSchemaExpression
- }
- }
- safeOutputsConfig[handlerName] = handlerCfg
- }
- }
+ addStandardHandlerConfigs(safeOutputsConfig, data)
if handlerConfig := buildCommentMemoryHandlerConfig(data.CommentMemoryConfig, data.SafeOutputs.Footer); handlerConfig != nil {
safeOutputsConfig[commentMemoryHandlerKey] = handlerConfig
}
- // Safe-jobs configuration: custom output types that run as separate GitHub Actions jobs.
- // These are not standard handlers but must be in config.json so the ingestion step can
- // validate and route those output types.
- if len(data.SafeOutputs.Jobs) > 0 {
- safeOutputsConfigLog.Printf("Processing %d safe job configurations", len(data.SafeOutputs.Jobs))
- for jobName, jobConfig := range data.SafeOutputs.Jobs {
- safeOutputsConfigLog.Printf("Generating config for safe job: %s", jobName)
- safeJobConfig := map[string]any{}
- if jobConfig.Description != "" {
- safeJobConfig["description"] = jobConfig.Description
- }
- if jobConfig.Output != "" {
- safeJobConfig["output"] = jobConfig.Output
- }
- if jobConfig.Max > 0 {
- safeJobConfig["max"] = jobConfig.Max
- }
- if len(jobConfig.Inputs) > 0 {
- inputsConfig := make(map[string]any)
- for inputName, inputDef := range jobConfig.Inputs {
- inputConfig := map[string]any{
- "type": inputDef.Type,
- "description": inputDef.Description,
- "required": inputDef.Required,
- }
- if inputDef.Default != "" {
- inputConfig["default"] = inputDef.Default
- }
- if len(inputDef.Options) > 0 {
- inputConfig["options"] = inputDef.Options
- }
- inputsConfig[inputName] = inputConfig
- }
- safeJobConfig["inputs"] = inputsConfig
- }
- safeOutputsConfig[jobName] = safeJobConfig
- }
+ addSafeJobsConfig(safeOutputsConfig, data.SafeOutputs.Jobs)
+ addSafeScriptsConfig(safeOutputsConfig, data.SafeOutputs.Scripts)
+ if err := addSafeActionsConfig(safeOutputsConfig, data.SafeOutputs.Actions); err != nil {
+ return "", err
}
+ addMentionsConfig(safeOutputsConfig, data.SafeOutputs)
+ addPushRepoMemoryConfig(safeOutputsConfig, data.RepoMemoryConfig)
- // Safe-scripts configuration: script output types handled inline by the handler manager.
- if len(data.SafeOutputs.Scripts) > 0 {
- safeOutputsConfigLog.Printf("Processing %d safe script configurations", len(data.SafeOutputs.Scripts))
- for scriptName, scriptConfig := range data.SafeOutputs.Scripts {
- normalizedName := stringutil.NormalizeSafeOutputIdentifier(scriptName)
- safeOutputsConfigLog.Printf("Generating config for safe script: %s (normalized: %s)", scriptName, normalizedName)
- safeScriptConfigMap := map[string]any{}
- if scriptConfig.Description != "" {
- safeScriptConfigMap["description"] = scriptConfig.Description
+ if len(safeOutputsConfig) == 0 {
+ return "", nil
+ }
+ // The agent job never depends on the custom jobs listed in safe-outputs.needs (those
+ // dependencies are only wired onto the later safe_outputs handler job by
+ // buildSafeOutputsJobNeeds). Any templated expression referencing needs. for one of
+ // those jobs would therefore be unresolvable inside the agent job and trip an actionlint
+ // "undefined property" error. Neutralize such expressions here; the handler job's own copy
+ // of the config (GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG) is unaffected and keeps the real value.
+ sanitizeAgentSafeOutputsConfig(safeOutputsConfig, data.SafeOutputs.Needs)
+ configJSON, err := marshalSafeOutputsConfig(safeOutputsConfig)
+ if err != nil {
+ return "", fmt.Errorf("marshaling safe-outputs config: %w", err)
+ }
+ safeOutputsConfigLog.Printf("Safe outputs config generation complete: %d tool types configured", len(safeOutputsConfig))
+ return string(configJSON), nil
+}
+
+// addStandardHandlerConfigs adds config for every registered standard safe-output
+// handler (same source as GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG) to safeOutputsConfig.
+func addStandardHandlerConfigs(safeOutputsConfig map[string]any, data *WorkflowData) {
+ engineManifestFiles, engineManifestPathPrefixes := getEngineAgentFileInfoFromWorkflowData(data)
+
+ for handlerName, builder := range handlerRegistry {
+ handlerCfg := builder(data.SafeOutputs)
+ if handlerCfg == nil {
+ continue
+ }
+ injectCurrentCheckoutPatchWorkspacePath(handlerName, handlerCfg, data)
+ injectCheckoutMapping(handlerName, handlerCfg, data)
+ excludeFiles := ParseStringArrayFromConfig(handlerCfg, "_protected_files_exclude", nil)
+ // Strip the internal sentinel key used by the handler manager for compile-time
+ // exclusion processing — it must not be forwarded to the runtime config.json.
+ delete(handlerCfg, "_protected_files_exclude")
+ if _, hasProtectedFiles := handlerCfg["protected_files"]; hasProtectedFiles {
+ fullManifestFiles := getAllManifestFiles(engineManifestFiles...)
+ fullPathPrefixes := getProtectedPathPrefixes(engineManifestPathPrefixes...)
+ handlerCfg["protected_files"] = sliceutil.Exclude(fullManifestFiles, excludeFiles...)
+ filteredPrefixes := sliceutil.Exclude(fullPathPrefixes, excludeFiles...)
+ if len(filteredPrefixes) > 0 {
+ handlerCfg["protected_path_prefixes"] = filteredPrefixes
+ } else {
+ delete(handlerCfg, "protected_path_prefixes")
}
- if len(scriptConfig.Inputs) > 0 {
- inputsConfig := make(map[string]any)
- for inputName, inputDef := range scriptConfig.Inputs {
- inputConfig := map[string]any{
- "type": inputDef.Type,
- "description": inputDef.Description,
- "required": inputDef.Required,
- }
- if inputDef.Default != "" {
- inputConfig["default"] = inputDef.Default
- }
- if len(inputDef.Options) > 0 {
- inputConfig["options"] = inputDef.Options
- }
- inputsConfig[inputName] = inputConfig
- }
- safeScriptConfigMap["inputs"] = inputsConfig
+ // Compute which top-level dot-folder prefixes are excluded so the runtime
+ // dot-folder check can skip them.
+ if dotFolderExcludes := getDotFolderExcludes(excludeFiles); len(dotFolderExcludes) > 0 {
+ handlerCfg["protected_dot_folder_excludes"] = dotFolderExcludes
}
- safeOutputsConfig[normalizedName] = safeScriptConfigMap
}
- }
-
- // Safe-actions configuration: custom GitHub Actions exposed as safe output tools.
- // The normalized action names are added as config keys so both MCP server implementations
- // recognise them as enabled tools (the tool schema is already in tools.json via
- // tools_meta.json; the MCP server just needs to see the name in config.json).
- if len(data.SafeOutputs.Actions) > 0 {
- safeOutputsConfigLog.Printf("Processing %d safe action configurations", len(data.SafeOutputs.Actions))
- for actionName := range data.SafeOutputs.Actions {
- normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
- if _, exists := safeOutputsConfig[normalizedName]; exists {
- return "", fmt.Errorf(
- "safe-outputs action %q has a normalized name %q that conflicts with an existing safe outputs config entry; rename the action to avoid the conflict",
- actionName,
- normalizedName,
- )
+ if data.SafeOutputs != nil && data.SafeOutputs.DataEnabled && isDataSchemaEnabledType(handlerName) {
+ handlerCfg["data_enabled"] = true
+ if data.SafeOutputs.NormalizedDataSchema != nil {
+ handlerCfg["data_schema"] = data.SafeOutputs.NormalizedDataSchema
+ } else if strings.TrimSpace(data.SafeOutputs.DataSchemaExpression) != "" {
+ handlerCfg["data_schema"] = data.SafeOutputs.DataSchemaExpression
}
- safeOutputsConfigLog.Printf("Adding safe action to config: %s (normalized: %s)", actionName, normalizedName)
- safeOutputsConfig[normalizedName] = true
}
+ safeOutputsConfig[handlerName] = handlerCfg
}
+}
+// addMentionsConfig adds mentions and max-bot-mentions configuration (consumed by the
+// ingestion step, not by standard handlers) to safeOutputsConfig.
+func addMentionsConfig(safeOutputsConfig map[string]any, safeOutputs *SafeOutputsConfig) {
// Mentions configuration: controls which @mentions are allowed in AI output.
- // This is consumed by the ingestion step, not by standard handlers.
- if data.SafeOutputs.Mentions != nil {
- mentionsConfig := buildMentionsHandlerConfig(data.SafeOutputs.Mentions)
+ if safeOutputs.Mentions != nil {
+ mentionsConfig := buildMentionsHandlerConfig(safeOutputs.Mentions)
if len(mentionsConfig) > 0 {
safeOutputsConfig["mentions"] = mentionsConfig
}
}
// Max bot mentions: limits bot trigger references (e.g. "fixes #123") in AI output.
- // Consumed by the ingestion step as a global config knob.
// Store as integer when possible (matching original behavior), or as expression string.
- if data.SafeOutputs.MaxBotMentions != nil {
- v := *data.SafeOutputs.MaxBotMentions
- if n := templatableIntValue(data.SafeOutputs.MaxBotMentions); n > 0 {
+ if safeOutputs.MaxBotMentions != nil {
+ v := *safeOutputs.MaxBotMentions
+ if n := templatableIntValue(safeOutputs.MaxBotMentions); n > 0 {
safeOutputsConfig["max_bot_mentions"] = n
} else if strings.HasPrefix(v, "${{") {
safeOutputsConfig["max_bot_mentions"] = v
}
}
+}
- // Push-repo-memory configuration: enables the push_repo_memory MCP tool for early
- // size validation during the agent session.
- if data.RepoMemoryConfig != nil && len(data.RepoMemoryConfig.Memories) > 0 {
- var memories []map[string]any
- for _, memory := range data.RepoMemoryConfig.Memories {
- memoryConfig := map[string]any{
- "id": memory.ID,
- "dir": constants.TmpRepoMemoryDir + memory.ID,
- "max_file_size": memory.MaxFileSize,
- "max_patch_size": memory.MaxPatchSize,
- "max_file_count": memory.MaxFileCount,
- }
- if memory.FormatJSON {
- memoryConfig["format_json"] = true
- }
- if memory.Validation != nil {
- memoryConfig["validation"] = map[string]any{
- "script": memory.Validation.Script,
- "timeout": memoryValidationTimeoutSeconds(memory.Validation),
- }
- }
- memories = append(memories, memoryConfig)
+// addSafeJobsConfig adds safe-jobs configuration (custom output types that run as
+// separate GitHub Actions jobs) to safeOutputsConfig. These are not standard handlers
+// but must be in config.json so the ingestion step can validate and route those output types.
+func addSafeJobsConfig(safeOutputsConfig map[string]any, jobs map[string]*SafeJobConfig) {
+ if len(jobs) == 0 {
+ return
+ }
+ safeOutputsConfigLog.Printf("Processing %d safe job configurations", len(jobs))
+ for jobName, jobConfig := range jobs {
+ safeOutputsConfigLog.Printf("Generating config for safe job: %s", jobName)
+ safeJobConfig := map[string]any{}
+ if jobConfig.Description != "" {
+ safeJobConfig["description"] = jobConfig.Description
+ }
+ if jobConfig.Output != "" {
+ safeJobConfig["output"] = jobConfig.Output
}
- safeOutputsConfig["push_repo_memory"] = map[string]any{
- "memories": memories,
+ if jobConfig.Max > 0 {
+ safeJobConfig["max"] = jobConfig.Max
}
- safeOutputsConfigLog.Printf("Added push_repo_memory config with %d memory entries", len(data.RepoMemoryConfig.Memories))
+ if inputsConfig := buildSafeOutputInputsConfig(jobConfig.Inputs); inputsConfig != nil {
+ safeJobConfig["inputs"] = inputsConfig
+ }
+ safeOutputsConfig[jobName] = safeJobConfig
}
+}
- if len(safeOutputsConfig) == 0 {
- return "", nil
+// addSafeScriptsConfig adds safe-scripts configuration (script output types handled
+// inline by the handler manager) to safeOutputsConfig.
+func addSafeScriptsConfig(safeOutputsConfig map[string]any, scripts map[string]*SafeScriptConfig) {
+ if len(scripts) == 0 {
+ return
}
- // The agent job never depends on the custom jobs listed in safe-outputs.needs (those
- // dependencies are only wired onto the later safe_outputs handler job by
- // buildSafeOutputsJobNeeds). Any templated expression referencing needs. for one of
- // those jobs would therefore be unresolvable inside the agent job and trip an actionlint
- // "undefined property" error. Neutralize such expressions here; the handler job's own copy
- // of the config (GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG) is unaffected and keeps the real value.
- sanitizeAgentSafeOutputsConfig(safeOutputsConfig, data.SafeOutputs.Needs)
- configJSON, err := marshalSafeOutputsConfig(safeOutputsConfig)
- if err != nil {
- return "", fmt.Errorf("marshaling safe-outputs config: %w", err)
+ safeOutputsConfigLog.Printf("Processing %d safe script configurations", len(scripts))
+ for scriptName, scriptConfig := range scripts {
+ normalizedName := stringutil.NormalizeSafeOutputIdentifier(scriptName)
+ safeOutputsConfigLog.Printf("Generating config for safe script: %s (normalized: %s)", scriptName, normalizedName)
+ safeScriptConfigMap := map[string]any{}
+ if scriptConfig.Description != "" {
+ safeScriptConfigMap["description"] = scriptConfig.Description
+ }
+ if inputsConfig := buildSafeOutputInputsConfig(scriptConfig.Inputs); inputsConfig != nil {
+ safeScriptConfigMap["inputs"] = inputsConfig
+ }
+ safeOutputsConfig[normalizedName] = safeScriptConfigMap
}
- safeOutputsConfigLog.Printf("Safe outputs config generation complete: %d tool types configured", len(safeOutputsConfig))
- return string(configJSON), nil
+}
+
+// buildSafeOutputInputsConfig converts safe-job/safe-script input definitions into
+// the map[string]any shape expected in config.json, or nil when there are none.
+func buildSafeOutputInputsConfig(inputs map[string]*InputDefinition) map[string]any {
+ if len(inputs) == 0 {
+ return nil
+ }
+ inputsConfig := make(map[string]any)
+ for inputName, inputDef := range inputs {
+ inputConfig := map[string]any{
+ "type": inputDef.Type,
+ "description": inputDef.Description,
+ "required": inputDef.Required,
+ }
+ if inputDef.Default != "" {
+ inputConfig["default"] = inputDef.Default
+ }
+ if len(inputDef.Options) > 0 {
+ inputConfig["options"] = inputDef.Options
+ }
+ inputsConfig[inputName] = inputConfig
+ }
+ return inputsConfig
+}
+
+// addSafeActionsConfig adds safe-actions configuration (custom GitHub Actions exposed
+// as safe output tools) to safeOutputsConfig. The normalized action names are added as
+// config keys so both MCP server implementations recognise them as enabled tools (the
+// tool schema is already in tools.json via tools_meta.json; the MCP server just needs
+// to see the name in config.json).
+func addSafeActionsConfig(safeOutputsConfig map[string]any, actions map[string]*SafeOutputActionConfig) error {
+ if len(actions) == 0 {
+ return nil
+ }
+ safeOutputsConfigLog.Printf("Processing %d safe action configurations", len(actions))
+ for actionName := range actions {
+ normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
+ if _, exists := safeOutputsConfig[normalizedName]; exists {
+ return fmt.Errorf(
+ "safe-outputs action %q has a normalized name %q that conflicts with an existing safe outputs config entry; rename the action to avoid the conflict",
+ actionName,
+ normalizedName,
+ )
+ }
+ safeOutputsConfigLog.Printf("Adding safe action to config: %s (normalized: %s)", actionName, normalizedName)
+ safeOutputsConfig[normalizedName] = true
+ }
+ return nil
+}
+
+// addPushRepoMemoryConfig adds push_repo_memory configuration (enables the
+// push_repo_memory MCP tool for early size/eligibility validation during the agent
+// session) to safeOutputsConfig, when repo-memory is configured.
+func addPushRepoMemoryConfig(safeOutputsConfig map[string]any, repoMemoryConfig *RepoMemoryConfig) {
+ if repoMemoryConfig == nil || len(repoMemoryConfig.Memories) == 0 {
+ return
+ }
+ var memories []map[string]any
+ for _, memory := range repoMemoryConfig.Memories {
+ memoryConfig := map[string]any{
+ "id": memory.ID,
+ "dir": constants.TmpRepoMemoryDir + memory.ID,
+ "max_file_size": memory.MaxFileSize,
+ "max_patch_size": memory.MaxPatchSize,
+ "max_file_count": memory.MaxFileCount,
+ }
+ if len(memory.AllowedExtensions) > 0 {
+ memoryConfig["allowed_extensions"] = memory.AllowedExtensions
+ }
+ if len(memory.FileGlob) > 0 {
+ memoryConfig["file_glob"] = strings.Join(memory.FileGlob, " ")
+ }
+ if memory.FormatJSON {
+ memoryConfig["format_json"] = true
+ }
+ if memory.Validation != nil {
+ memoryConfig["validation"] = map[string]any{
+ "script": memory.Validation.Script,
+ "timeout": memoryValidationTimeoutSeconds(memory.Validation),
+ }
+ }
+ memories = append(memories, memoryConfig)
+ }
+ safeOutputsConfig["push_repo_memory"] = map[string]any{
+ "memories": memories,
+ }
+ safeOutputsConfigLog.Printf("Added push_repo_memory config with %d memory entries", len(repoMemoryConfig.Memories))
}
func getEngineAgentFileInfoFromWorkflowData(data *WorkflowData) (manifestFiles []string, pathPrefixes []string) {
@@ -261,30 +294,11 @@ func getEngineAgentFileInfoFromWorkflowData(data *WorkflowData) (manifestFiles [
return provider.GetAgentManifestFiles(), provider.GetAgentManifestPathPrefixes()
}
-// generateCustomJobToolDefinition creates an MCP tool definition for a custom safe-output job.
-// Returns a map representing the tool definition in MCP format with name, description, and inputSchema.
-func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) map[string]any {
- safeOutputsConfigLog.Printf("Generating tool definition for custom job: %s", jobName)
-
- description := jobConfig.Description
- if description == "" {
- description = fmt.Sprintf("Execute the %s custom job", jobName)
- }
-
- inputSchema := map[string]any{
- "type": "object",
- "properties": make(map[string]any),
- "additionalProperties": false,
- }
-
+// populateCustomJobToolProperties fills properties (an MCP tool inputSchema.properties
+// map) from the given safe-job input definitions, returning the names of required inputs.
+func populateCustomJobToolProperties(properties map[string]any, inputs map[string]*InputDefinition) []string {
var requiredFields []string
- properties, ok := inputSchema["properties"].(map[string]any)
- if !ok {
- properties = make(map[string]any)
- inputSchema["properties"] = properties
- }
-
- for inputName, inputDef := range jobConfig.Inputs {
+ for inputName, inputDef := range inputs {
property := map[string]any{}
if inputDef.Description != "" {
@@ -315,6 +329,31 @@ func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) m
properties[inputName] = property
}
+ return requiredFields
+}
+
+// generateCustomJobToolDefinition creates an MCP tool definition for a custom safe-output job.
+// Returns a map representing the tool definition in MCP format with name, description, and inputSchema.
+func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) map[string]any {
+ safeOutputsConfigLog.Printf("Generating tool definition for custom job: %s", jobName)
+
+ description := jobConfig.Description
+ if description == "" {
+ description = fmt.Sprintf("Execute the %s custom job", jobName)
+ }
+
+ inputSchema := map[string]any{
+ "type": "object",
+ "properties": make(map[string]any),
+ "additionalProperties": false,
+ }
+
+ properties, ok := inputSchema["properties"].(map[string]any)
+ if !ok {
+ properties = make(map[string]any)
+ inputSchema["properties"] = properties
+ }
+ requiredFields := populateCustomJobToolProperties(properties, jobConfig.Inputs)
if len(requiredFields) > 0 {
sort.Strings(requiredFields)
diff --git a/pkg/workflow/safe_outputs_config_generation_test.go b/pkg/workflow/safe_outputs_config_generation_test.go
index 1d4fa7c3ae2..649dd01db8a 100644
--- a/pkg/workflow/safe_outputs_config_generation_test.go
+++ b/pkg/workflow/safe_outputs_config_generation_test.go
@@ -1124,6 +1124,60 @@ func TestGenerateSafeOutputsConfigRepoMemory(t *testing.T) {
assert.InDelta(t, float64(2048), mem1["max_file_size"], 0.0001, "Second memory max_file_size should match")
}
+// TestGenerateSafeOutputsConfigRepoMemoryFilters tests that generateSafeOutputsConfig
+// passes allowed-extensions/file-glob through to the push_repo_memory preflight config,
+// so the MCP tool preflight can apply the same eligibility filtering as the agent-side
+// filter step and the push job (regression: preflight was previously unfiltered).
+func TestGenerateSafeOutputsConfigRepoMemoryFilters(t *testing.T) {
+ data := &WorkflowData{
+ SafeOutputs: &SafeOutputsConfig{},
+ RepoMemoryConfig: &RepoMemoryConfig{
+ Memories: []RepoMemoryEntry{
+ {
+ ID: "default",
+ MaxFileSize: 5120,
+ MaxPatchSize: 20480,
+ MaxFileCount: 50,
+ AllowedExtensions: []string{".json", ".md"},
+ FileGlob: []string{"*.json", "metrics/**"},
+ },
+ {
+ ID: "notes",
+ MaxFileSize: 2048,
+ MaxPatchSize: 8192,
+ MaxFileCount: 20,
+ },
+ },
+ },
+ }
+
+ result, err := generateSafeOutputsConfig(data)
+ require.NoError(t, err, "generateSafeOutputsConfig should not return an error")
+
+ var parsed map[string]any
+ require.NoError(t, json.Unmarshal([]byte(result), &parsed), "Result must be valid JSON")
+
+ pushRepoMemory, ok := parsed["push_repo_memory"].(map[string]any)
+ require.True(t, ok, "Expected push_repo_memory key in config")
+ memories, ok := pushRepoMemory["memories"].([]any)
+ require.True(t, ok, "Expected memories to be an array")
+ require.Len(t, memories, 2, "Expected 2 memory entries")
+
+ mem0, ok := memories[0].(map[string]any)
+ require.True(t, ok, "First memory entry should be a map")
+ allowedExts, ok := mem0["allowed_extensions"].([]any)
+ require.True(t, ok, "First memory should carry allowed_extensions")
+ assert.Equal(t, []any{".json", ".md"}, allowedExts, "allowed_extensions should be passed through")
+ assert.Equal(t, "*.json metrics/**", mem0["file_glob"], "file_glob should be joined with spaces")
+
+ mem1, ok := memories[1].(map[string]any)
+ require.True(t, ok, "Second memory entry should be a map")
+ _, hasAllowedExts := mem1["allowed_extensions"]
+ assert.False(t, hasAllowedExts, "Second memory should not carry allowed_extensions when unset")
+ _, hasFileGlob := mem1["file_glob"]
+ assert.False(t, hasFileGlob, "Second memory should not carry file_glob when unset")
+}
+
// TestGenerateSafeOutputsConfigNoRepoMemory tests that push_repo_memory is absent
// from the config when RepoMemoryConfig is not present.
func TestGenerateSafeOutputsConfigNoRepoMemory(t *testing.T) {
From 91e55b227099925ff3078606f247688e60e7c7d8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 06:30:03 +0000
Subject: [PATCH 6/7] repo-memory: add real behavioral test coverage for
push-job eligibility scan
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
actions/setup/js/push_repo_memory.test.cjs | 68 ++++++++++++++++++++++
pkg/workflow/repo_memory_test.go | 32 +++++++++-
2 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/actions/setup/js/push_repo_memory.test.cjs b/actions/setup/js/push_repo_memory.test.cjs
index b96e865b1e0..7114cc8828a 100644
--- a/actions/setup/js/push_repo_memory.test.cjs
+++ b/actions/setup/js/push_repo_memory.test.cjs
@@ -1654,6 +1654,74 @@ describe("push_repo_memory.cjs - allowed-extensions persistence filter (regressi
expect(filterIdx).toBeLessThan(formatIdx);
expect(filterIdx).toBeLessThan(validateIdx);
});
+
+ describe("behavioral: scanDirectory's inline eligibility check against real fixtures", () => {
+ // push_repo_memory.cjs cannot be driven end-to-end through main() in this test
+ // environment: it needs a successful `git fetch`/checkout before scanDirectory
+ // runs, and vi.doMock cannot intercept CJS require() calls for git_helpers.cjs
+ // here (see "should propagate git fetch authentication failure..." above, which
+ // documents the same limitation). So this test drives the *actual* eligibility
+ // helpers push_repo_memory.cjs's scanDirectory calls inline (isMemoryFileEligible,
+ // compileFileGlobPatterns, both imported for real, not mocked) against a real
+ // artifact directory on disk, mirroring the exact scan loop in the script.
+ const nodeFs = require("fs");
+ const nodePath = require("path");
+ const os = require("os");
+
+ let artifactDir;
+
+ beforeEach(() => {
+ artifactDir = nodeFs.mkdtempSync(nodePath.join(os.tmpdir(), "push-repo-memory-scan-"));
+ });
+
+ afterEach(() => {
+ nodeFs.rmSync(artifactDir, { recursive: true, force: true });
+ });
+
+ /** Mirrors the scanDirectory() walk in push_repo_memory.cjs, real fs + real eligibility helpers. */
+ async function scanEligibleFiles(dirPath, allowedExtensions, fileGlobFilter) {
+ const { compileFileGlobPatterns, isMemoryFileEligible } = await import("./memory_file_eligibility.cjs");
+ const { compiledPatterns } = compileFileGlobPatterns(fileGlobFilter);
+ const kept = [];
+ const filteredOut = [];
+ (function walk(dir, relativePath = "") {
+ for (const entry of nodeFs.readdirSync(dir, { withFileTypes: true })) {
+ const fullPath = nodePath.join(dir, entry.name);
+ const relativeFilePath = relativePath ? nodePath.join(relativePath, entry.name) : entry.name;
+ if (entry.isDirectory()) {
+ walk(fullPath, relativeFilePath);
+ } else if (entry.isFile()) {
+ const eligibility = isMemoryFileEligible(relativeFilePath, allowedExtensions, compiledPatterns);
+ if (eligibility.eligible) {
+ kept.push(relativeFilePath.replace(/\\/g, "/"));
+ } else {
+ filteredOut.push(relativeFilePath.replace(/\\/g, "/"));
+ }
+ }
+ }
+ })(dirPath);
+ return { kept, filteredOut };
+ }
+
+ it("filters out a disallowed-extension file and keeps the eligible one (notes.json.new regression)", async () => {
+ nodeFs.writeFileSync(nodePath.join(artifactDir, "notes.json"), "{}");
+ nodeFs.writeFileSync(nodePath.join(artifactDir, "notes.json.new"), "{}");
+
+ const { kept, filteredOut } = await scanEligibleFiles(artifactDir, [".json"], "");
+
+ expect(kept).toEqual(["notes.json"]);
+ expect(filteredOut).toEqual(["notes.json.new"]);
+ });
+
+ it("filters out every file when none are eligible, leaving nothing to copy", async () => {
+ nodeFs.writeFileSync(nodePath.join(artifactDir, "notes.json.new"), "{}");
+
+ const { kept, filteredOut } = await scanEligibleFiles(artifactDir, [".json"], "");
+
+ expect(kept).toEqual([]);
+ expect(filteredOut).toEqual(["notes.json.new"]);
+ });
+ });
});
// ──────────────────────────────────────────────────────────────────────────────
diff --git a/pkg/workflow/repo_memory_test.go b/pkg/workflow/repo_memory_test.go
index 21d2506a5fc..f9d3e0d678f 100644
--- a/pkg/workflow/repo_memory_test.go
+++ b/pkg/workflow/repo_memory_test.go
@@ -334,6 +334,7 @@ func TestRepoMemoryFilterStepGatesUpload(t *testing.T) {
ID: "default",
BranchName: "memory/default",
AllowedExtensions: []string{".json"},
+ FileGlob: []string{"*.json", "notes/*.md"},
Validation: &MemoryValidationConfig{
Script: "// noop",
},
@@ -351,15 +352,25 @@ func TestRepoMemoryFilterStepGatesUpload(t *testing.T) {
assert.Contains(t, output, "id: "+filterStepID, "Filter step must have an id")
- filterPos := strings.Index(output, "id: "+filterStepID)
+ sanitizeNamePos := strings.Index(output, "Sanitize repo-memory filenames (default)")
+ filterNamePos := strings.Index(output, "Filter repo-memory files (default)")
+ filterIDPos := strings.Index(output, "id: "+filterStepID)
validationNamePos := strings.Index(output, "Validate repo-memory domain content (default)")
uploadNamePos := strings.Index(output, "Upload repo-memory artifact (default)")
- require.Greater(t, filterPos, -1)
+ require.Greater(t, sanitizeNamePos, -1)
+ require.Greater(t, filterNamePos, -1)
require.Greater(t, validationNamePos, -1)
require.Greater(t, uploadNamePos, -1)
- assert.Less(t, filterPos, validationNamePos, "Filter step must appear before validation step")
+ assert.Less(t, sanitizeNamePos, filterNamePos, "Sanitize step must appear before filter step")
+ assert.Less(t, filterNamePos, validationNamePos, "Filter step must appear before validation step")
assert.Less(t, validationNamePos, uploadNamePos, "Validation step must appear before upload step")
+ filterSection := output[filterNamePos:validationNamePos]
+ assert.Contains(t, filterSection, "id: "+filterStepID, "Filter step id must appear within the filter step")
+ assert.Contains(t, filterSection, `ALLOWED_EXTENSIONS: '[".json"]'`, "Filter step must set ALLOWED_EXTENSIONS to the JSON-encoded extensions list")
+ assert.Contains(t, filterSection, `FILE_GLOB_FILTER: "*.json notes/*.md"`, "Filter step must set FILE_GLOB_FILTER to the space-joined glob patterns")
+ assert.Greater(t, filterIDPos, filterNamePos, "Filter step id must appear after its name")
+
validationSection := output[validationNamePos:uploadNamePos]
assert.Contains(t, validationSection, "if: always() && steps."+filterStepID+".outcome == 'success'",
"Validation step must be gated on the filter step's success")
@@ -371,6 +382,21 @@ func TestRepoMemoryFilterStepGatesUpload(t *testing.T) {
"Upload step must also be gated on the validation step's success")
}
+// TestRepoMemoryFilterStepEmptyBothFieldsSkipsFilter verifies that the "no filter step"
+// branch is only reachable when a caller explicitly leaves both AllowedExtensions and
+// FileGlob empty — i.e. it's not accidentally triggered by a nil vs. empty-slice distinction.
+func TestRepoMemoryFilterStepEmptyBothFieldsSkipsFilter(t *testing.T) {
+ var builder strings.Builder
+ stepID := generateRepoMemoryFilterFilesStep(&builder, RepoMemoryEntry{
+ ID: "default",
+ AllowedExtensions: []string{},
+ FileGlob: []string{},
+ }, "/tmp/gh-aw/repo-memory/default", "repo-memory")
+
+ assert.Empty(t, stepID, "No filter step id should be returned when both fields are empty slices")
+ assert.Empty(t, builder.String(), "No filter step should be emitted when both fields are empty slices")
+}
+
// TestRepoMemoryNoFilterStepWhenNoFilterConfigured verifies that when no
// allowed-extensions/file-glob is configured, no filter step id is emitted and the
// upload step is not gated on a nonexistent filter step.
From e52731a208b217ad3b85b7b2cbd595c59b7e6682 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 3 Sep 2026 08:49:23 +0000
Subject: [PATCH 7/7] docs: fix stale slashless file-glob depth semantics after
matchSubfolderRoot removal
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
docs/src/content/docs/reference/repo-memory.md | 6 +++---
pkg/workflow/repo_memory_validation.go | 5 +++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/docs/src/content/docs/reference/repo-memory.md b/docs/src/content/docs/reference/repo-memory.md
index 6191ea36f3c..44b2869e738 100644
--- a/docs/src/content/docs/reference/repo-memory.md
+++ b/docs/src/content/docs/reference/repo-memory.md
@@ -48,11 +48,11 @@ tools:
**File Glob Matching Rules**:
- Patterns are matched against the **relative path** within the artifact directory — do **not** include the branch name.
-- **Slashless patterns** (no `/` in the pattern, e.g. `*.json`, `*.md`) match files at the root of a single memory subfolder — **depth 1 only**. They do _not_ match files at the artifact root (depth 0) or deeper than one subfolder level (depth 2+).
+- **Slashless patterns** (no `/` in the pattern, e.g. `*.json`, `*.md`) are matched against the full relative path, so a single `*` only matches files at the **artifact root (depth 0)**. They do _not_ match files inside subfolders (depth 1+) — use a pattern containing `/` (e.g. `**/*.json`) for that.
- **Patterns containing `/`** (e.g. `metrics/**`, `data/*.csv`) are matched against the full relative path from the artifact root and work as standard glob expressions.
- **Absolute paths** (patterns starting with `/`) are **not supported** and are rejected at compile time and runtime.
-Example: with the default filter `["*.json", "*.md"]`, the file `discussion-task-miner/processed-discussions.json` is persisted (depth 1 ✓), but `processed-discussions.json` (depth 0) and `discussion-task-miner/archive/old.json` (depth 2) are not.
+Example: with the default filter `["*.json", "*.md"]`, the root-level file `processed-discussions.json` is persisted (depth 0 ✓), but `discussion-task-miner/processed-discussions.json` (depth 1) is not — use `["**/*.json", "**/*.md"]` to also match files nested in subfolders.
## Multiple Configurations
@@ -69,7 +69,7 @@ tools:
---
```
-Mounts at `/tmp/gh-aw/repo-memory-{id}/` during workflow execution. The required `id` determines the folder name, and `branch-name` defaults to `{branch-prefix}/{id}` with `memory` as the default prefix. Files are stored inside the branch under that branch-name path. File globs always match the relative path within the artifact directory, so never include the branch name; slashless patterns such as `*.json` match only the root of a memory subfolder (depth 1).
+Mounts at `/tmp/gh-aw/repo-memory-{id}/` during workflow execution. The required `id` determines the folder name, and `branch-name` defaults to `{branch-prefix}/{id}` with `memory` as the default prefix. Files are stored inside the branch under that branch-name path. File globs always match the relative path within the artifact directory, so never include the branch name; slashless patterns such as `*.json` match only files at the artifact root (depth 0).
## Behavior
diff --git a/pkg/workflow/repo_memory_validation.go b/pkg/workflow/repo_memory_validation.go
index 47cea565f63..6defe5c88ae 100644
--- a/pkg/workflow/repo_memory_validation.go
+++ b/pkg/workflow/repo_memory_validation.go
@@ -71,8 +71,9 @@ func validateNoDuplicateMemoryIDs(memories []RepoMemoryEntry) error {
// validateFileGlobPatterns validates file-glob patterns for a repo-memory entry.
//
-// Patterns are evaluated relative to the memory subfolder root (depth 1 from the artifact root).
-// Slashless patterns such as "*.json" match files at the root of any single memory subfolder.
+// Patterns are evaluated against the full relative path from the artifact root.
+// Slashless patterns such as "*.json" match only files at the artifact root (depth 0),
+// since a single "*" does not cross "/"; use "**/*.json" to also match nested files.
// Patterns containing "/" match against the full relative path from the artifact root.
//
// Rejected patterns: