From 2d7ba91b11c9b3112b76af50b43e66ff9878dd77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:56:30 +0000 Subject: [PATCH 1/5] Initial plan From 66e47263e891fb0a1fe8992e1291fda6398c774c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:03:40 +0000 Subject: [PATCH 2/5] Initial analysis: Identify duplication patterns in safe outputs config Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- .../github-remote-mcp-auth-test.lock.yml | 2 +- specs/artifacts.md | 247 ++---------------- 2 files changed, 17 insertions(+), 232 deletions(-) diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml index 5a8aa61b60..aa6c5b2888 100644 --- a/.github/workflows/github-remote-mcp-auth-test.lock.yml +++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml @@ -156,7 +156,7 @@ jobs: env: TOKEN_CHECK: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} if: env.TOKEN_CHECK != '' - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const determineAutomaticLockdown = require('/tmp/gh-aw/actions/determine_automatic_lockdown.cjs'); diff --git a/specs/artifacts.md b/specs/artifacts.md index b04ceff524..c9f074a2ad 100644 --- a/specs/artifacts.md +++ b/specs/artifacts.md @@ -22,37 +22,37 @@ This section provides an overview of artifacts organized by job name, with dupli - `agent-artifacts` - **Paths**: `/tmp/gh-aw/agent-stdio.log`, `/tmp/gh-aw/aw-prompts/prompt.txt`, `/tmp/gh-aw/aw.patch`, `/tmp/gh-aw/aw_info.json`, `/tmp/gh-aw/mcp-logs/`, `/tmp/gh-aw/safe-inputs/logs/`, `/tmp/gh-aw/sandbox/firewall/logs/` - - **Used in**: 78 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-custom-error-patterns.md, example-permissions-warning.md, example-workflow-analyzer.md, firewall.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, metrics-collector.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt-custom-config.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 75 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-custom-error-patterns.md, example-workflow-analyzer.md, firewall.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, metrics-collector.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt-custom-config.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md - `agent-output` - **Paths**: `${{ env.GH_AW_AGENT_OUTPUT }}` - - **Used in**: 73 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 71 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md - `agent_outputs` - **Paths**: `/tmp/gh-aw/mcp-config/logs/`, `/tmp/gh-aw/redacted-urls.log`, `/tmp/gh-aw/sandbox/agent/logs/` - - **Used in**: 65 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-custom-error-patterns.md, example-permissions-warning.md, firewall.md, glossary-maintainer.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, metrics-collector.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-srt-custom-config.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 62 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-custom-error-patterns.md, firewall.md, glossary-maintainer.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, metrics-collector.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-srt-custom-config.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md - `cache-memory` - **Paths**: `/tmp/gh-aw/cache-memory` - - **Used in**: 31 workflow(s) - ci-coach.md, ci-doctor.md, cloclo.md, copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, grumpy-reviewer.md, issue-template-optimizer.md, mcp-inspector.md, org-health-report.md, pdf-summary.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, scout.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, weekly-issue-summary.md + - **Used in**: 29 workflow(s) - ci-coach.md, ci-doctor.md, cloclo.md, copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, grumpy-reviewer.md, issue-template-optimizer.md, mcp-inspector.md, pdf-summary.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, scout.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, weekly-issue-summary.md - `cache-memory-focus-areas` - **Paths**: `/tmp/gh-aw/cache-memory-focus-areas` - **Used in**: 1 workflow(s) - repository-quality-improver.md - `data-charts` - **Paths**: `/tmp/gh-aw/python/charts/*.png` - - **Used in**: 10 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, github-mcp-structural-analysis.md, org-health-report.md, python-data-charts.md, stale-repo-identifier.md, weekly-issue-summary.md + - **Used in**: 8 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, github-mcp-structural-analysis.md, python-data-charts.md, stale-repo-identifier.md, weekly-issue-summary.md - `playwright-debug-logs-${{ github.run_id }}` - **Paths**: `/tmp/gh-aw/playwright-debug-logs/` - **Used in**: 1 workflow(s) - smoke-copilot-playwright.md - `python-source-and-data` - **Paths**: `/tmp/gh-aw/python/*.py`, `/tmp/gh-aw/python/data/*` - - **Used in**: 10 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, github-mcp-structural-analysis.md, org-health-report.md, python-data-charts.md, stale-repo-identifier.md, weekly-issue-summary.md + - **Used in**: 8 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, github-mcp-structural-analysis.md, python-data-charts.md, stale-repo-identifier.md, weekly-issue-summary.md - `repo-memory-default` - **Paths**: `/tmp/gh-aw/repo-memory/default` - **Used in**: 8 workflow(s) - agent-performance-analyzer.md, copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, deep-report.md, metrics-collector.md, security-compliance.md, workflow-health-manager.md - `safe-output` - **Paths**: `${{ env.GH_AW_SAFE_OUTPUTS }}` - - **Used in**: 73 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 71 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md - `safe-outputs-assets` - **Paths**: `/tmp/gh-aw/safeoutputs/assets/` - - **Used in**: 14 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, org-health-report.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md + - **Used in**: 12 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md - `trending-charts` - **Paths**: `/tmp/gh-aw/python/charts/*.png` - **Used in**: 2 workflow(s) - portfolio-analyst.md, stale-repo-identifier.md @@ -72,7 +72,7 @@ This section provides an overview of artifacts organized by job name, with dupli - `agent-output` - **Download paths**: `/tmp/gh-aw/safeoutputs/` - - **Used in**: 73 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 71 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md ### Job: `detection` @@ -80,16 +80,16 @@ This section provides an overview of artifacts organized by job name, with dupli - `threat-detection.log` - **Paths**: `/tmp/gh-aw/threat-detection/detection.log` - - **Used in**: 72 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 70 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md **Artifacts Downloaded:** - `agent-artifacts` - **Download paths**: `/tmp/gh-aw/threat-detection/` - - **Used in**: 72 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 70 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md - `agent-output` - **Download paths**: `/tmp/gh-aw/threat-detection/` - - **Used in**: 72 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 70 workflow(s) - agent-performance-analyzer.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-choice-test.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, notion-issue-summary.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, smoke-srt.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md ### Job: `generate-sbom` @@ -132,7 +132,7 @@ This section provides an overview of artifacts organized by job name, with dupli - **Used in**: 17 workflow(s) - changeset.md, ci-coach.md, cloclo.md, craft.md, dictation-prompt.md, github-mcp-tools-report.md, glossary-maintainer.md, hourly-ci-cleaner.md, issue-template-optimizer.md, layout-spec-maintainer.md, mergefest.md, playground-snapshots-refresh.md, poem-bot.md, q.md, slide-deck-maintainer.md, technical-doc-writer.md, tidy.md - `agent-output` - **Download paths**: `/tmp/gh-aw/safeoutputs/` - - **Used in**: 70 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, org-health-report.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md + - **Used in**: 68 workflow(s) - agent-performance-analyzer.md, ai-moderator.md, archie.md, artifacts-summary.md, blog-auditor.md, brave.md, breaking-change-checker.md, campaign-generator.md, changeset.md, ci-coach.md, ci-doctor.md, cli-consistency-checker.md, cloclo.md, commit-changes-analyzer.md, copilot-pr-merged-report.md, copilot-pr-nlp-analysis.md, craft.md, daily-copilot-token-report.md, daily-fact.md, daily-file-diet.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, dependabot-go-checker.md, dev-hawk.md, dev.md, dictation-prompt.md, example-workflow-analyzer.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, go-pattern-detector.md, grumpy-reviewer.md, hourly-ci-cleaner.md, issue-classifier.md, issue-template-optimizer.md, issue-triage-agent.md, layout-spec-maintainer.md, mcp-inspector.md, mergefest.md, pdf-summary.md, plan.md, playground-org-project-update-issue.md, playground-snapshots-refresh.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, release.md, repo-tree-map.md, repository-quality-improver.md, research.md, scout.md, security-compliance.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, tidy.md, typist.md, video-analyzer.md, weekly-issue-summary.md, workflow-generator.md, workflow-health-manager.md ### Job: `super_linter` @@ -156,7 +156,7 @@ This section provides an overview of artifacts organized by job name, with dupli - `cache-memory` - **Download paths**: `/tmp/gh-aw/cache-memory` - - **Used in**: 31 workflow(s) - ci-coach.md, ci-doctor.md, cloclo.md, copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, grumpy-reviewer.md, issue-template-optimizer.md, mcp-inspector.md, org-health-report.md, pdf-summary.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, scout.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, weekly-issue-summary.md + - **Used in**: 29 workflow(s) - ci-coach.md, ci-doctor.md, cloclo.md, copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, github-mcp-tools-report.md, glossary-maintainer.md, go-fan.md, grumpy-reviewer.md, issue-template-optimizer.md, mcp-inspector.md, pdf-summary.md, poem-bot.md, portfolio-analyst.md, pr-nitpick-reviewer.md, python-data-charts.md, q.md, scout.md, slide-deck-maintainer.md, smoke-copilot-playwright.md, smoke-detector.md, stale-repo-identifier.md, super-linter.md, technical-doc-writer.md, weekly-issue-summary.md - `cache-memory-focus-areas` - **Download paths**: `/tmp/gh-aw/cache-memory-focus-areas` - **Used in**: 1 workflow(s) - repository-quality-improver.md @@ -167,10 +167,10 @@ This section provides an overview of artifacts organized by job name, with dupli - `agent-output` - **Download paths**: `/tmp/gh-aw/safeoutputs/` - - **Used in**: 14 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, org-health-report.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md + - **Used in**: 12 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md - `safe-outputs-assets` - **Download paths**: `/tmp/gh-aw/safeoutputs/assets/` - - **Used in**: 14 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-issues-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, org-health-report.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md + - **Used in**: 12 workflow(s) - copilot-pr-nlp-analysis.md, daily-copilot-token-report.md, daily-news.md, daily-repo-chronicle.md, deep-report.md, github-mcp-structural-analysis.md, poem-bot.md, portfolio-analyst.md, python-data-charts.md, stale-repo-identifier.md, technical-doc-writer.md, weekly-issue-summary.md ## Workflows @@ -1580,104 +1580,6 @@ This section provides an overview of artifacts organized by job name, with dupli **Downloads:** -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [agent detection] - -### daily-issues-report.md - -#### Job: `agent` - -**Uploads:** - -- **Artifact**: `data-charts` - - **Upload paths**: - - `/tmp/gh-aw/python/charts/*.png` - -- **Artifact**: `python-source-and-data` - - **Upload paths**: - - `/tmp/gh-aw/python/*.py` - - `/tmp/gh-aw/python/data/*` - -- **Artifact**: `safe-output` - - **Upload paths**: - - `${{ env.GH_AW_SAFE_OUTPUTS }}` - -- **Artifact**: `agent-output` - - **Upload paths**: - - `${{ env.GH_AW_AGENT_OUTPUT }}` - -- **Artifact**: `agent_outputs` - - **Upload paths**: - - `/tmp/gh-aw/mcp-config/logs/` - - `/tmp/gh-aw/redacted-urls.log` - -- **Artifact**: `cache-memory` - - **Upload paths**: - - `/tmp/gh-aw/cache-memory` - -- **Artifact**: `safe-outputs-assets` - - **Upload paths**: - - `/tmp/gh-aw/safeoutputs/assets/` - -- **Artifact**: `agent-artifacts` - - **Upload paths**: - - `/tmp/gh-aw/aw-prompts/prompt.txt` - - `/tmp/gh-aw/aw_info.json` - - `/tmp/gh-aw/mcp-logs/` - - `/tmp/gh-aw/sandbox/firewall/logs/` - - `/tmp/gh-aw/agent-stdio.log` - -#### Job: `conclusion` - -**Downloads:** - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [activation agent detection safe_outputs update_cache_memory upload_assets] - -#### Job: `detection` - -**Uploads:** - -- **Artifact**: `threat-detection.log` - - **Upload paths**: - - `/tmp/gh-aw/threat-detection/detection.log` - -**Downloads:** - -- **Artifact**: `agent-artifacts` (by name) - - **Download path**: `/tmp/gh-aw/threat-detection/` - - **Depends on jobs**: [agent] - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/threat-detection/` - - **Depends on jobs**: [agent] - -#### Job: `safe_outputs` - -**Downloads:** - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [agent detection] - -#### Job: `update_cache_memory` - -**Downloads:** - -- **Artifact**: `cache-memory` (by name) - - **Download path**: `/tmp/gh-aw/cache-memory` - - **Depends on jobs**: [agent detection] - -#### Job: `upload_assets` - -**Downloads:** - -- **Artifact**: `safe-outputs-assets` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/assets/` - - **Depends on jobs**: [agent detection] - - **Artifact**: `agent-output` (by name) - **Download path**: `/tmp/gh-aw/safeoutputs/` - **Depends on jobs**: [agent detection] @@ -2246,25 +2148,6 @@ This section provides an overview of artifacts organized by job name, with dupli **Uploads:** -- **Artifact**: `agent_outputs` - - **Upload paths**: - - `/tmp/gh-aw/sandbox/agent/logs/` - - `/tmp/gh-aw/redacted-urls.log` - -- **Artifact**: `agent-artifacts` - - **Upload paths**: - - `/tmp/gh-aw/aw-prompts/prompt.txt` - - `/tmp/gh-aw/aw_info.json` - - `/tmp/gh-aw/mcp-logs/` - - `/tmp/gh-aw/sandbox/firewall/logs/` - - `/tmp/gh-aw/agent-stdio.log` - -### example-permissions-warning.md - -#### Job: `agent` - -**Uploads:** - - **Artifact**: `agent_outputs` - **Upload paths**: - `/tmp/gh-aw/sandbox/agent/logs/` @@ -3367,104 +3250,6 @@ This section provides an overview of artifacts organized by job name, with dupli - **Download path**: `/tmp/gh-aw/safe-jobs/` - **Depends on jobs**: [agent detection] -### org-health-report.md - -#### Job: `agent` - -**Uploads:** - -- **Artifact**: `data-charts` - - **Upload paths**: - - `/tmp/gh-aw/python/charts/*.png` - -- **Artifact**: `python-source-and-data` - - **Upload paths**: - - `/tmp/gh-aw/python/*.py` - - `/tmp/gh-aw/python/data/*` - -- **Artifact**: `safe-output` - - **Upload paths**: - - `${{ env.GH_AW_SAFE_OUTPUTS }}` - -- **Artifact**: `agent-output` - - **Upload paths**: - - `${{ env.GH_AW_AGENT_OUTPUT }}` - -- **Artifact**: `agent_outputs` - - **Upload paths**: - - `/tmp/gh-aw/sandbox/agent/logs/` - - `/tmp/gh-aw/redacted-urls.log` - -- **Artifact**: `cache-memory` - - **Upload paths**: - - `/tmp/gh-aw/cache-memory` - -- **Artifact**: `safe-outputs-assets` - - **Upload paths**: - - `/tmp/gh-aw/safeoutputs/assets/` - -- **Artifact**: `agent-artifacts` - - **Upload paths**: - - `/tmp/gh-aw/aw-prompts/prompt.txt` - - `/tmp/gh-aw/aw_info.json` - - `/tmp/gh-aw/mcp-logs/` - - `/tmp/gh-aw/sandbox/firewall/logs/` - - `/tmp/gh-aw/agent-stdio.log` - -#### Job: `conclusion` - -**Downloads:** - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [activation agent detection safe_outputs update_cache_memory upload_assets] - -#### Job: `detection` - -**Uploads:** - -- **Artifact**: `threat-detection.log` - - **Upload paths**: - - `/tmp/gh-aw/threat-detection/detection.log` - -**Downloads:** - -- **Artifact**: `agent-artifacts` (by name) - - **Download path**: `/tmp/gh-aw/threat-detection/` - - **Depends on jobs**: [agent] - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/threat-detection/` - - **Depends on jobs**: [agent] - -#### Job: `safe_outputs` - -**Downloads:** - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [agent detection] - -#### Job: `update_cache_memory` - -**Downloads:** - -- **Artifact**: `cache-memory` (by name) - - **Download path**: `/tmp/gh-aw/cache-memory` - - **Depends on jobs**: [agent detection] - -#### Job: `upload_assets` - -**Downloads:** - -- **Artifact**: `safe-outputs-assets` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/assets/` - - **Depends on jobs**: [agent detection] - -- **Artifact**: `agent-output` (by name) - - **Download path**: `/tmp/gh-aw/safeoutputs/` - - **Depends on jobs**: [agent detection] - ### pdf-summary.md #### Job: `agent` From 22cc13691280fa5adcf007319784786eab8c9705 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:14:18 +0000 Subject: [PATCH 3/5] Refactor: Replace 460 lines of duplication with generic config builder - Created safe_output_config_builder.go with reflection-based approach - buildHandlerConfig() automatically extracts fields from structs - buildSafeOutputConfigs() processes all safe output types - Handles embedded structs (BaseSafeOutputConfig, SafeOutputTargetConfig, etc.) - Reduces compiler_safe_outputs_config.go from 476 to 17 lines - All existing tests pass without modifications Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/workflow/compiler_safe_outputs_config.go | 451 +----------------- pkg/workflow/safe_output_config_builder.go | 258 ++++++++++ .../safe_output_config_builder_test.go | 312 ++++++++++++ 3 files changed, 572 insertions(+), 449 deletions(-) create mode 100644 pkg/workflow/safe_output_config_builder.go create mode 100644 pkg/workflow/safe_output_config_builder_test.go diff --git a/pkg/workflow/compiler_safe_outputs_config.go b/pkg/workflow/compiler_safe_outputs_config.go index 605160e0f0..8a07a5702e 100644 --- a/pkg/workflow/compiler_safe_outputs_config.go +++ b/pkg/workflow/compiler_safe_outputs_config.go @@ -10,455 +10,8 @@ func (c *Compiler) addHandlerManagerConfigEnvVar(steps *[]string, data *Workflow return } - config := make(map[string]map[string]any) - - // Add config for each enabled safe output type with their options - // Presence in config = enabled, so no need for "enabled": true field - if data.SafeOutputs.CreateIssues != nil { - cfg := data.SafeOutputs.CreateIssues - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if len(cfg.AllowedLabels) > 0 { - handlerConfig["allowed_labels"] = cfg.AllowedLabels - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - if cfg.Expires > 0 { - handlerConfig["expires"] = cfg.Expires - } - // Add labels, title_prefix to config - if len(cfg.Labels) > 0 { - handlerConfig["labels"] = cfg.Labels - } - if cfg.TitlePrefix != "" { - handlerConfig["title_prefix"] = cfg.TitlePrefix - } - // Add assignees to config - if len(cfg.Assignees) > 0 { - handlerConfig["assignees"] = cfg.Assignees - } - // Add target-repo to config - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - config["create_issue"] = handlerConfig - } - - if data.SafeOutputs.AddComments != nil { - cfg := data.SafeOutputs.AddComments - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if cfg.HideOlderComments { - handlerConfig["hide_older_comments"] = true - } - // Add target-repo to config - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["add_comment"] = handlerConfig - } - - if data.SafeOutputs.CreateDiscussions != nil { - cfg := data.SafeOutputs.CreateDiscussions - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Category != "" { - handlerConfig["category"] = cfg.Category - } - if cfg.TitlePrefix != "" { - handlerConfig["title_prefix"] = cfg.TitlePrefix - } - if len(cfg.Labels) > 0 { - handlerConfig["labels"] = cfg.Labels - } - if len(cfg.AllowedLabels) > 0 { - handlerConfig["allowed_labels"] = cfg.AllowedLabels - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - if cfg.CloseOlderDiscussions { - handlerConfig["close_older_discussions"] = true - } - if cfg.Expires > 0 { - handlerConfig["expires"] = cfg.Expires - } - // Add target-repo to config - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - config["create_discussion"] = handlerConfig - } - - if data.SafeOutputs.CloseIssues != nil { - cfg := data.SafeOutputs.CloseIssues - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if len(cfg.RequiredLabels) > 0 { - handlerConfig["required_labels"] = cfg.RequiredLabels - } - if cfg.RequiredTitlePrefix != "" { - handlerConfig["required_title_prefix"] = cfg.RequiredTitlePrefix - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["close_issue"] = handlerConfig - } - - if data.SafeOutputs.CloseDiscussions != nil { - cfg := data.SafeOutputs.CloseDiscussions - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if len(cfg.RequiredLabels) > 0 { - handlerConfig["required_labels"] = cfg.RequiredLabels - } - if cfg.RequiredTitlePrefix != "" { - handlerConfig["required_title_prefix"] = cfg.RequiredTitlePrefix - } - if cfg.RequiredCategory != "" { - handlerConfig["required_category"] = cfg.RequiredCategory - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["close_discussion"] = handlerConfig - } - - if data.SafeOutputs.AddLabels != nil { - cfg := data.SafeOutputs.AddLabels - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if len(cfg.Allowed) > 0 { - handlerConfig["allowed"] = cfg.Allowed - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["add_labels"] = handlerConfig - } - - if data.SafeOutputs.UpdateIssues != nil { - cfg := data.SafeOutputs.UpdateIssues - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - // Boolean pointer fields indicate which fields can be updated - if cfg.Status != nil { - handlerConfig["allow_status"] = true - } - if cfg.Title != nil { - handlerConfig["allow_title"] = true - } - if cfg.Body != nil { - handlerConfig["allow_body"] = true - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["update_issue"] = handlerConfig - } - - if data.SafeOutputs.UpdateDiscussions != nil { - cfg := data.SafeOutputs.UpdateDiscussions - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - // Boolean pointer fields indicate which fields can be updated - if cfg.Title != nil { - handlerConfig["allow_title"] = true - } - if cfg.Body != nil { - handlerConfig["allow_body"] = true - } - if cfg.Labels != nil { - handlerConfig["allow_labels"] = true - } - if len(cfg.AllowedLabels) > 0 { - handlerConfig["allowed_labels"] = cfg.AllowedLabels - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["update_discussion"] = handlerConfig - } - - if data.SafeOutputs.LinkSubIssue != nil { - cfg := data.SafeOutputs.LinkSubIssue - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if len(cfg.ParentRequiredLabels) > 0 { - handlerConfig["parent_required_labels"] = cfg.ParentRequiredLabels - } - if cfg.ParentTitlePrefix != "" { - handlerConfig["parent_title_prefix"] = cfg.ParentTitlePrefix - } - if len(cfg.SubRequiredLabels) > 0 { - handlerConfig["sub_required_labels"] = cfg.SubRequiredLabels - } - if cfg.SubTitlePrefix != "" { - handlerConfig["sub_title_prefix"] = cfg.SubTitlePrefix - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["link_sub_issue"] = handlerConfig - } - - if data.SafeOutputs.UpdateRelease != nil { - cfg := data.SafeOutputs.UpdateRelease - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - config["update_release"] = handlerConfig - } - - if data.SafeOutputs.CreatePullRequestReviewComments != nil { - cfg := data.SafeOutputs.CreatePullRequestReviewComments - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Side != "" { - handlerConfig["side"] = cfg.Side - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["create_pull_request_review_comment"] = handlerConfig - } - - if data.SafeOutputs.CreatePullRequests != nil { - cfg := data.SafeOutputs.CreatePullRequests - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.TitlePrefix != "" { - handlerConfig["title_prefix"] = cfg.TitlePrefix - } - if len(cfg.Labels) > 0 { - handlerConfig["labels"] = cfg.Labels - } - if cfg.Draft != nil { - handlerConfig["draft"] = *cfg.Draft - } - if cfg.IfNoChanges != "" { - handlerConfig["if_no_changes"] = cfg.IfNoChanges - } - if cfg.AllowEmpty { - handlerConfig["allow_empty"] = cfg.AllowEmpty - } - if cfg.Expires > 0 { - handlerConfig["expires"] = cfg.Expires - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - // Add base branch (required for git operations) - handlerConfig["base_branch"] = "${{ github.ref_name }}" - // Add max patch size - maxPatchSize := 1024 // default 1024 KB - if data.SafeOutputs.MaximumPatchSize > 0 { - maxPatchSize = data.SafeOutputs.MaximumPatchSize - } - handlerConfig["max_patch_size"] = maxPatchSize - config["create_pull_request"] = handlerConfig - } - - if data.SafeOutputs.PushToPullRequestBranch != nil { - cfg := data.SafeOutputs.PushToPullRequestBranch - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if cfg.TitlePrefix != "" { - handlerConfig["title_prefix"] = cfg.TitlePrefix - } - if len(cfg.Labels) > 0 { - handlerConfig["labels"] = cfg.Labels - } - if cfg.IfNoChanges != "" { - handlerConfig["if_no_changes"] = cfg.IfNoChanges - } - if cfg.CommitTitleSuffix != "" { - handlerConfig["commit_title_suffix"] = cfg.CommitTitleSuffix - } - // Add base branch (required for git operations) - handlerConfig["base_branch"] = "${{ github.ref_name }}" - // Add max patch size - maxPatchSize := 1024 // default 1024 KB - if data.SafeOutputs.MaximumPatchSize > 0 { - maxPatchSize = data.SafeOutputs.MaximumPatchSize - } - handlerConfig["max_patch_size"] = maxPatchSize - config["push_to_pull_request_branch"] = handlerConfig - } - - if data.SafeOutputs.UpdatePullRequests != nil { - cfg := data.SafeOutputs.UpdatePullRequests - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - // Boolean pointer fields indicate which fields can be updated - // Default to true if not specified (backward compatibility) - if cfg.Title != nil { - handlerConfig["allow_title"] = *cfg.Title - } else { - handlerConfig["allow_title"] = true - } - if cfg.Body != nil { - handlerConfig["allow_body"] = *cfg.Body - } else { - handlerConfig["allow_body"] = true - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["update_pull_request"] = handlerConfig - } - - if data.SafeOutputs.ClosePullRequests != nil { - cfg := data.SafeOutputs.ClosePullRequests - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.Target != "" { - handlerConfig["target"] = cfg.Target - } - if len(cfg.RequiredLabels) > 0 { - handlerConfig["required_labels"] = cfg.RequiredLabels - } - if cfg.RequiredTitlePrefix != "" { - handlerConfig["required_title_prefix"] = cfg.RequiredTitlePrefix - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["close_pull_request"] = handlerConfig - } - - if data.SafeOutputs.HideComment != nil { - cfg := data.SafeOutputs.HideComment - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if len(cfg.AllowedReasons) > 0 { - handlerConfig["allowed_reasons"] = cfg.AllowedReasons - } - if cfg.TargetRepoSlug != "" { - handlerConfig["target-repo"] = cfg.TargetRepoSlug - } - if len(cfg.AllowedRepos) > 0 { - handlerConfig["allowed_repos"] = cfg.AllowedRepos - } - config["hide_comment"] = handlerConfig - } - - if data.SafeOutputs.DispatchWorkflow != nil { - cfg := data.SafeOutputs.DispatchWorkflow - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if len(cfg.Workflows) > 0 { - handlerConfig["workflows"] = cfg.Workflows - } - config["dispatch_workflow"] = handlerConfig - } - - if data.SafeOutputs.CreateProjectStatusUpdates != nil { - cfg := data.SafeOutputs.CreateProjectStatusUpdates - handlerConfig := make(map[string]any) - if cfg.Max > 0 { - handlerConfig["max"] = cfg.Max - } - if cfg.GitHubToken != "" { - handlerConfig["github-token"] = cfg.GitHubToken - } - config["create_project_status_update"] = handlerConfig - } + // Use the new generic builder instead of manual duplication + config := buildSafeOutputConfigs(data.SafeOutputs) // Only add the env var if there are handlers to configure if len(config) > 0 { diff --git a/pkg/workflow/safe_output_config_builder.go b/pkg/workflow/safe_output_config_builder.go new file mode 100644 index 0000000000..142a93f4d6 --- /dev/null +++ b/pkg/workflow/safe_output_config_builder.go @@ -0,0 +1,258 @@ +package workflow + +import ( + "reflect" + + "github.com/githubnext/gh-aw/pkg/logger" +) + +var safeOutputConfigBuilderLog = logger.New("workflow:safe_output_config_builder") + +// fieldMapping defines how struct field names map to handler config keys +var fieldMapping = map[string]string{ + "Max": "max", + "GitHubToken": "github-token", + "TitlePrefix": "title_prefix", + "Labels": "labels", + "AllowedLabels": "allowed_labels", + "Assignees": "assignees", + "TargetRepoSlug": "target-repo", + "AllowedRepos": "allowed_repos", + "Expires": "expires", + "Target": "target", + "HideOlderComments": "hide_older_comments", + "Category": "category", + "CloseOlderDiscussions": "close_older_discussions", + "RequiredLabels": "required_labels", + "RequiredTitlePrefix": "required_title_prefix", + "RequiredCategory": "required_category", + "Allowed": "allowed", + "Status": "allow_status", + "Title": "allow_title", + "Body": "allow_body", + "ParentRequiredLabels": "parent_required_labels", + "ParentTitlePrefix": "parent_title_prefix", + "SubRequiredLabels": "sub_required_labels", + "SubTitlePrefix": "sub_title_prefix", + "Side": "side", + "Draft": "draft", + "IfNoChanges": "if_no_changes", + "AllowEmpty": "allow_empty", + "CommitTitleSuffix": "commit_title_suffix", + "AllowedReasons": "allowed_reasons", + "Workflows": "workflows", + "Reviewers": "reviewers", + "DefaultAgent": "default_agent", + "Discussion": "discussion", +} + +// buildHandlerConfig builds a handler configuration map from a config struct using reflection. +// It automatically handles common patterns like: +// - Skipping zero values (empty strings, 0, nil slices, nil pointers) +// - Converting boolean pointers to their presence (for allow_* fields) +// - Mapping Go struct field names to handler config keys +// - Processing embedded structs recursively +func buildHandlerConfig(configStruct any) map[string]any { + handlerConfig := make(map[string]any) + + v := reflect.ValueOf(configStruct) + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return handlerConfig + } + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + safeOutputConfigBuilderLog.Printf("Expected struct, got %v", v.Kind()) + return handlerConfig + } + + buildHandlerConfigRecursive(v, handlerConfig) + return handlerConfig +} + +// buildHandlerConfigRecursive processes struct fields recursively, handling embedded structs +func buildHandlerConfigRecursive(v reflect.Value, handlerConfig map[string]any) { + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := t.Field(i) + + // Skip unexported fields + if !fieldType.IsExported() { + continue + } + + // Handle embedded structs by recursing into them + if fieldType.Anonymous { + if field.Kind() == reflect.Struct { + buildHandlerConfigRecursive(field, handlerConfig) + } + continue + } + + // Get the field name and look up the mapping + fieldName := fieldType.Name + configKey, exists := fieldMapping[fieldName] + if !exists { + // If no mapping exists, skip this field + continue + } + + // Handle different field types + switch field.Kind() { + case reflect.String: + str := field.String() + if str != "" { + handlerConfig[configKey] = str + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + intVal := field.Int() + if intVal > 0 { + handlerConfig[configKey] = intVal + } + + case reflect.Bool: + boolVal := field.Bool() + if boolVal { + handlerConfig[configKey] = true + } + + case reflect.Slice: + if !field.IsNil() && field.Len() > 0 { + // Convert slice to []any for JSON marshaling + slice := make([]any, field.Len()) + for j := 0; j < field.Len(); j++ { + slice[j] = field.Index(j).Interface() + } + handlerConfig[configKey] = slice + } + + case reflect.Ptr: + if !field.IsNil() { + // For pointer fields, check the type they point to + elem := field.Elem() + switch elem.Kind() { + case reflect.Bool: + // Boolean pointers indicate "allow_*" fields + // For these, we just set the key to true if the pointer exists + handlerConfig[configKey] = true + default: + // For other pointer types, use their underlying value + handlerConfig[configKey] = elem.Interface() + } + } + + default: + safeOutputConfigBuilderLog.Printf("Unhandled field type %v for field %s", field.Kind(), fieldName) + } + } +} + +// safeOutputRegistry maps safe output types to their handler names +type safeOutputHandler struct { + fieldName string + handlerName string + customizer func(config any, handlerConfig map[string]any) // Optional customizer for special cases +} + +var safeOutputHandlers = []safeOutputHandler{ + {fieldName: "CreateIssues", handlerName: "create_issue"}, + {fieldName: "AddComments", handlerName: "add_comment"}, + {fieldName: "CreateDiscussions", handlerName: "create_discussion"}, + {fieldName: "CloseIssues", handlerName: "close_issue"}, + {fieldName: "CloseDiscussions", handlerName: "close_discussion"}, + {fieldName: "AddLabels", handlerName: "add_labels"}, + {fieldName: "UpdateIssues", handlerName: "update_issue"}, + {fieldName: "UpdateDiscussions", handlerName: "update_discussion"}, + {fieldName: "LinkSubIssue", handlerName: "link_sub_issue"}, + {fieldName: "UpdateRelease", handlerName: "update_release"}, + {fieldName: "CreatePullRequestReviewComments", handlerName: "create_pull_request_review_comment"}, + { + fieldName: "CreatePullRequests", + handlerName: "create_pull_request", + customizer: func(config any, handlerConfig map[string]any) { + // Add base branch for git operations + handlerConfig["base_branch"] = "${{ github.ref_name }}" + }, + }, + { + fieldName: "PushToPullRequestBranch", + handlerName: "push_to_pull_request_branch", + customizer: func(config any, handlerConfig map[string]any) { + // Add base branch for git operations + handlerConfig["base_branch"] = "${{ github.ref_name }}" + }, + }, + {fieldName: "UpdatePullRequests", handlerName: "update_pull_request", customizer: updatePullRequestCustomizer}, + {fieldName: "ClosePullRequests", handlerName: "close_pull_request"}, + {fieldName: "HideComment", handlerName: "hide_comment"}, + {fieldName: "DispatchWorkflow", handlerName: "dispatch_workflow"}, + {fieldName: "CreateProjectStatusUpdates", handlerName: "create_project_status_update"}, +} + +// updatePullRequestCustomizer handles special defaulting logic for update_pull_request +func updatePullRequestCustomizer(config any, handlerConfig map[string]any) { + // For update_pull_request, default to true if not specified + if _, exists := handlerConfig["allow_title"]; !exists { + handlerConfig["allow_title"] = true + } + if _, exists := handlerConfig["allow_body"]; !exists { + handlerConfig["allow_body"] = true + } +} + +// buildSafeOutputConfigs builds handler configuration for all safe output types using reflection. +// This replaces the massive duplication in addHandlerManagerConfigEnvVar. +func buildSafeOutputConfigs(safeOutputs *SafeOutputsConfig) map[string]map[string]any { + config := make(map[string]map[string]any) + + if safeOutputs == nil { + return config + } + + v := reflect.ValueOf(safeOutputs).Elem() + + // Process each registered handler + for _, handler := range safeOutputHandlers { + field := v.FieldByName(handler.fieldName) + if !field.IsValid() || field.IsNil() { + continue + } + + // Build the handler config using reflection + handlerConfig := buildHandlerConfig(field.Interface()) + + // Apply any custom logic + if handler.customizer != nil { + handler.customizer(field.Interface(), handlerConfig) + } + + // Only add if there are actual config values + if len(handlerConfig) > 0 { + config[handler.handlerName] = handlerConfig + } + } + + // Handle max_patch_size for PR-related handlers + if safeOutputs.MaximumPatchSize > 0 { + for _, handlerName := range []string{"create_pull_request", "push_to_pull_request_branch"} { + if cfg, exists := config[handlerName]; exists { + cfg["max_patch_size"] = safeOutputs.MaximumPatchSize + } + } + } else if safeOutputs.CreatePullRequests != nil || safeOutputs.PushToPullRequestBranch != nil { + // Add default max_patch_size + defaultSize := 1024 + for _, handlerName := range []string{"create_pull_request", "push_to_pull_request_branch"} { + if cfg, exists := config[handlerName]; exists { + cfg["max_patch_size"] = defaultSize + } + } + } + + return config +} diff --git a/pkg/workflow/safe_output_config_builder_test.go b/pkg/workflow/safe_output_config_builder_test.go new file mode 100644 index 0000000000..880ea60e7b --- /dev/null +++ b/pkg/workflow/safe_output_config_builder_test.go @@ -0,0 +1,312 @@ +package workflow + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildHandlerConfig(t *testing.T) { + tests := []struct { + name string + input any + expected map[string]any + }{ + { + name: "CreateIssuesConfig with all fields", + input: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 5, + }, + TitlePrefix: "[AI] ", + Labels: []string{"bug", "ai"}, + AllowedLabels: []string{"bug", "feature"}, + Assignees: []string{"user1", "user2"}, + TargetRepoSlug: "owner/repo", + AllowedRepos: []string{"org/repo1", "org/repo2"}, + Expires: 72, + }, + expected: map[string]any{ + "max": int64(5), + "title_prefix": "[AI] ", + "labels": []any{"bug", "ai"}, + "allowed_labels": []any{"bug", "feature"}, + "assignees": []any{"user1", "user2"}, + "target-repo": "owner/repo", + "allowed_repos": []any{"org/repo1", "org/repo2"}, + "expires": int64(72), + }, + }, + { + name: "AddCommentsConfig with target and hide older", + input: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 3, + }, + Target: "issue", + HideOlderComments: true, + TargetRepoSlug: "owner/repo", + AllowedRepos: []string{"owner/repo2"}, + }, + expected: map[string]any{ + "max": int64(3), + "target": "issue", + "hide_older_comments": true, + "target-repo": "owner/repo", + "allowed_repos": []any{"owner/repo2"}, + }, + }, + { + name: "Config with zero values omitted", + input: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 0, // Should be omitted + }, + TitlePrefix: "", // Should be omitted + Labels: []string{}, // Should be omitted + }, + expected: map[string]any{}, + }, + { + name: "UpdateIssuesConfig with boolean pointers", + input: &UpdateIssuesConfig{ + UpdateEntityConfig: UpdateEntityConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 1, + }, + SafeOutputTargetConfig: SafeOutputTargetConfig{ + Target: "issue", + }, + }, + Status: testBoolPtr(true), + Title: testBoolPtr(true), + Body: testBoolPtr(true), + }, + expected: map[string]any{ + "max": int64(1), + "target": "issue", + "allow_status": true, + "allow_title": true, + "allow_body": true, + }, + }, + { + name: "CloseEntityConfig with required fields", + input: &CloseEntityConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 10, + }, + SafeOutputTargetConfig: SafeOutputTargetConfig{ + Target: "issue", + }, + SafeOutputFilterConfig: SafeOutputFilterConfig{ + RequiredLabels: []string{"stale"}, + RequiredTitlePrefix: "[OLD]", + }, + }, + expected: map[string]any{ + "max": int64(10), + "target": "issue", + "required_labels": []any{"stale"}, + "required_title_prefix": "[OLD]", + }, + }, + { + name: "Nil pointer returns empty config", + input: (*CreateIssuesConfig)(nil), + expected: map[string]any{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := buildHandlerConfig(tt.input) + assert.Equal(t, tt.expected, result, "Handler config should match expected") + }) + } +} + +func TestBuildSafeOutputConfigs(t *testing.T) { + tests := []struct { + name string + safeOutputs *SafeOutputsConfig + expectedKeys []string + validateField func(t *testing.T, config map[string]map[string]any) + }{ + { + name: "Single handler - CreateIssues", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 5, + }, + TitlePrefix: "[AI] ", + AllowedLabels: []string{"bug"}, + }, + }, + expectedKeys: []string{"create_issue"}, + validateField: func(t *testing.T, config map[string]map[string]any) { + issueConfig, exists := config["create_issue"] + require.True(t, exists, "create_issue should exist") + assert.Equal(t, int64(5), issueConfig["max"]) + assert.Equal(t, "[AI] ", issueConfig["title_prefix"]) + assert.Equal(t, []any{"bug"}, issueConfig["allowed_labels"]) + }, + }, + { + name: "Multiple handlers", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 5, + }, + }, + AddComments: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 3, + }, + Target: "issue", + }, + AddLabels: &AddLabelsConfig{ + Allowed: []string{"bug", "feature"}, + }, + }, + expectedKeys: []string{"create_issue", "add_comment", "add_labels"}, + validateField: func(t *testing.T, config map[string]map[string]any) { + assert.Len(t, config, 3, "Should have 3 handlers") + + issueConfig := config["create_issue"] + assert.Equal(t, int64(5), issueConfig["max"]) + + commentConfig := config["add_comment"] + assert.Equal(t, int64(3), commentConfig["max"]) + assert.Equal(t, "issue", commentConfig["target"]) + + labelConfig := config["add_labels"] + assert.Equal(t, []any{"bug", "feature"}, labelConfig["allowed"]) + }, + }, + { + name: "CreatePullRequests with base_branch customization", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 1, + }, + TitlePrefix: "[PR] ", + }, + }, + expectedKeys: []string{"create_pull_request"}, + validateField: func(t *testing.T, config map[string]map[string]any) { + prConfig, exists := config["create_pull_request"] + require.True(t, exists) + assert.Equal(t, "${{ github.ref_name }}", prConfig["base_branch"], "Should add base_branch") + assert.Equal(t, 1024, prConfig["max_patch_size"], "Should add default max_patch_size") + }, + }, + { + name: "UpdatePullRequests with default allow fields", + safeOutputs: &SafeOutputsConfig{ + UpdatePullRequests: &UpdatePullRequestsConfig{ + UpdateEntityConfig: UpdateEntityConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 1, + }, + SafeOutputTargetConfig: SafeOutputTargetConfig{ + Target: "pr", + }, + }, + }, + }, + expectedKeys: []string{"update_pull_request"}, + validateField: func(t *testing.T, config map[string]map[string]any) { + prConfig, exists := config["update_pull_request"] + require.True(t, exists) + assert.Equal(t, true, prConfig["allow_title"], "Should default to true") + assert.Equal(t, true, prConfig["allow_body"], "Should default to true") + }, + }, + { + name: "Custom MaximumPatchSize", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 1, + }, + }, + MaximumPatchSize: 2048, + }, + expectedKeys: []string{"create_pull_request"}, + validateField: func(t *testing.T, config map[string]map[string]any) { + prConfig := config["create_pull_request"] + assert.Equal(t, 2048, prConfig["max_patch_size"], "Should use custom max_patch_size") + }, + }, + { + name: "Nil SafeOutputsConfig returns empty", + safeOutputs: nil, + expectedKeys: []string{}, + validateField: func(t *testing.T, config map[string]map[string]any) { + assert.Empty(t, config) + }, + }, + { + name: "Empty configs are excluded", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + // All zero values + }, + }, + expectedKeys: []string{}, + validateField: func(t *testing.T, config map[string]map[string]any) { + assert.Empty(t, config, "Empty configs should not be included") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := buildSafeOutputConfigs(tt.safeOutputs) + + // Check expected keys + for _, key := range tt.expectedKeys { + assert.Contains(t, result, key, "Should contain key %s", key) + } + + // Additional validation + if tt.validateField != nil { + tt.validateField(t, result) + } + }) + } +} + +func TestBuildHandlerConfig_EdgeCases(t *testing.T) { + t.Run("Non-struct input", func(t *testing.T) { + result := buildHandlerConfig("not a struct") + assert.Empty(t, result, "Should return empty for non-struct") + }) + + t.Run("Struct with no mapped fields", func(t *testing.T) { + type UnknownConfig struct { + UnmappedField string + } + result := buildHandlerConfig(&UnknownConfig{UnmappedField: "value"}) + assert.Empty(t, result, "Should skip unmapped fields") + }) + + t.Run("Struct with embedded BaseSafeOutputConfig", func(t *testing.T) { + config := &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 10, + GitHubToken: "token-value", + }, + TitlePrefix: "[Test]", + } + result := buildHandlerConfig(config) + assert.Equal(t, int64(10), result["max"], "Should extract Max from embedded struct") + assert.Equal(t, "token-value", result["github-token"], "Should extract GitHubToken from embedded struct") + assert.Equal(t, "[Test]", result["title_prefix"], "Should extract direct fields") + }) +} From 7e9841dc1a9b1069a60a4019ef533bb96cb66fea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:20:48 +0000 Subject: [PATCH 4/5] Fix: Include empty safe output configs to indicate handler is enabled The presence of a non-nil config pointer indicates the handler should be enabled, even if all fields are zero-values. This matches the original behavior where an empty config like `create-issue: {}` would still enable the handler. Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- pkg/workflow/safe_output_config_builder.go | 6 ++---- pkg/workflow/safe_output_config_builder_test.go | 8 +++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/workflow/safe_output_config_builder.go b/pkg/workflow/safe_output_config_builder.go index 142a93f4d6..5f2fbbd01a 100644 --- a/pkg/workflow/safe_output_config_builder.go +++ b/pkg/workflow/safe_output_config_builder.go @@ -231,10 +231,8 @@ func buildSafeOutputConfigs(safeOutputs *SafeOutputsConfig) map[string]map[strin handler.customizer(field.Interface(), handlerConfig) } - // Only add if there are actual config values - if len(handlerConfig) > 0 { - config[handler.handlerName] = handlerConfig - } + // Add even if empty - the presence of the config pointer indicates the handler is enabled + config[handler.handlerName] = handlerConfig } // Handle max_patch_size for PR-related handlers diff --git a/pkg/workflow/safe_output_config_builder_test.go b/pkg/workflow/safe_output_config_builder_test.go index 880ea60e7b..880fb27810 100644 --- a/pkg/workflow/safe_output_config_builder_test.go +++ b/pkg/workflow/safe_output_config_builder_test.go @@ -252,15 +252,17 @@ func TestBuildSafeOutputConfigs(t *testing.T) { }, }, { - name: "Empty configs are excluded", + name: "Empty configs are included (handler enabled by presence)", safeOutputs: &SafeOutputsConfig{ CreateIssues: &CreateIssuesConfig{ // All zero values }, }, - expectedKeys: []string{}, + expectedKeys: []string{"create_issue"}, validateField: func(t *testing.T, config map[string]map[string]any) { - assert.Empty(t, config, "Empty configs should not be included") + assert.Contains(t, config, "create_issue", "Empty config should still be included") + issueConfig := config["create_issue"] + assert.Empty(t, issueConfig, "Config should be empty map") }, }, } From 89aa8360613d200d1327bef74b689245f6a1f727 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:48:03 +0000 Subject: [PATCH 5/5] style: Format safe output config builder files Applied Go formatter (gofmt) to align field mappings and struct literals for better readability. No functional changes. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/safe_output_config_builder.go | 68 +++++++++---------- .../safe_output_config_builder_test.go | 58 ++++++++-------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/pkg/workflow/safe_output_config_builder.go b/pkg/workflow/safe_output_config_builder.go index 5f2fbbd01a..3f801b585c 100644 --- a/pkg/workflow/safe_output_config_builder.go +++ b/pkg/workflow/safe_output_config_builder.go @@ -10,40 +10,40 @@ var safeOutputConfigBuilderLog = logger.New("workflow:safe_output_config_builder // fieldMapping defines how struct field names map to handler config keys var fieldMapping = map[string]string{ - "Max": "max", - "GitHubToken": "github-token", - "TitlePrefix": "title_prefix", - "Labels": "labels", - "AllowedLabels": "allowed_labels", - "Assignees": "assignees", - "TargetRepoSlug": "target-repo", - "AllowedRepos": "allowed_repos", - "Expires": "expires", - "Target": "target", - "HideOlderComments": "hide_older_comments", - "Category": "category", - "CloseOlderDiscussions": "close_older_discussions", - "RequiredLabels": "required_labels", - "RequiredTitlePrefix": "required_title_prefix", - "RequiredCategory": "required_category", - "Allowed": "allowed", - "Status": "allow_status", - "Title": "allow_title", - "Body": "allow_body", - "ParentRequiredLabels": "parent_required_labels", - "ParentTitlePrefix": "parent_title_prefix", - "SubRequiredLabels": "sub_required_labels", - "SubTitlePrefix": "sub_title_prefix", - "Side": "side", - "Draft": "draft", - "IfNoChanges": "if_no_changes", - "AllowEmpty": "allow_empty", - "CommitTitleSuffix": "commit_title_suffix", - "AllowedReasons": "allowed_reasons", - "Workflows": "workflows", - "Reviewers": "reviewers", - "DefaultAgent": "default_agent", - "Discussion": "discussion", + "Max": "max", + "GitHubToken": "github-token", + "TitlePrefix": "title_prefix", + "Labels": "labels", + "AllowedLabels": "allowed_labels", + "Assignees": "assignees", + "TargetRepoSlug": "target-repo", + "AllowedRepos": "allowed_repos", + "Expires": "expires", + "Target": "target", + "HideOlderComments": "hide_older_comments", + "Category": "category", + "CloseOlderDiscussions": "close_older_discussions", + "RequiredLabels": "required_labels", + "RequiredTitlePrefix": "required_title_prefix", + "RequiredCategory": "required_category", + "Allowed": "allowed", + "Status": "allow_status", + "Title": "allow_title", + "Body": "allow_body", + "ParentRequiredLabels": "parent_required_labels", + "ParentTitlePrefix": "parent_title_prefix", + "SubRequiredLabels": "sub_required_labels", + "SubTitlePrefix": "sub_title_prefix", + "Side": "side", + "Draft": "draft", + "IfNoChanges": "if_no_changes", + "AllowEmpty": "allow_empty", + "CommitTitleSuffix": "commit_title_suffix", + "AllowedReasons": "allowed_reasons", + "Workflows": "workflows", + "Reviewers": "reviewers", + "DefaultAgent": "default_agent", + "Discussion": "discussion", } // buildHandlerConfig builds a handler configuration map from a config struct using reflection. diff --git a/pkg/workflow/safe_output_config_builder_test.go b/pkg/workflow/safe_output_config_builder_test.go index 880fb27810..9e1b332e63 100644 --- a/pkg/workflow/safe_output_config_builder_test.go +++ b/pkg/workflow/safe_output_config_builder_test.go @@ -19,23 +19,23 @@ func TestBuildHandlerConfig(t *testing.T) { BaseSafeOutputConfig: BaseSafeOutputConfig{ Max: 5, }, - TitlePrefix: "[AI] ", - Labels: []string{"bug", "ai"}, - AllowedLabels: []string{"bug", "feature"}, - Assignees: []string{"user1", "user2"}, + TitlePrefix: "[AI] ", + Labels: []string{"bug", "ai"}, + AllowedLabels: []string{"bug", "feature"}, + Assignees: []string{"user1", "user2"}, TargetRepoSlug: "owner/repo", - AllowedRepos: []string{"org/repo1", "org/repo2"}, - Expires: 72, + AllowedRepos: []string{"org/repo1", "org/repo2"}, + Expires: 72, }, expected: map[string]any{ - "max": int64(5), - "title_prefix": "[AI] ", - "labels": []any{"bug", "ai"}, - "allowed_labels": []any{"bug", "feature"}, - "assignees": []any{"user1", "user2"}, - "target-repo": "owner/repo", - "allowed_repos": []any{"org/repo1", "org/repo2"}, - "expires": int64(72), + "max": int64(5), + "title_prefix": "[AI] ", + "labels": []any{"bug", "ai"}, + "allowed_labels": []any{"bug", "feature"}, + "assignees": []any{"user1", "user2"}, + "target-repo": "owner/repo", + "allowed_repos": []any{"org/repo1", "org/repo2"}, + "expires": int64(72), }, }, { @@ -50,11 +50,11 @@ func TestBuildHandlerConfig(t *testing.T) { AllowedRepos: []string{"owner/repo2"}, }, expected: map[string]any{ - "max": int64(3), - "target": "issue", - "hide_older_comments": true, - "target-repo": "owner/repo", - "allowed_repos": []any{"owner/repo2"}, + "max": int64(3), + "target": "issue", + "hide_older_comments": true, + "target-repo": "owner/repo", + "allowed_repos": []any{"owner/repo2"}, }, }, { @@ -63,7 +63,7 @@ func TestBuildHandlerConfig(t *testing.T) { BaseSafeOutputConfig: BaseSafeOutputConfig{ Max: 0, // Should be omitted }, - TitlePrefix: "", // Should be omitted + TitlePrefix: "", // Should be omitted Labels: []string{}, // Should be omitted }, expected: map[string]any{}, @@ -106,10 +106,10 @@ func TestBuildHandlerConfig(t *testing.T) { }, }, expected: map[string]any{ - "max": int64(10), - "target": "issue", - "required_labels": []any{"stale"}, - "required_title_prefix": "[OLD]", + "max": int64(10), + "target": "issue", + "required_labels": []any{"stale"}, + "required_title_prefix": "[OLD]", }, }, { @@ -175,14 +175,14 @@ func TestBuildSafeOutputConfigs(t *testing.T) { expectedKeys: []string{"create_issue", "add_comment", "add_labels"}, validateField: func(t *testing.T, config map[string]map[string]any) { assert.Len(t, config, 3, "Should have 3 handlers") - + issueConfig := config["create_issue"] assert.Equal(t, int64(5), issueConfig["max"]) - + commentConfig := config["add_comment"] assert.Equal(t, int64(3), commentConfig["max"]) assert.Equal(t, "issue", commentConfig["target"]) - + labelConfig := config["add_labels"] assert.Equal(t, []any{"bug", "feature"}, labelConfig["allowed"]) }, @@ -270,12 +270,12 @@ func TestBuildSafeOutputConfigs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := buildSafeOutputConfigs(tt.safeOutputs) - + // Check expected keys for _, key := range tt.expectedKeys { assert.Contains(t, result, key, "Should contain key %s", key) } - + // Additional validation if tt.validateField != nil { tt.validateField(t, result)