From 4b211c5c46ad02251de91600fb2742bee8ee922c Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer Date: Fri, 2 Jan 2026 20:48:13 +0100 Subject: [PATCH 1/6] chore: enhance campaign management - Added support for "Human Oversight Required" field in project updates. - Updated tests to ensure correct handling of new field names and validation rules. - Improved documentation to reflect changes in campaign structure and project management. - Modified orchestrator logic to handle optional tracker labels and improve campaign hub visibility. - Enhanced validation to allow campaigns without a tracker label, focusing on project membership as the primary scope. - Updated safe output validation to include new fields for draft issues and improved error handling. --- actions/setup/js/package-lock.json | 7 -- .../setup/js/safe_output_type_validator.cjs | 32 +++++++ .../js/safe_output_type_validator.test.cjs | 91 +++++++++++++++++++ actions/setup/js/update_project.cjs | 55 ++++++++--- actions/setup/js/update_project.test.cjs | 41 +++++++++ .../docs/guides/campaigns/getting-started.md | 17 ++++ .../guides/campaigns/project-management.md | 55 ++++++++++- .../content/docs/guides/campaigns/specs.md | 36 ++++++-- pkg/campaign/orchestrator.go | 8 +- pkg/campaign/orchestrator_test.go | 27 +++++- .../prompts/orchestrator_instructions.md | 82 ++++++++++++----- .../prompts/project_update_instructions.md | 18 ++-- .../schemas/campaign_spec_schema.json | 2 +- pkg/campaign/template_test.go | 11 +-- pkg/campaign/validation.go | 4 +- pkg/campaign/validation_test.go | 14 +-- pkg/workflow/safe_output_validation_config.go | 5 +- .../safe_output_validation_config_test.go | 19 ++++ 18 files changed, 431 insertions(+), 93 deletions(-) diff --git a/actions/setup/js/package-lock.json b/actions/setup/js/package-lock.json index e4090cb2b2f..a26075db1eb 100644 --- a/actions/setup/js/package-lock.json +++ b/actions/setup/js/package-lock.json @@ -775,7 +775,6 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -1316,7 +1315,6 @@ "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1456,7 +1454,6 @@ "integrity": "sha512-oWtNM89Np+YsQO3ttT5i1Aer/0xbzQzp66NzuJn/U16bB7MnvSzdLKXgk1kkMLYyKSSzA2ajzqMkYheaE9opuQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/utils": "4.0.10", "fflate": "^0.8.2", @@ -1897,7 +1894,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2091,7 +2087,6 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -2213,7 +2208,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -2289,7 +2283,6 @@ "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.10", "@vitest/mocker": "4.0.10", diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 550b4c89397..96e0d31ff6e 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -465,6 +465,38 @@ function executeCustomValidation(item, customValidation, lineNum, itemType) { } } + if (customValidation === "updateProjectValidTarget") { + const contentType = item.content_type; + + if (contentType === "draft_issue") { + const draftTitle = typeof item.draft_title === "string" ? item.draft_title.trim() : ""; + if (!draftTitle) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} 'draft_title' is required and must be a non-empty string when 'content_type' is 'draft_issue'`, + }; + } + return null; + } + + const normalize = v => { + if (v === undefined || v === null) return ""; + if (typeof v === "number") return Number.isFinite(v) ? String(v) : ""; + return String(v).trim(); + }; + + const hasContentNumber = normalize(item.content_number) !== ""; + const hasIssue = normalize(item.issue) !== ""; + const hasPullRequest = normalize(item.pull_request) !== ""; + + if (!hasContentNumber && !hasIssue && !hasPullRequest) { + return { + isValid: false, + error: `Line ${lineNum}: ${itemType} requires one of: 'content_number', 'issue', or 'pull_request' (or set 'content_type' to 'draft_issue' with 'draft_title')`, + }; + } + } + return null; } diff --git a/actions/setup/js/safe_output_type_validator.test.cjs b/actions/setup/js/safe_output_type_validator.test.cjs index f984515282e..d0d4bd24e94 100644 --- a/actions/setup/js/safe_output_type_validator.test.cjs +++ b/actions/setup/js/safe_output_type_validator.test.cjs @@ -96,6 +96,28 @@ const SAMPLE_VALIDATION_CONFIG = { }, }, }, + update_project: { + defaultMax: 10, + customValidation: "updateProjectValidTarget", + fields: { + project: { + required: true, + type: "string", + sanitize: true, + maxLength: 512, + pattern: "^https://github\\.com/(orgs|users)/[^/]+/projects/\\d+", + patternError: "must be a full GitHub project URL (e.g., https://github.com/orgs/myorg/projects/42)", + }, + campaign_id: { type: "string", sanitize: true, maxLength: 128 }, + content_type: { type: "string", enum: ["issue", "pull_request", "draft_issue"] }, + content_number: { optionalPositiveInteger: true }, + issue: { optionalPositiveInteger: true }, + pull_request: { optionalPositiveInteger: true }, + draft_title: { type: "string", sanitize: true, maxLength: 256 }, + draft_body: { type: "string", sanitize: true, maxLength: 65000 }, + fields: { type: "object" }, + }, + }, }; describe("safe_output_type_validator", () => { @@ -396,6 +418,75 @@ describe("safe_output_type_validator", () => { }); }); + describe("custom validation: updateProjectValidTarget", () => { + it("should fail when content_type is draft_issue and draft_title is missing", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "update_project", + project: "https://github.com/orgs/acme/projects/42", + content_type: "draft_issue", + }, + "update_project", + 1 + ); + + expect(result.isValid).toBe(false); + expect(result.error).toContain("draft_title"); + }); + + it("should pass when content_type is draft_issue and draft_title is provided", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "update_project", + project: "https://github.com/orgs/acme/projects/42", + content_type: "draft_issue", + draft_title: "Investigate flaky CI", + }, + "update_project", + 1 + ); + + expect(result.isValid).toBe(true); + }); + + it("should fail when non-draft item has no target identifiers", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "update_project", + project: "https://github.com/orgs/acme/projects/42", + }, + "update_project", + 1 + ); + + expect(result.isValid).toBe(false); + expect(result.error).toContain("requires one of"); + }); + + it("should pass when non-draft item provides content_number", async () => { + const { validateItem } = await import("./safe_output_type_validator.cjs"); + + const result = validateItem( + { + type: "update_project", + project: "https://github.com/orgs/acme/projects/42", + content_type: "issue", + content_number: 123, + }, + "update_project", + 1 + ); + + expect(result.isValid).toBe(true); + }); + }); + describe("enum validation", () => { it("should validate enum values (case-insensitive)", async () => { const { validateItem } = await import("./safe_output_type_validator.cjs"); diff --git a/actions/setup/js/update_project.cjs b/actions/setup/js/update_project.cjs index 86e160652cb..03f380c7366 100644 --- a/actions/setup/js/update_project.cjs +++ b/actions/setup/js/update_project.cjs @@ -4,6 +4,37 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { getErrorMessage } = require("./error_helpers.cjs"); +/** + * Normalize a field name for matching against existing Project field names. + * This intentionally treats punctuation (like "/") as whitespace so that + * keys like "worker_workflow" can match field names like "Worker/Workflow". + * + * @param {unknown} name + * @returns {string} + */ +function normalizeFieldNameForComparison(name) { + return String(name || "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, " ") + .trim(); +} + +/** + * Convert a key-like field name (snake_case, kebab-case, etc) into a display + * name suitable for creating/renaming Project fields. + * + * @param {unknown} fieldKey + * @returns {string} + */ +function toProjectFieldDisplayName(fieldKey) { + return String(fieldKey || "") + .trim() + .split(/[^a-zA-Z0-9]+/) + .filter(Boolean) + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(" "); +} + /** * Log detailed GraphQL error information * @param {Error & { errors?: Array<{ type?: string, message: string, path?: unknown, locations?: unknown }>, request?: unknown, data?: unknown }} error - GraphQL error @@ -379,19 +410,17 @@ async function updateProject(output) { ) ).node.fields.nodes; for (const [fieldName, fieldValue] of Object.entries(fieldsToUpdate)) { - const normalizedFieldName = fieldName - .split(/[\s_-]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(" "); + const wantedFieldName = normalizeFieldNameForComparison(fieldName); + const displayFieldName = toProjectFieldDisplayName(fieldName); let valueToSet, - field = projectFields.find(f => f.name.toLowerCase() === normalizedFieldName.toLowerCase()); + field = projectFields.find(f => normalizeFieldNameForComparison(f.name) === wantedFieldName); if (!field) if ("classification" === fieldName.toLowerCase() || ("string" == typeof fieldValue && fieldValue.includes("|"))) try { field = ( await github.graphql( "mutation($projectId: ID!, $name: String!, $dataType: ProjectV2CustomFieldType!) {\n createProjectV2Field(input: {\n projectId: $projectId,\n name: $name,\n dataType: $dataType\n }) {\n projectV2Field {\n ... on ProjectV2Field {\n id\n name\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n options { id name }\n }\n }\n }\n }", - { projectId, name: normalizedFieldName, dataType: "TEXT" } + { projectId, name: displayFieldName, dataType: "TEXT" } ) ).createProjectV2Field.projectV2Field; } catch (createError) { @@ -403,7 +432,7 @@ async function updateProject(output) { field = ( await github.graphql( "mutation($projectId: ID!, $name: String!, $dataType: ProjectV2CustomFieldType!, $options: [ProjectV2SingleSelectFieldOptionInput!]!) {\n createProjectV2Field(input: {\n projectId: $projectId,\n name: $name,\n dataType: $dataType,\n singleSelectOptions: $options\n }) {\n projectV2Field {\n ... on ProjectV2SingleSelectField {\n id\n name\n options { id name }\n }\n ... on ProjectV2Field {\n id\n name\n }\n }\n }\n }", - { projectId, name: normalizedFieldName, dataType: "SINGLE_SELECT", options: [{ name: String(fieldValue), description: "", color: "GRAY" }] } + { projectId, name: displayFieldName, dataType: "SINGLE_SELECT", options: [{ name: String(fieldValue), description: "", color: "GRAY" }] } ) ).createProjectV2Field.projectV2Field; } catch (createError) { @@ -501,19 +530,17 @@ async function updateProject(output) { ) ).node.fields.nodes; for (const [fieldName, fieldValue] of Object.entries(fieldsToUpdate)) { - const normalizedFieldName = fieldName - .split(/[\s_-]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(" "); + const wantedFieldName = normalizeFieldNameForComparison(fieldName); + const displayFieldName = toProjectFieldDisplayName(fieldName); let valueToSet, - field = projectFields.find(f => f.name.toLowerCase() === normalizedFieldName.toLowerCase()); + field = projectFields.find(f => normalizeFieldNameForComparison(f.name) === wantedFieldName); if (!field) if ("classification" === fieldName.toLowerCase() || ("string" == typeof fieldValue && fieldValue.includes("|"))) try { field = ( await github.graphql( "mutation($projectId: ID!, $name: String!, $dataType: ProjectV2CustomFieldType!) {\n createProjectV2Field(input: {\n projectId: $projectId,\n name: $name,\n dataType: $dataType\n }) {\n projectV2Field {\n ... on ProjectV2Field {\n id\n name\n }\n ... on ProjectV2SingleSelectField {\n id\n name\n options { id name }\n }\n }\n }\n }", - { projectId, name: normalizedFieldName, dataType: "TEXT" } + { projectId, name: displayFieldName, dataType: "TEXT" } ) ).createProjectV2Field.projectV2Field; } catch (createError) { @@ -525,7 +552,7 @@ async function updateProject(output) { field = ( await github.graphql( "mutation($projectId: ID!, $name: String!, $dataType: ProjectV2CustomFieldType!, $options: [ProjectV2SingleSelectFieldOptionInput!]!) {\n createProjectV2Field(input: {\n projectId: $projectId,\n name: $name,\n dataType: $dataType,\n singleSelectOptions: $options\n }) {\n projectV2Field {\n ... on ProjectV2SingleSelectField {\n id\n name\n options { id name }\n }\n ... on ProjectV2Field {\n id\n name\n }\n }\n }\n }", - { projectId, name: normalizedFieldName, dataType: "SINGLE_SELECT", options: [{ name: String(fieldValue), description: "", color: "GRAY" }] } + { projectId, name: displayFieldName, dataType: "SINGLE_SELECT", options: [{ name: String(fieldValue), description: "", color: "GRAY" }] } ) ).createProjectV2Field.projectV2Field; } catch (createError) { diff --git a/actions/setup/js/update_project.test.cjs b/actions/setup/js/update_project.test.cjs index 8e6e88be106..74e7f2323ee 100644 --- a/actions/setup/js/update_project.test.cjs +++ b/actions/setup/js/update_project.test.cjs @@ -542,6 +542,47 @@ describe("updateProject", () => { expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to add campaign label")); }); + it("matches existing field names with slashes (Worker/Workflow) when using snake_case keys", async () => { + const projectUrl = "https://github.com/orgs/testowner/projects/60"; + const output = { + type: "update_project", + project: projectUrl, + content_type: "issue", + content_number: 88, + fields: { + worker_workflow: "orchestrator", + }, + }; + + queueResponses([ + repoResponse(), + viewerResponse(), + orgProjectV2Response(projectUrl, 60, "project-worker-workflow"), + issueResponse("issue-id-88"), + existingItemResponse("issue-id-88", "item-worker-workflow"), + fieldsResponse([ + { + id: "field-worker-workflow", + name: "Worker/Workflow", + dataType: "SINGLE_SELECT", + options: [{ id: "opt-orchestrator", name: "orchestrator", color: "GRAY" }], + }, + ]), + updateFieldValueResponse(), + ]); + + await updateProject(output); + + // Should update existing field value, not try to create a new field. + const createdFieldCall = mockGithub.graphql.mock.calls.find(([query]) => query.includes("createProjectV2Field")); + expect(createdFieldCall).toBeUndefined(); + + const updateCall = mockGithub.graphql.mock.calls.find(([query]) => query.includes("updateProjectV2ItemFieldValue")); + expect(updateCall).toBeDefined(); + expect(updateCall[1].fieldId).toBe("field-worker-workflow"); + expect(updateCall[1].value).toEqual({ singleSelectOptionId: "opt-orchestrator" }); + }); + it("rejects non-URL project identifier", async () => { const output = { type: "update_project", project: "My Campaign", campaign_id: "my-campaign-123" }; await expect(updateProject(output)).rejects.toThrow(/full GitHub project URL/); diff --git a/docs/src/content/docs/guides/campaigns/getting-started.md b/docs/src/content/docs/guides/campaigns/getting-started.md index 1e42fd7f6e9..85ddafc18a3 100644 --- a/docs/src/content/docs/guides/campaigns/getting-started.md +++ b/docs/src/content/docs/guides/campaigns/getting-started.md @@ -21,10 +21,12 @@ In GitHub: your org → **Projects** → **New project**. - Start with a **Table** view (simplest option) - Add a **Board** view grouped by `Status` for kanban-style tracking - Consider a **Roadmap** view for timeline visualization (requires Start Date/End Date fields) +- Add a dedicated “Needs human” view filtered by `Human Oversight Required = Yes` **Recommended custom fields** (see [Project Management](/gh-aw/guides/campaigns/project-management/) for details): - **Status** (Single select): Todo, In Progress, Blocked, Done - **Worker/Workflow** (Single select): Names of your worker workflows +- **Human Oversight Required** (Single select): Yes, No - **Priority** (Single select): High, Medium, Low - **Start Date** / **End Date** (Date): For roadmap timeline views @@ -77,6 +79,21 @@ Trigger the orchestrator workflow from GitHub Actions. Its job is to keep the da - Updates fields/status - Posts a short report +### Where the epic issue, summaries, and metrics go + +- **Epic issue (campaign hub)**: The issue created from the “🚀 Start an Agentic Campaign” issue form. This is the human-facing command center for decisions and context. +- **Campaign summaries**: Each orchestrator run should produce a short, human-readable summary in the GitHub Actions run summary. If you want the same summary posted to the epic issue, label the epic issue with `campaign:` once you know the campaign id. +- **Metrics**: Durable metrics/KPI snapshots are written to repo-memory (when enabled via metrics globs in the campaign spec). These JSON snapshots are the canonical machine-readable history; the orchestrator can also include a short “latest metrics” excerpt in the run summary and/or epic issue comment. + +**Tip (recommended)**: After the campaign spec exists and you know the `id`, add a label like `campaign:framework-upgrade` to the epic issue. This gives the orchestrator a deterministic way to find the hub issue and post summaries without guessing. + +**Make the epic issue visible on the Project board (Option A)**: Add the epic issue to the Project board (it can live alongside the campaign tasks). If your board has these fields, set: + +- `Worker/Workflow = orchestrator` +- `Human Oversight Required = Yes` + +This keeps the campaign hub in your “Needs human” queue and makes it obvious in Roadmap/Board/Table views. + ## 5) Add work items Apply the tracker label (for example `campaign:framework-upgrade`) to issues/PRs you want tracked. The orchestrator will pick them up on the next run. diff --git a/docs/src/content/docs/guides/campaigns/project-management.md b/docs/src/content/docs/guides/campaigns/project-management.md index 30126e1e34d..7fa0485b1e7 100644 --- a/docs/src/content/docs/guides/campaigns/project-management.md +++ b/docs/src/content/docs/guides/campaigns/project-management.md @@ -5,10 +5,50 @@ description: "Use GitHub Projects with roadmap views and custom date fields for GitHub Projects offers powerful visualization and tracking capabilities for agentic campaigns. This guide covers view configurations, custom fields, and filtering strategies to maximize campaign visibility and control. +## Where summaries and metrics live + +- **Project board**: Canonical state for tasks (membership, status, worker/workflow, oversight flags). Keep it structured and queryable. +- **Epic issue (campaign hub)**: Human decision log and narrative context (created via the campaign issue form, labeled `campaign` + `campaign-tracker`). +- **Orchestrator run summary**: The per-run execution report (what changed, what was discovered, what needs attention). +- **Repo-memory metrics**: The durable, machine-readable history (metrics/KPI snapshots written as JSON files when `metrics-glob` is configured). + +**Recommended wiring**: Once the campaign has an `id`, label the epic issue with `campaign:` (for example `campaign:framework-upgrade`). This enables orchestrators to reliably find the hub issue and post a short per-run comment without guessing. + +## Make the epic issue visible on the board (Option A) + +If you want the campaign hub to show up directly in your Roadmap/Board/Table views, add the epic issue as a Project item. + +Recommended field values for the epic issue item: +- **Worker/Workflow**: `orchestrator` +- **Human Oversight Required**: `Yes` + +This keeps the hub in the human review queue and makes it easy to find from any view. + ## Recommended Custom Fields for Campaigns Before configuring views, set up custom fields that provide valuable filtering and grouping capabilities: +## Recommended Views (high-value defaults) + +After the fields below exist, create a small set of views that each answer one question: + +1. **Roadmap** (Roadmap layout) + - Group by: **Worker/Workflow** + - Date fields: **Start Date** and **End Date** + - Filter: `Status != Done` + +2. **Board** (Board layout) + - Group by: **Status** + - Saved filter: `Human Oversight Required = Yes` (your human review queue) + +3. **Backlog** (Table layout) + - Columns: `Status`, `Human Oversight Required`, `Worker/Workflow`, `Priority` + - Use “Slice by” on **Worker/Workflow** to review one worker at a time + +4. **Exceptions** (Table layout) + - Filter: `Status = Blocked OR Human Oversight Required = Yes` + - Keep this view small and human-operated + ### Essential Campaign Fields **One-time manual setup** (in the GitHub Projects UI): @@ -28,23 +68,28 @@ Before configuring views, set up custom fields that provide valuable filtering a - Purpose: Track work state across the campaign - Default field in most project templates -4. **Start Date** (Date) +4. **Human Oversight Required** (Single select) + - Values: Yes, No + - Purpose: Create an explicit human review queue (triage, approvals, risky changes) + - Use this to power a dedicated “Needs human” view + +5. **Start Date** (Date) - Purpose: When work begins (auto-populated from issue `createdAt`) - Required for Roadmap timeline visualization -5. **End Date** (Date) +6. **End Date** (Date) - Purpose: When work completes (auto-populated from issue `closedAt`) - Required for Roadmap timeline visualization -6. **Effort** (Single select - optional) +7. **Effort** (Single select - optional) - Values: Small (1-3 days), Medium (1 week), Large (2+ weeks) - Purpose: Estimate work size for capacity planning -7. **Team** (Single select - optional) +8. **Team** (Single select - optional) - Values: Frontend, Backend, DevOps, Documentation, etc. - Purpose: Track which team or area owns the work -8. **Repository** (Single select - optional, for cross-repository campaigns) +9. **Repository** (Single select - optional, for cross-repository campaigns) - Values: Repository names (e.g., "gh-aw", "docs-site", "api-server") - Purpose: Track which repository an item belongs to - Enables filtering and grouping by repository in multi-repo campaigns diff --git a/docs/src/content/docs/guides/campaigns/specs.md b/docs/src/content/docs/guides/campaigns/specs.md index 9e7568a67d9..20aa4d03826 100644 --- a/docs/src/content/docs/guides/campaigns/specs.md +++ b/docs/src/content/docs/guides/campaigns/specs.md @@ -1,20 +1,21 @@ --- title: "Campaign Specs" -description: "Define and configure agentic campaigns with spec files, tracker labels, and recommended wiring" +description: "Define and configure agentic campaigns with spec files and GitHub Projects" --- Campaigns are defined as Markdown files under `.github/workflows/` with a `.campaign.md` suffix. The YAML frontmatter is the campaign “contract”; the body can contain optional narrative context. ## What a campaign is (in gh-aw) -In GitHub Agentic Workflows, a campaign is not “a special kind of workflow.” The `.campaign.md` file is a specification: a reviewable contract that wires together agentic workflows around a shared initiative (a tracker label, a GitHub Project dashboard, and optional durable state). +In GitHub Agentic Workflows, a campaign is not “a special kind of workflow.” The `.campaign.md` file is a specification: a reviewable contract that wires together agentic workflows around a shared initiative (a GitHub Project dashboard, optional ingestion signals like tracker labels, and optional durable state). In a typical setup: - Worker workflows do the work. They run an agent and use safe-outputs (for example `create_pull_request`, `add_comment`, or `update_issues`) for write operations. -- A generated orchestrator workflow keeps the campaign coherent over time. It discovers items tagged with your tracker label, updates the Project board, and produces ongoing progress reporting. +- A generated orchestrator workflow keeps the campaign coherent over time. It reads the campaign Project, updates Project fields, and produces ongoing progress reporting. - Repo-memory (optional) makes the campaign repeatable. It lets you store a cursor checkpoint and append-only metrics snapshots so each run can pick up where the last one left off. + ### Mental model ```mermaid @@ -23,7 +24,7 @@ flowchart TB compile["fa:fa-cogs gh aw compile"] debug["fa:fa-file .campaign.g.md
debug artifact
(not tracked)
"] lock["fa:fa-lock .campaign.lock.yml
compiled workflow
(tracked in git)
"] - orchestrator["fa:fa-sitemap Orchestrator workflow
discovers items via tracker-label
updates Project dashboard
reads/writes repo-memory
"] + orchestrator["fa:fa-sitemap Orchestrator workflow
reads Project items
updates Project fields
reads/writes repo-memory
"] worker1["fa:fa-robot Worker workflow
agent + safe-outputs"] worker2["fa:fa-robot Worker workflow
agent + safe-outputs"] project["fa:fa-table GitHub Project board
campaign dashboard"] @@ -35,8 +36,8 @@ flowchart TB lock --> orchestrator orchestrator -->|triggers/coordinates| worker1 orchestrator -->|triggers/coordinates| worker2 - worker1 -->|creates/updates
Issues/PRs with
tracker-label| project - worker2 -->|creates/updates
Issues/PRs with
tracker-label| project + worker1 -->|creates/updates
Issues/PRs
(optional tracker-label)| project + worker2 -->|creates/updates
Issues/PRs
(optional tracker-label)| project orchestrator -.->|reads/writes| memory project -.->|dashboard view| orchestrator @@ -92,11 +93,30 @@ owners: - `id`: stable identifier used for file naming, reporting, and (if used) repo-memory paths. - `project-url`: the GitHub Project that acts as the campaign dashboard. -- `tracker-label`: the label applied to issues and pull requests that belong to the campaign (commonly `campaign:`). This is the key that lets the orchestrator discover work across runs. +- `tracker-label` (optional): a label applied to issues and pull requests (commonly `campaign:`) to help discovery/ingestion. For a robust campaign, Project membership is the source of truth for “in scope”. - `objective`: a single sentence describing what “done” means. - `kpis`: the measures you use to report progress (exactly one should be marked `primary`). - `workflows`: the participating workflow IDs. These refer to workflows in the repo (commonly `.github/workflows/.md`), and they can be scheduled, event-driven, or long-running. + +## Membership contract (one Project per campaign) + +For a robust campaign, keep the membership rule explicit and deterministic: + +- “In scope” = “is a Project item in the campaign Project”. +- Non-trackable work = Project draft item. +- Labels, workflow history, and free-form text are not membership signals (they can still be useful context). + +This keeps campaigns worker-agnostic because membership does not depend on which workflow (or AI engine) created/updated an item. + +## Tracker labels (optional ingestion) + +If you want labels, treat them as a convenience for discovery/ingestion: + +- Worker workflows may add `tracker-label` to issues/PRs they create. +- The orchestrator may use the label as a helper to auto-add items into the Project. +- The Project remains the system of record for membership and progress. + ## KPIs (recommended shape) Keep KPIs small and crisp: @@ -141,7 +161,7 @@ governance: ## Compilation and orchestrators -`gh aw compile` validates campaign specs. When the spec has meaningful details (tracker label, workflows, memory paths, or a metrics glob), it also generates an orchestrator and compiles it to `.campaign.lock.yml`. +`gh aw compile` validates campaign specs. When the spec has meaningful details (project URL, workflows, memory paths, or a metrics glob), it also generates an orchestrator and compiles it to `.campaign.lock.yml`. During compilation, a `.campaign.g.md` file is generated locally as a debug artifact to help developers understand the orchestrator structure, but this file is not committed to git—only the compiled `.campaign.lock.yml` is tracked. diff --git a/pkg/campaign/orchestrator.go b/pkg/campaign/orchestrator.go index 75bc066743b..7c823b09696 100644 --- a/pkg/campaign/orchestrator.go +++ b/pkg/campaign/orchestrator.go @@ -93,7 +93,11 @@ func BuildOrchestrator(spec *CampaignSpec, campaignFilePath string) (*workflow.W description := spec.Description if strings.TrimSpace(description) == "" { - description = fmt.Sprintf("Orchestrator workflow for campaign '%s' (tracker: %s)", spec.ID, spec.TrackerLabel) + if strings.TrimSpace(spec.TrackerLabel) != "" { + description = fmt.Sprintf("Orchestrator workflow for campaign '%s' (tracker: %s)", spec.ID, spec.TrackerLabel) + } else { + description = fmt.Sprintf("Orchestrator workflow for campaign '%s'", spec.ID) + } } // Default triggers: daily schedule plus manual workflow_dispatch. @@ -112,7 +116,7 @@ func BuildOrchestrator(spec *CampaignSpec, campaignFilePath string) (*workflow.W hasDetails := false if spec.TrackerLabel != "" { - fmt.Fprintf(markdownBuilder, "- Tracker label: `%s`\n", spec.TrackerLabel) + fmt.Fprintf(markdownBuilder, "- Tracker label (optional ingestion): `%s`\n", spec.TrackerLabel) hasDetails = true } if strings.TrimSpace(spec.Objective) != "" { diff --git a/pkg/campaign/orchestrator_test.go b/pkg/campaign/orchestrator_test.go index da1eabe2079..1c698f48400 100644 --- a/pkg/campaign/orchestrator_test.go +++ b/pkg/campaign/orchestrator_test.go @@ -51,7 +51,7 @@ func TestBuildOrchestrator_BasicShape(t *testing.T) { t.Fatalf("expected markdown content to mention campaign name, got: %q", data.MarkdownContent) } - if !strings.Contains(data.MarkdownContent, spec.TrackerLabel) { + if spec.TrackerLabel != "" && !strings.Contains(data.MarkdownContent, spec.TrackerLabel) { t.Fatalf("expected markdown content to mention tracker label %q, got: %q", spec.TrackerLabel, data.MarkdownContent) } @@ -62,6 +62,31 @@ func TestBuildOrchestrator_BasicShape(t *testing.T) { } } +func TestBuildOrchestrator_NoTrackerLabelDoesNotMentionTracker(t *testing.T) { + spec := &CampaignSpec{ + ID: "test-campaign", + Name: "Test Campaign", + ProjectURL: "https://github.com/orgs/test/projects/1", + Workflows: []string{"test-workflow"}, + TrackerLabel: "", + } + + mdPath := ".github/workflows/test-campaign.campaign.md" + data, _ := BuildOrchestrator(spec, mdPath) + + if data == nil { + t.Fatalf("expected non-nil WorkflowData") + } + + if strings.Contains(data.MarkdownContent, "- Tracker label") { + t.Fatalf("did not expect tracker label bullet when tracker-label is omitted, got: %q", data.MarkdownContent) + } + + if strings.Contains(data.Description, "tracker:") { + t.Fatalf("did not expect default description to include tracker when tracker-label is omitted, got: %q", data.Description) + } +} + func TestBuildOrchestrator_CompletionInstructions(t *testing.T) { spec := &CampaignSpec{ ID: "test-campaign", diff --git a/pkg/campaign/prompts/orchestrator_instructions.md b/pkg/campaign/prompts/orchestrator_instructions.md index 6f5d38cc446..c59a988def2 100644 --- a/pkg/campaign/prompts/orchestrator_instructions.md +++ b/pkg/campaign/prompts/orchestrator_instructions.md @@ -1,6 +1,6 @@ ## Campaign Orchestrator Rules -This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. It also maintains the campaign dashboard by ensuring the GitHub Project stays in sync with the campaign's tracker label. +This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. The GitHub Project is the single source of truth for campaign membership and state. ### Traffic and rate limits (required) @@ -58,7 +58,7 @@ Guidance: - Use an ISO date (UTC) filename, for example: `metrics/2025-12-22.json`. - Keep snapshots append-only: write a new file per run; do not rewrite historical snapshots. - If a KPI is present, record its computed value and trend (Improving/Flat/Regressing) in the kpi_trends array. -- Count tasks from all sources: tracker-labeled issues, worker-created issues, and project board items. +- Count tasks from all sources: project board items (canonical), plus any newly discovered tracker-labeled or worker-created items that will be added. - Set tasks_total to the total number of unique tasks discovered in this run. - Set tasks_completed to the count of tasks with state "Done" or closed status. {{ end }} @@ -83,7 +83,7 @@ Guidance: 10. **Predefined fields only** - Only update explicitly defined project board fields 11. **Explicit outcomes** - Record actual outcomes, never infer status 12. **Idempotent operations** - Re-execution produces the same result without corruption -13. **Dashboard synchronization** - Keep Project items in sync with tracker-labeled issues/PRs +13. **Dashboard synchronization** - Keep the Project board in sync with discovered work (Project items are canonical; tracker labels are optional ingestion) ### Objective and KPIs (first-class) @@ -118,13 +118,18 @@ Execute these steps in sequence each time this orchestrator runs: #### Phase 1: Read State (Discovery) -1. **Query tracker-labeled items** - Search for issues and PRs matching the campaign's tracker label +1. **Query current project state (canonical)** - Read the GitHub Project board + - Retrieve all items currently on the project board + - For each item, record: content type, URL (if present), draft title (if present), status field value, and other predefined field values + - Create a snapshot of current board state + +2. **Query tracker-labeled items (optional ingestion)** - If a tracker label is configured, search for issues and PRs matching it - Search: `repo:OWNER/REPO label:TRACKER_LABEL` for all open and closed items - If governance opt-out labels are configured, exclude items with those labels - Collect all matching issue/PR URLs - Record metadata: number, title, state (open/closed), created date, updated date -2. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids +3. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids {{ if .Workflows }} - Worker workflows: {{ range $i, $w := .Workflows }}{{ if $i }}, {{ end }}{{ $w }}{{ end }} - **IMPORTANT**: You MUST perform SEPARATE searches for EACH worker workflow listed above - **IMPORTANT**: Workers may create different types of content (issues, PRs, discussions, comments). Search ALL content types to discover all worker outputs. @@ -139,18 +144,29 @@ Execute these steps in sequence each time this orchestrator runs: - Combine results from all worker searches into a single list of discovered items - Note: Comments are discovered via their parent issue/PR - the issue/PR is what gets added to the board -3. **Query current project state** - Read the GitHub Project board - - Retrieve all items currently on the project board - - For each item, record: issue URL, status field value, other predefined field values - - Create a snapshot of current board state - -4. **Compare and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) - - Items from step 1 or 2 not on board = **new work discovered** (report count) - - Items on board with state mismatch = **status updates needed** (report count) +4. **Merge and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) + - Items on the board are **in scope** by definition (canonical membership) + - Items from steps 2-3 not on board = **new work discovered** (report count) + - Items on board with state mismatch vs issue/PR state = **status updates needed** (report count) - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** (report count) - - Items on board but no longer found = **check if archived/deleted** (report count) + - Items on board but no longer accessible = **check if archived/deleted** (report count) - **CRITICAL**: This comparison is for reporting and planning only. In Phase 3, you MUST send ALL discovered items to update-project regardless of whether they appear to be on the board. The update-project tool handles duplicate detection automatically. +4.8 **Locate the campaign hub issue (optional but recommended)** + +If you have permission and comment writes are allowed, attempt to locate the campaign hub (“epic”) issue for posting per-run summaries. + +Deterministic matching rules: +- Prefer an issue that has BOTH labels: `campaign-tracker` AND `campaign:{{ .CampaignID }}`. +- If none found, do NOT guess. Proceed without an epic issue comment. +- If multiple matches, treat as ambiguous and proceed without commenting (report ambiguity). + +If a single hub issue is found, treat it as an in-scope item for synchronization: +- Include it in the Phase 3 `update-project` operations so it is present on the Project board (idempotent). +- When updating the hub issue item, set (when the fields exist): + - `worker_workflow = "orchestrator"` + - `human_oversight_required = "Yes"` + #### Phase 2: Make Decisions (Planning) 4.5 **Deterministic planner step (required when objective/KPIs are present)** @@ -173,12 +189,13 @@ Plan format (keep under 2KB): } ``` -5. **Decide processing order (with pacing)** - For items discovered in steps 1-2: - - **CRITICAL**: ALL discovered items (both tracker-labeled from step 1 AND worker-created from step 2) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. +5. **Decide processing order (with pacing)** - For items discovered in steps 1-3: + - **CRITICAL**: ALL discovered items (project items from step 1, tracker-labeled from step 2, and worker-created from step 3) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. - If `governance.max-new-items-per-run` is set, process at most that many items in this single run (remaining items will be processed in subsequent runs) - When applying the governance limit, prioritize in this order: - 1. Tracker-labeled items (campaign tasks) - process oldest first - 2. Worker-created items (worker outputs) - process oldest first + 1. Project board items (canonical scope) - process oldest first + 2. Tracker-labeled items (optional ingestion) - process oldest first + 3. Worker-created items (worker outputs) - process oldest first - Determine appropriate status field value based on item state: - Open issue/PR/discussion → "Todo" status - Closed issue/discussion → "Done" status @@ -207,12 +224,12 @@ Plan format (keep under 2KB): #### Phase 3: Write State (Execution) -**CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-2, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. +**CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-3, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. 8. **Execute project updates** - Send update-project for ALL discovered items - - Process ALL items from steps 1-2 (both tracker-labeled and worker-created), up to the governance limit if set + - Process ALL items from steps 1-3 (project items, tracker-labeled, and worker-created), up to the governance limit if set - Use `update-project` safe-output for EVERY discovered item - - Include fields from steps 5-6.5: `status`, `worker_workflow`, `priority`, `size`, etc. + - Include fields from steps 5-6.5: `status`, `worker_workflow`, `human_oversight_required`, `priority`, `size`, etc. - **The update-project tool will automatically**: - Skip adding items that are already on the board (idempotent add) - Update fields for items already on the board @@ -230,21 +247,36 @@ Plan format (keep under 2KB): #### Phase 4: Report (Output) 10. **Generate status report** - Summarize execution results: - - Total items discovered via tracker label (by type: issues, PRs) - - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) - - Items processed with update-project this run (count and URLs, broken down by: tracker-labeled vs worker-created) + - Total items currently on the project board (canonical) + - Total items discovered via tracker label (optional ingestion, by type: issues, PRs) + - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) + - Items processed with update-project this run (count and URLs, broken down by: project-board vs tracker-labeled vs worker-created) - Items skipped due to governance limits (count, type, and why - noting they will be processed in next run) - Current campaign metrics: open vs closed, progress percentage - Any failures encountered during update-project operations - Campaign completion status +11. **Post a hub issue comment (if hub issue was found and add-comment is allowed)** + +Post a short comment to the campaign hub issue that includes: +- Link to the Project board +- What was processed this run (counts + a few representative URLs) +- The current “Needs human” queue size (items with `human_oversight_required = Yes` if that field is used) +- If repo-memory metrics are enabled, include the path to the latest metrics snapshot written this run + +Do not paste large JSON blobs. Keep the comment concise and human-scannable. + ### Predefined Project Fields Only these fields may be updated on the project board: -- `status` (required) - Values: "Todo", "In Progress", "Done" +- `status` (required) - Values: "Todo", "In Progress", "Blocked", "Done" +- `worker_workflow` (optional) - String (recommended: the worker workflow ID/name) +- `human_oversight_required` (optional) - Values: "Yes", "No" (powers a dedicated human review queue) - `priority` (optional) - Values: "High", "Medium", "Low" - `size` (optional) - Values: "Small", "Medium", "Large" +- `start_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) +- `end_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) - `campaign_status` (metadata) - Values: "active", "completed" Do NOT update any other fields or create custom fields. diff --git a/pkg/campaign/prompts/project_update_instructions.md b/pkg/campaign/prompts/project_update_instructions.md index fa120ffa39b..aa68b4acfa0 100644 --- a/pkg/campaign/prompts/project_update_instructions.md +++ b/pkg/campaign/prompts/project_update_instructions.md @@ -4,9 +4,7 @@ Execute state writes using the `update-project` safe-output. All writes must target this exact project URL: **Project URL**: {{.ProjectURL}} -{{if .TrackerLabel}} -**Campaign ID**: Extract from tracker label `{{.TrackerLabel}}` (format: `campaign:CAMPAIGN_ID`) -{{end}} +**Campaign ID**: `{{.CampaignID}}` (recommended to tag items via the optional `campaign_id` field) #### Adding New Issues/PRs @@ -16,8 +14,8 @@ update-project: project: "{{.ProjectURL}}" content_type: "issue" # or "pull_request" content_number: 123 # Extract number from URL like https://github.com/owner/repo/issues/123 -{{if .TrackerLabel}} campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label {{.TrackerLabel}} -{{end}} fields: + campaign_id: "{{.CampaignID}}" # Optional: tags items for this campaign + fields: status: "Todo" # or "Done" if issue/PR is already closed/merged ``` @@ -40,8 +38,9 @@ update-project: content_number: 123 # Extract from URL fields: status: "Todo" # or "In Progress", "Blocked", "Done" -{{if .TrackerLabel}} campaign_id: "CAMPAIGN_ID" # Extract from tracker label {{.TrackerLabel}} -{{end}} worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + campaign_id: "{{.CampaignID}}" # Optional: tags items for this campaign + worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + human_oversight_required: "No" # or "Yes" - powers a dedicated human review queue priority: "High" # or "Medium", "Low" - enables priority-based views effort: "Medium" # or "Small", "Large" - enables capacity planning team: "TEAM_NAME" # Optional: for team-based grouping @@ -50,6 +49,7 @@ update-project: **Custom Field Benefits**: - `worker_workflow`: Groups items by workflow in Roadmap swimlanes; enables "Slice by" filtering in Table views (orchestrator populates this by discovering which worker created the item via tracker-id) +- `human_oversight_required`: Creates an explicit human review queue; use a "Needs human" view filtered to "Yes" - `priority`: Enables priority-based filtering and sorting - `effort`: Supports capacity planning and workload distribution - `team`: Enables team-based grouping for multi-team campaigns @@ -67,8 +67,8 @@ update-project: project: "{{.ProjectURL}}" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL -{{if .TrackerLabel}} campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label {{.TrackerLabel}} -{{end}} fields: + campaign_id: "{{.CampaignID}}" # Optional: tags items for this campaign + fields: status: "Done" # or "In Progress", "Todo" ``` diff --git a/pkg/campaign/schemas/campaign_spec_schema.json b/pkg/campaign/schemas/campaign_spec_schema.json index 1064a083aa3..812f547adf0 100644 --- a/pkg/campaign/schemas/campaign_spec_schema.json +++ b/pkg/campaign/schemas/campaign_spec_schema.json @@ -150,7 +150,7 @@ }, "tracker-label": { "type": "string", - "description": "Label used to associate issues/PRs with this campaign (e.g., campaign:incident-response)", + "description": "Optional ingestion label used to discover issues/PRs for this campaign (e.g., campaign:incident-response). Project membership is the canonical campaign scope.", "pattern": "^[^:]+:.+$", "minLength": 1 }, diff --git a/pkg/campaign/template_test.go b/pkg/campaign/template_test.go index c2e4928dbaf..4a96490d672 100644 --- a/pkg/campaign/template_test.go +++ b/pkg/campaign/template_test.go @@ -39,7 +39,7 @@ func TestRenderOrchestratorInstructions(t *testing.T) { shouldContain: []string{ "Query worker-created content", "Query current project state", - "Compare and identify gaps", + "Merge and identify gaps", "Decide processing order", "Decide updates", "Decide field values", @@ -94,19 +94,18 @@ func TestRenderProjectUpdateInstructions(t *testing.T) { shouldBeEmpty: false, }, { - name: "with project URL and tracker label", + name: "with project URL and campaign ID", data: CampaignPromptData{ ProjectURL: "https://github.com/orgs/test/projects/1", - TrackerLabel: "campaign:my-campaign", + CampaignID: "my-campaign", }, shouldContain: []string{ "Project Board Integration", "update-project", "https://github.com/orgs/test/projects/1", "Campaign ID", - "campaign:my-campaign", - "campaign_id:", - "CAMPAIGN_ID", + "my-campaign", + "campaign_id: \"my-campaign\"", }, shouldBeEmpty: false, }, diff --git a/pkg/campaign/validation.go b/pkg/campaign/validation.go index 9a14a2093b2..31c4feb8881 100644 --- a/pkg/campaign/validation.go +++ b/pkg/campaign/validation.go @@ -77,9 +77,7 @@ func ValidateSpec(spec *CampaignSpec) []string { } } - if strings.TrimSpace(spec.TrackerLabel) == "" { - problems = append(problems, "tracker-label should be set to link issues and PRs to this campaign") - } else if !strings.Contains(spec.TrackerLabel, ":") { + if strings.TrimSpace(spec.TrackerLabel) != "" && !strings.Contains(spec.TrackerLabel, ":") { problems = append(problems, "tracker-label should follow a namespaced pattern (for example: campaign:security-q1-2025)") } diff --git a/pkg/campaign/validation_test.go b/pkg/campaign/validation_test.go index dbd5b542db2..c2ebcbdc942 100644 --- a/pkg/campaign/validation_test.go +++ b/pkg/campaign/validation_test.go @@ -123,7 +123,7 @@ func TestValidateSpec_MissingWorkflows(t *testing.T) { } } -func TestValidateSpec_MissingTrackerLabel(t *testing.T) { +func TestValidateSpec_MissingTrackerLabelIsAllowed(t *testing.T) { spec := &CampaignSpec{ ID: "test-campaign", Name: "Test Campaign", @@ -132,20 +132,12 @@ func TestValidateSpec_MissingTrackerLabel(t *testing.T) { } problems := ValidateSpec(spec) - if len(problems) == 0 { - t.Fatal("Expected validation problems for missing tracker label") - } - - found := false for _, p := range problems { - if strings.Contains(p, "tracker-label should be set") { - found = true + if strings.Contains(p, "tracker-label") { + t.Errorf("Did not expect tracker-label problem when omitted, got: %v", problems) break } } - if !found { - t.Errorf("Expected tracker label validation problem, got: %v", problems) - } } func TestValidateSpec_InvalidTrackerLabelFormat(t *testing.T) { diff --git a/pkg/workflow/safe_output_validation_config.go b/pkg/workflow/safe_output_validation_config.go index 58727acfcbf..0742a091071 100644 --- a/pkg/workflow/safe_output_validation_config.go +++ b/pkg/workflow/safe_output_validation_config.go @@ -231,16 +231,19 @@ var ValidationConfig = map[string]TypeValidationConfig{ }, "update_project": { DefaultMax: 10, + CustomValidation: "updateProjectValidTarget", Fields: map[string]FieldValidation{ "project": {Required: true, Type: "string", Sanitize: true, MaxLength: 512, Pattern: "^https://github\\.com/(orgs|users)/[^/]+/projects/\\d+", PatternError: "must be a full GitHub project URL (e.g., https://github.com/orgs/myorg/projects/42)"}, // campaign_id is an optional field used by Campaign Workflows to tag project items. // When provided, the update-project safe output applies a "campaign:" label. // This is part of the campaign tracking convention but not required for general use. "campaign_id": {Type: "string", Sanitize: true, MaxLength: 128}, - "content_type": {Type: "string", Enum: []string{"issue", "pull_request"}}, + "content_type": {Type: "string", Enum: []string{"issue", "pull_request", "draft_issue"}}, "content_number": {OptionalPositiveInteger: true}, "issue": {OptionalPositiveInteger: true}, // Legacy "pull_request": {OptionalPositiveInteger: true}, // Legacy + "draft_title": {Type: "string", Sanitize: true, MaxLength: 256}, + "draft_body": {Type: "string", Sanitize: true, MaxLength: MaxBodyLength}, "fields": {Type: "object"}, }, }, diff --git a/pkg/workflow/safe_output_validation_config_test.go b/pkg/workflow/safe_output_validation_config_test.go index 4335116eb68..8553cc29924 100644 --- a/pkg/workflow/safe_output_validation_config_test.go +++ b/pkg/workflow/safe_output_validation_config_test.go @@ -30,6 +30,7 @@ func TestGetValidationConfigJSON(t *testing.T) { "assign_milestone", "assign_to_agent", "assign_to_user", + "update_project", "update_issue", "update_pull_request", "push_to_pull_request_branch", @@ -134,6 +135,23 @@ func TestGetValidationConfigForType(t *testing.T) { wantMax: 1, wantFields: []string{"title", "body", "labels", "parent", "temporary_id"}, }, + { + name: "update_project type", + typeName: "update_project", + wantFound: true, + wantMax: 10, + wantFields: []string{ + "project", + "campaign_id", + "content_type", + "content_number", + "issue", + "pull_request", + "draft_title", + "draft_body", + "fields", + }, + }, { name: "link_sub_issue type", typeName: "link_sub_issue", @@ -236,6 +254,7 @@ func TestValidationConfigConsistency(t *testing.T) { "requiresOneOf:title,body": true, "startLineLessOrEqualLine": true, "parentAndSubDifferent": true, + "updateProjectValidTarget": true, } for typeName, config := range ValidationConfig { From 7a3c47d79848a48a63789d23c1a826c0ffd04125 Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer Date: Fri, 2 Jan 2026 21:02:17 +0100 Subject: [PATCH 2/6] update for passing tests --- pkg/cli/completions.go | 11 ++- pkg/cli/completions_test.go | 167 +++++++++++------------------------- pkg/cli/status_command.go | 7 +- 3 files changed, 64 insertions(+), 121 deletions(-) diff --git a/pkg/cli/completions.go b/pkg/cli/completions.go index d6bf390f0e7..140223475a9 100644 --- a/pkg/cli/completions.go +++ b/pkg/cli/completions.go @@ -13,6 +13,8 @@ import ( var completionsLog = logger.New("cli:completions") +const workflowsDirAnnotationKey = "gh-aw-workflows-dir" + // getWorkflowDescription extracts the description field from a workflow's frontmatter // Returns empty string if the description is not found or if there's an error reading the file func getWorkflowDescription(filePath string) string { @@ -77,7 +79,14 @@ func ValidEngineNames() []string { func CompleteWorkflowNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { completionsLog.Printf("Completing workflow names with prefix: %s", toComplete) - mdFiles, err := getMarkdownWorkflowFiles() + workflowsDir := getWorkflowsDir() + if cmd != nil && cmd.Annotations != nil { + if overrideDir, ok := cmd.Annotations[workflowsDirAnnotationKey]; ok && overrideDir != "" { + workflowsDir = overrideDir + } + } + + mdFiles, err := getMarkdownWorkflowFilesInDir(workflowsDir) if err != nil { completionsLog.Printf("Failed to get workflow files: %v", err) return nil, cobra.ShellCompDirectiveNoFileComp diff --git a/pkg/cli/completions_test.go b/pkg/cli/completions_test.go index ddb4e0b4ee3..73ef75d51a6 100644 --- a/pkg/cli/completions_test.go +++ b/pkg/cli/completions_test.go @@ -142,15 +142,7 @@ No description workflow f.Close() } - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} tests := []struct { name string @@ -290,15 +282,7 @@ func TestCompleteWorkflowNames(t *testing.T) { f.Close() } - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} tests := []struct { name string @@ -340,15 +324,7 @@ func TestCompleteWorkflowNamesNoWorkflowsDir(t *testing.T) { // Create a temporary directory without .github/workflows tmpDir := t.TempDir() - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: filepath.Join(tmpDir, ".github", "workflows")}} completions, directive := CompleteWorkflowNames(cmd, nil, "") assert.Empty(t, completions) @@ -401,15 +377,7 @@ func TestCompleteWorkflowNamesWithSpecialCharacters(t *testing.T) { f.Close() } - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} tests := []struct { name string @@ -506,15 +474,7 @@ func TestCompleteWorkflowNamesWithInvalidFiles(t *testing.T) { err = os.WriteFile(invalidMd, []byte("not valid yaml frontmatter"), 0644) require.NoError(t, err) - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} completions, directive := CompleteWorkflowNames(cmd, nil, "") @@ -538,73 +498,58 @@ func TestCompleteWorkflowNamesWithInvalidFiles(t *testing.T) { // TestCompleteWorkflowNamesCaseSensitivity tests prefix matching is case-sensitive func TestCompleteWorkflowNamesCaseSensitivity(t *testing.T) { - // Create a temporary directory structure - tmpDir := t.TempDir() - workflowsDir := filepath.Join(tmpDir, ".github", "workflows") - require.NoError(t, os.MkdirAll(workflowsDir, 0755)) - - // Create test workflow files with different cases - testWorkflows := []string{ - "test-workflow.md", - "Test-Workflow.md", - "TEST-WORKFLOW.md", - "other-workflow.md", - } - for _, wf := range testWorkflows { - f, err := os.Create(filepath.Join(workflowsDir, wf)) - require.NoError(t, err) - f.Close() - } - - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} - - tests := []struct { - name string - toComplete string - expectContains []string - expectMissing []string + // Note: macOS filesystems are commonly case-insensitive. To keep this test reliable across + // environments, run each case-variant in its own temp directory. + variants := []struct { + name string + fileName string + matches []string + misses []string }{ { - name: "lowercase test prefix", - toComplete: "test", - expectContains: []string{"test-workflow"}, - expectMissing: []string{"Test-Workflow", "TEST-WORKFLOW"}, + name: "lowercase workflow name", + fileName: "test-workflow.md", + matches: []string{"test"}, + misses: []string{"Test", "TEST"}, }, { - name: "capitalized Test prefix", - toComplete: "Test", - expectContains: []string{"Test-Workflow"}, - expectMissing: []string{"test-workflow", "TEST-WORKFLOW"}, + name: "capitalized workflow name", + fileName: "Test-Workflow.md", + matches: []string{"Test"}, + misses: []string{"test", "TEST"}, }, { - name: "uppercase TEST prefix", - toComplete: "TEST", - expectContains: []string{"TEST-WORKFLOW"}, - expectMissing: []string{"test-workflow", "Test-Workflow"}, + name: "uppercase workflow name", + fileName: "TEST-WORKFLOW.md", + matches: []string{"TEST"}, + misses: []string{"test", "Test"}, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - completions, directive := CompleteWorkflowNames(cmd, nil, tt.toComplete) - assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive) - - // Verify expected completions are present - for _, expected := range tt.expectContains { - assert.Contains(t, completions, expected, "Expected completion '%s' not found", expected) + for _, v := range variants { + t.Run(v.name, func(t *testing.T) { + tmpDir := t.TempDir() + workflowsDir := filepath.Join(tmpDir, ".github", "workflows") + require.NoError(t, os.MkdirAll(workflowsDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(workflowsDir, v.fileName), []byte("# test\n"), 0644)) + require.NoError(t, os.WriteFile(filepath.Join(workflowsDir, "other-workflow.md"), []byte("# other\n"), 0644)) + + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} + + for _, prefix := range v.matches { + t.Run("matches_"+prefix, func(t *testing.T) { + completions, directive := CompleteWorkflowNames(cmd, nil, prefix) + assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive) + assert.Contains(t, completions, strings.TrimSuffix(v.fileName, ".md"), "Expected workflow not found") + }) } - // Verify unwanted completions are not present - for _, missing := range tt.expectMissing { - assert.NotContains(t, completions, missing, "Unexpected completion '%s' found", missing) + for _, prefix := range v.misses { + t.Run("misses_"+prefix, func(t *testing.T) { + completions, directive := CompleteWorkflowNames(cmd, nil, prefix) + assert.Equal(t, cobra.ShellCompDirectiveNoFileComp, directive) + assert.NotContains(t, completions, strings.TrimSuffix(v.fileName, ".md"), "Unexpected workflow found") + }) } }) } @@ -629,15 +574,7 @@ func TestCompleteWorkflowNamesExactMatch(t *testing.T) { f.Close() } - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} // Test exact match - should still return the match and any others with same prefix completions, directive := CompleteWorkflowNames(cmd, nil, "test") @@ -668,15 +605,7 @@ func TestCompleteWorkflowNamesLongNames(t *testing.T) { require.NoError(t, err) f.Close() - // Change to the temp directory - originalDir, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(tmpDir)) - defer func() { - _ = os.Chdir(originalDir) - }() - - cmd := &cobra.Command{} + cmd := &cobra.Command{Annotations: map[string]string{workflowsDirAnnotationKey: workflowsDir}} // Test completion with empty prefix should include both completions, directive := CompleteWorkflowNames(cmd, nil, "") diff --git a/pkg/cli/status_command.go b/pkg/cli/status_command.go index b42ec2440c4..88bd150a22b 100644 --- a/pkg/cli/status_command.go +++ b/pkg/cli/status_command.go @@ -369,8 +369,13 @@ func calculateTimeRemaining(stopTimeStr string) string { // StatusWorkflows shows status of workflows // getMarkdownWorkflowFiles finds all markdown files in .github/workflows directory func getMarkdownWorkflowFiles() ([]string, error) { - workflowsDir := getWorkflowsDir() + return getMarkdownWorkflowFilesInDir(getWorkflowsDir()) +} + +// getMarkdownWorkflowFilesInDir finds all markdown workflow files in the provided directory. +// This is primarily used for shell completions and tests to avoid relying on process-wide cwd. +func getMarkdownWorkflowFilesInDir(workflowsDir string) ([]string, error) { if _, err := os.Stat(workflowsDir); os.IsNotExist(err) { return nil, fmt.Errorf("no .github/workflows directory found") } From 94e97c5eb35ea8740be35a6972ca638570e24970 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:15:20 +0100 Subject: [PATCH 3/6] fix: resolve linting errors in test files (#8672) --- pkg/campaign/orchestrator_test.go | 8 ++++---- pkg/campaign/template_test.go | 4 ++-- pkg/cli/compile_integration_test.go | 1 - pkg/cli/interfaces_test.go | 6 +++--- pkg/cli/run_command_test.go | 1 - pkg/cli/status_command.go | 1 - pkg/workflow/safe_output_validation_config.go | 2 +- 7 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pkg/campaign/orchestrator_test.go b/pkg/campaign/orchestrator_test.go index 1c698f48400..9d41a973268 100644 --- a/pkg/campaign/orchestrator_test.go +++ b/pkg/campaign/orchestrator_test.go @@ -64,10 +64,10 @@ func TestBuildOrchestrator_BasicShape(t *testing.T) { func TestBuildOrchestrator_NoTrackerLabelDoesNotMentionTracker(t *testing.T) { spec := &CampaignSpec{ - ID: "test-campaign", - Name: "Test Campaign", - ProjectURL: "https://github.com/orgs/test/projects/1", - Workflows: []string{"test-workflow"}, + ID: "test-campaign", + Name: "Test Campaign", + ProjectURL: "https://github.com/orgs/test/projects/1", + Workflows: []string{"test-workflow"}, TrackerLabel: "", } diff --git a/pkg/campaign/template_test.go b/pkg/campaign/template_test.go index 4a96490d672..ea5ba83db87 100644 --- a/pkg/campaign/template_test.go +++ b/pkg/campaign/template_test.go @@ -96,8 +96,8 @@ func TestRenderProjectUpdateInstructions(t *testing.T) { { name: "with project URL and campaign ID", data: CampaignPromptData{ - ProjectURL: "https://github.com/orgs/test/projects/1", - CampaignID: "my-campaign", + ProjectURL: "https://github.com/orgs/test/projects/1", + CampaignID: "my-campaign", }, shouldContain: []string{ "Project Board Integration", diff --git a/pkg/cli/compile_integration_test.go b/pkg/cli/compile_integration_test.go index 7439a4fa33d..8801f7a4f3e 100644 --- a/pkg/cli/compile_integration_test.go +++ b/pkg/cli/compile_integration_test.go @@ -4,7 +4,6 @@ package cli import ( "bytes" - "context" "io" "os" "os/exec" diff --git a/pkg/cli/interfaces_test.go b/pkg/cli/interfaces_test.go index ad066e3a4c3..74566bd4659 100644 --- a/pkg/cli/interfaces_test.go +++ b/pkg/cli/interfaces_test.go @@ -27,21 +27,21 @@ func TestCommandProviderInterface(t *testing.T) { t.Run("GenBashCompletion", func(t *testing.T) { var buf bytes.Buffer err := provider.GenBashCompletion(&buf) - assert.NoError(t, err, "GenBashCompletion should not error") + require.NoError(t, err, "GenBashCompletion should not error") assert.NotEmpty(t, buf.String(), "GenBashCompletion should generate content") }) t.Run("GenZshCompletion", func(t *testing.T) { var buf bytes.Buffer err := provider.GenZshCompletion(&buf) - assert.NoError(t, err, "GenZshCompletion should not error") + require.NoError(t, err, "GenZshCompletion should not error") assert.NotEmpty(t, buf.String(), "GenZshCompletion should generate content") }) t.Run("GenFishCompletion", func(t *testing.T) { var buf bytes.Buffer err := provider.GenFishCompletion(&buf, true) - assert.NoError(t, err, "GenFishCompletion should not error") + require.NoError(t, err, "GenFishCompletion should not error") assert.NotEmpty(t, buf.String(), "GenFishCompletion should generate content") }) } diff --git a/pkg/cli/run_command_test.go b/pkg/cli/run_command_test.go index 3c73035e2cd..ceaaec4290d 100644 --- a/pkg/cli/run_command_test.go +++ b/pkg/cli/run_command_test.go @@ -3,7 +3,6 @@ package cli import ( - "context" "fmt" "strings" "testing" diff --git a/pkg/cli/status_command.go b/pkg/cli/status_command.go index 88bd150a22b..67aac7d8241 100644 --- a/pkg/cli/status_command.go +++ b/pkg/cli/status_command.go @@ -372,7 +372,6 @@ func getMarkdownWorkflowFiles() ([]string, error) { return getMarkdownWorkflowFilesInDir(getWorkflowsDir()) } - // getMarkdownWorkflowFilesInDir finds all markdown workflow files in the provided directory. // This is primarily used for shell completions and tests to avoid relying on process-wide cwd. func getMarkdownWorkflowFilesInDir(workflowsDir string) ([]string, error) { diff --git a/pkg/workflow/safe_output_validation_config.go b/pkg/workflow/safe_output_validation_config.go index 0742a091071..6971af4eac6 100644 --- a/pkg/workflow/safe_output_validation_config.go +++ b/pkg/workflow/safe_output_validation_config.go @@ -230,7 +230,7 @@ var ValidationConfig = map[string]TypeValidationConfig{ }, }, "update_project": { - DefaultMax: 10, + DefaultMax: 10, CustomValidation: "updateProjectValidTarget", Fields: map[string]FieldValidation{ "project": {Required: true, Type: "string", Sanitize: true, MaxLength: 512, Pattern: "^https://github\\.com/(orgs|users)/[^/]+/projects/\\d+", PatternError: "must be a full GitHub project URL (e.g., https://github.com/orgs/myorg/projects/42)"}, From 60d68d03b7b9eb34ebc127c04a1567f586d70977 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 12:31:53 -0800 Subject: [PATCH 4/6] Fix generateFooter call signature in mark_pull_request_as_ready_for_review (#8674) --- .../js/mark_pull_request_as_ready_for_review.cjs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/mark_pull_request_as_ready_for_review.cjs b/actions/setup/js/mark_pull_request_as_ready_for_review.cjs index d74d2fd6d8c..bfef7be53bc 100644 --- a/actions/setup/js/mark_pull_request_as_ready_for_review.cjs +++ b/actions/setup/js/mark_pull_request_as_ready_for_review.cjs @@ -4,6 +4,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateFooter } = require("./generate_footer.cjs"); const { sanitizeContent } = require("./sanitize_content.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Generate staged preview for mark-pull-request-as-ready-for-review items @@ -69,10 +70,17 @@ async function markPullRequestAsReadyForReview(github, owner, repo, prNumber, re // Add comment with reason const workflowName = process.env.GH_AW_WORKFLOW_NAME || "GitHub Agentic Workflow"; + const workflowSource = process.env.GH_AW_WORKFLOW_SOURCE || ""; + const workflowSourceURL = process.env.GH_AW_WORKFLOW_SOURCE_URL || ""; const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`; + + // Extract triggering context for footer generation + const triggeringIssueNumber = context.payload?.issue?.number && !context.payload?.issue?.pull_request ? context.payload.issue.number : undefined; + const triggeringPRNumber = prNumber; + const triggeringDiscussionNumber = context.payload?.discussion?.number; const sanitizedReason = sanitizeContent(reason); - const footer = generateFooter(workflowName, runUrl); + const footer = generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber); const commentBody = `${sanitizedReason}\n\n${footer}`; await github.rest.issues.createComment({ @@ -148,8 +156,9 @@ async function main() { try { await markPullRequestAsReadyForReview(github, context.repo.owner, context.repo.repo, prNumber, item.reason); } catch (error) { - core.error(`Failed to mark PR #${prNumber} as ready for review: ${error.message}`); - core.setFailed(`Failed to mark PR #${prNumber} as ready for review: ${error.message}`); + const errorMessage = getErrorMessage(error); + core.error(`Failed to mark PR #${prNumber} as ready for review: ${errorMessage}`); + core.setFailed(`Failed to mark PR #${prNumber} as ready for review: ${errorMessage}`); } } } From 4eebd847405c07d1731dd0c3cc1b033bed71065d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:02:43 +0100 Subject: [PATCH 5/6] fix: update test expectations for handler manager and context parameters (#8675) --- .github/workflows/campaign-manager.lock.yml | 16 +- ...ty-maintenance-project67.campaign.lock.yml | 124 +++++++---- ...size-reduction-project64.campaign.lock.yml | 193 +++++++++++------- ...ayground-org-project-update-issue.lock.yml | 16 +- pkg/cli/mcp_server_error_codes_test.go | 2 +- pkg/cli/mcp_server_json_integration_test.go | 2 +- pkg/cli/run_command_test.go | 19 +- pkg/cli/templates/github-agentic-workflows.md | 77 +++++++ pkg/workflow/safe_outputs_integration_test.go | 5 +- 9 files changed, 321 insertions(+), 133 deletions(-) diff --git a/.github/workflows/campaign-manager.lock.yml b/.github/workflows/campaign-manager.lock.yml index fde9369cfa8..e9af039e9ed 100644 --- a/.github/workflows/campaign-manager.lock.yml +++ b/.github/workflows/campaign-manager.lock.yml @@ -490,9 +490,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -510,7 +521,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF diff --git a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml index 272e3a69af9..4a75c022759 100644 --- a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml +++ b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml @@ -365,9 +365,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -385,7 +396,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF @@ -515,7 +527,7 @@ jobs: This workflow orchestrates the 'Documentation Quality & Maintenance Campaign (Project 67)' campaign. - - Tracker label: `campaign:docs-quality-maintenance-project67` + - Tracker label (optional ingestion): `campaign:docs-quality-maintenance-project67` - Objective: Maintain high-quality, accessible, and consistent documentation following the Diátaxis framework while ensuring all docs are accurate, complete, and user-friendly - KPIs: - Documentation coverage of features (primary): baseline 85 → target 95 over 90 days percent @@ -534,7 +546,7 @@ jobs: ## Campaign Orchestrator Rules - This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. It also maintains the campaign dashboard by ensuring the GitHub Project stays in sync with the campaign's tracker label. + This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. The GitHub Project is the single source of truth for campaign membership and state. ### Traffic and rate limits (required) @@ -592,7 +604,7 @@ jobs: - Use an ISO date (UTC) filename, for example: `metrics/2025-12-22.json`. - Keep snapshots append-only: write a new file per run; do not rewrite historical snapshots. - If a KPI is present, record its computed value and trend (Improving/Flat/Regressing) in the kpi_trends array. - - Count tasks from all sources: tracker-labeled issues, worker-created issues, and project board items. + - Count tasks from all sources: project board items (canonical), plus any newly discovered tracker-labeled or worker-created items that will be added. - Set tasks_total to the total number of unique tasks discovered in this run. - Set tasks_completed to the count of tasks with state "Done" or closed status. @@ -617,7 +629,7 @@ jobs: 10. **Predefined fields only** - Only update explicitly defined project board fields 11. **Explicit outcomes** - Record actual outcomes, never infer status 12. **Idempotent operations** - Re-execution produces the same result without corruption - 13. **Dashboard synchronization** - Keep Project items in sync with tracker-labeled issues/PRs + 13. **Dashboard synchronization** - Keep the Project board in sync with discovered work (Project items are canonical; tracker labels are optional ingestion) ### Objective and KPIs (first-class) @@ -656,13 +668,18 @@ jobs: #### Phase 1: Read State (Discovery) - 1. **Query tracker-labeled items** - Search for issues and PRs matching the campaign's tracker label + 1. **Query current project state (canonical)** - Read the GitHub Project board + - Retrieve all items currently on the project board + - For each item, record: content type, URL (if present), draft title (if present), status field value, and other predefined field values + - Create a snapshot of current board state + + 2. **Query tracker-labeled items (optional ingestion)** - If a tracker label is configured, search for issues and PRs matching it - Search: `repo:OWNER/REPO label:TRACKER_LABEL` for all open and closed items - If governance opt-out labels are configured, exclude items with those labels - Collect all matching issue/PR URLs - Record metadata: number, title, state (open/closed), created date, updated date - 2. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids + 3. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids - Worker workflows: daily-doc-updater, docs-noob-tester, daily-multi-device-docs-tester, unbloat-docs, developer-docs-consolidator, technical-doc-writer - **IMPORTANT**: You MUST perform SEPARATE searches for EACH worker workflow listed above - **IMPORTANT**: Workers may create different types of content (issues, PRs, discussions, comments). Search ALL content types to discover all worker outputs. @@ -702,18 +719,29 @@ jobs: - Combine results from all worker searches into a single list of discovered items - Note: Comments are discovered via their parent issue/PR - the issue/PR is what gets added to the board - 3. **Query current project state** - Read the GitHub Project board - - Retrieve all items currently on the project board - - For each item, record: issue URL, status field value, other predefined field values - - Create a snapshot of current board state - - 4. **Compare and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) - - Items from step 1 or 2 not on board = **new work discovered** (report count) - - Items on board with state mismatch = **status updates needed** (report count) + 4. **Merge and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) + - Items on the board are **in scope** by definition (canonical membership) + - Items from steps 2-3 not on board = **new work discovered** (report count) + - Items on board with state mismatch vs issue/PR state = **status updates needed** (report count) - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** (report count) - - Items on board but no longer found = **check if archived/deleted** (report count) + - Items on board but no longer accessible = **check if archived/deleted** (report count) - **CRITICAL**: This comparison is for reporting and planning only. In Phase 3, you MUST send ALL discovered items to update-project regardless of whether they appear to be on the board. The update-project tool handles duplicate detection automatically. + 4.8 **Locate the campaign hub issue (optional but recommended)** + + If you have permission and comment writes are allowed, attempt to locate the campaign hub (“epic”) issue for posting per-run summaries. + + Deterministic matching rules: + - Prefer an issue that has BOTH labels: `campaign-tracker` AND `campaign:docs-quality-maintenance-project67`. + - If none found, do NOT guess. Proceed without an epic issue comment. + - If multiple matches, treat as ambiguous and proceed without commenting (report ambiguity). + + If a single hub issue is found, treat it as an in-scope item for synchronization: + - Include it in the Phase 3 `update-project` operations so it is present on the Project board (idempotent). + - When updating the hub issue item, set (when the fields exist): + - `worker_workflow = "orchestrator"` + - `human_oversight_required = "Yes"` + #### Phase 2: Make Decisions (Planning) 4.5 **Deterministic planner step (required when objective/KPIs are present)** @@ -736,12 +764,13 @@ jobs: } ``` - 5. **Decide processing order (with pacing)** - For items discovered in steps 1-2: - - **CRITICAL**: ALL discovered items (both tracker-labeled from step 1 AND worker-created from step 2) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. + 5. **Decide processing order (with pacing)** - For items discovered in steps 1-3: + - **CRITICAL**: ALL discovered items (project items from step 1, tracker-labeled from step 2, and worker-created from step 3) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. - If `governance.max-new-items-per-run` is set, process at most that many items in this single run (remaining items will be processed in subsequent runs) - When applying the governance limit, prioritize in this order: - 1. Tracker-labeled items (campaign tasks) - process oldest first - 2. Worker-created items (worker outputs) - process oldest first + 1. Project board items (canonical scope) - process oldest first + 2. Tracker-labeled items (optional ingestion) - process oldest first + 3. Worker-created items (worker outputs) - process oldest first - Determine appropriate status field value based on item state: - Open issue/PR/discussion → "Todo" status - Closed issue/discussion → "Done" status @@ -770,12 +799,18 @@ jobs: #### Phase 3: Write State (Execution) - **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-2, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. + **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-3, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. 8. **Execute project updates** - Send update-project for ALL discovered items - - Process ALL items from steps 1-2 (both tracker-labeled and worker-created), up to the governance limit if set + - Process ALL items from steps 1-3 (project items, tracker-labeled, and worker-created), up to the governance limit if set - Use `update-project` safe-output for EVERY discovered item - - Include fields from steps 5-6.5: `status`, `worker_workflow`, `priority`, `size`, etc. + PROMPT_EOF + - name: Append prompt (part 2) + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: | + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + - Include fields from steps 5-6.5: `status`, `worker_workflow`, `human_oversight_required`, `priority`, `size`, etc. - **The update-project tool will automatically**: - Skip adding items that are already on the board (idempotent add) - Update fields for items already on the board @@ -793,27 +828,36 @@ jobs: #### Phase 4: Report (Output) 10. **Generate status report** - Summarize execution results: - - Total items discovered via tracker label (by type: issues, PRs) - - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) - PROMPT_EOF - - name: Append prompt (part 2) - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: | - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - - Items processed with update-project this run (count and URLs, broken down by: tracker-labeled vs worker-created) + - Total items currently on the project board (canonical) + - Total items discovered via tracker label (optional ingestion, by type: issues, PRs) + - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) + - Items processed with update-project this run (count and URLs, broken down by: project-board vs tracker-labeled vs worker-created) - Items skipped due to governance limits (count, type, and why - noting they will be processed in next run) - Current campaign metrics: open vs closed, progress percentage - Any failures encountered during update-project operations - Campaign completion status + 11. **Post a hub issue comment (if hub issue was found and add-comment is allowed)** + + Post a short comment to the campaign hub issue that includes: + - Link to the Project board + - What was processed this run (counts + a few representative URLs) + - The current “Needs human” queue size (items with `human_oversight_required = Yes` if that field is used) + - If repo-memory metrics are enabled, include the path to the latest metrics snapshot written this run + + Do not paste large JSON blobs. Keep the comment concise and human-scannable. + ### Predefined Project Fields Only these fields may be updated on the project board: - - `status` (required) - Values: "Todo", "In Progress", "Done" + - `status` (required) - Values: "Todo", "In Progress", "Blocked", "Done" + - `worker_workflow` (optional) - String (recommended: the worker workflow ID/name) + - `human_oversight_required` (optional) - Values: "Yes", "No" (powers a dedicated human review queue) - `priority` (optional) - Values: "High", "Medium", "Low" - `size` (optional) - Values: "Small", "Medium", "Large" + - `start_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + - `end_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) - `campaign_status` (metadata) - Values: "active", "completed" Do NOT update any other fields or create custom fields. @@ -843,9 +887,7 @@ jobs: Execute state writes using the `update-project` safe-output. All writes must target this exact project URL: **Project URL**: https://github.com/orgs/githubnext/projects/67 - - **Campaign ID**: Extract from tracker label `campaign:docs-quality-maintenance-project67` (format: `campaign:CAMPAIGN_ID`) - + **Campaign ID**: `docs-quality-maintenance-project67` (recommended to tag items via the optional `campaign_id` field) #### Adding New Issues/PRs @@ -855,7 +897,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/67" content_type: "issue" # or "pull_request" content_number: 123 # Extract number from URL like https://github.com/owner/repo/issues/123 - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign fields: status: "Todo" # or "Done" if issue/PR is already closed/merged ``` @@ -879,8 +921,9 @@ jobs: content_number: 123 # Extract from URL fields: status: "Todo" # or "In Progress", "Blocked", "Done" - campaign_id: "CAMPAIGN_ID" # Extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + human_oversight_required: "No" # or "Yes" - powers a dedicated human review queue priority: "High" # or "Medium", "Low" - enables priority-based views effort: "Medium" # or "Small", "Large" - enables capacity planning team: "TEAM_NAME" # Optional: for team-based grouping @@ -889,6 +932,7 @@ jobs: **Custom Field Benefits**: - `worker_workflow`: Groups items by workflow in Roadmap swimlanes; enables "Slice by" filtering in Table views (orchestrator populates this by discovering which worker created the item via tracker-id) + - `human_oversight_required`: Creates an explicit human review queue; use a "Needs human" view filtered to "Yes" - `priority`: Enables priority-based filtering and sorting - `effort`: Supports capacity planning and workload distribution - `team`: Enables team-based grouping for multi-team campaigns @@ -906,7 +950,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/67" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign fields: status: "Done" # or "In Progress", "Todo" ``` diff --git a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml index c8ee37d9148..3d014fa7828 100644 --- a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml +++ b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml @@ -365,9 +365,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -385,7 +396,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF @@ -515,7 +527,7 @@ jobs: This workflow orchestrates the 'Go File Size Reduction Campaign (Project 64)' campaign. - - Tracker label: `campaign:go-file-size-reduction-project64` + - Tracker label (optional ingestion): `campaign:go-file-size-reduction-project64` - Objective: Reduce all Go files to ≤800 lines of code while maintaining test coverage and preventing regressions - KPIs: - Files reduced to target size (primary): baseline 0 → target 100 over 90 days percent @@ -533,7 +545,7 @@ jobs: ## Campaign Orchestrator Rules - This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. It also maintains the campaign dashboard by ensuring the GitHub Project stays in sync with the campaign's tracker label. + This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. The GitHub Project is the single source of truth for campaign membership and state. ### Traffic and rate limits (required) @@ -591,7 +603,7 @@ jobs: - Use an ISO date (UTC) filename, for example: `metrics/2025-12-22.json`. - Keep snapshots append-only: write a new file per run; do not rewrite historical snapshots. - If a KPI is present, record its computed value and trend (Improving/Flat/Regressing) in the kpi_trends array. - - Count tasks from all sources: tracker-labeled issues, worker-created issues, and project board items. + - Count tasks from all sources: project board items (canonical), plus any newly discovered tracker-labeled or worker-created items that will be added. - Set tasks_total to the total number of unique tasks discovered in this run. - Set tasks_completed to the count of tasks with state "Done" or closed status. @@ -616,7 +628,7 @@ jobs: 10. **Predefined fields only** - Only update explicitly defined project board fields 11. **Explicit outcomes** - Record actual outcomes, never infer status 12. **Idempotent operations** - Re-execution produces the same result without corruption - 13. **Dashboard synchronization** - Keep Project items in sync with tracker-labeled issues/PRs + 13. **Dashboard synchronization** - Keep the Project board in sync with discovered work (Project items are canonical; tracker labels are optional ingestion) ### Objective and KPIs (first-class) @@ -653,13 +665,18 @@ jobs: #### Phase 1: Read State (Discovery) - 1. **Query tracker-labeled items** - Search for issues and PRs matching the campaign's tracker label + 1. **Query current project state (canonical)** - Read the GitHub Project board + - Retrieve all items currently on the project board + - For each item, record: content type, URL (if present), draft title (if present), status field value, and other predefined field values + - Create a snapshot of current board state + + 2. **Query tracker-labeled items (optional ingestion)** - If a tracker label is configured, search for issues and PRs matching it - Search: `repo:OWNER/REPO label:TRACKER_LABEL` for all open and closed items - If governance opt-out labels are configured, exclude items with those labels - Collect all matching issue/PR URLs - Record metadata: number, title, state (open/closed), created date, updated date - 2. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids + 3. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids - Worker workflows: daily-file-diet - **IMPORTANT**: You MUST perform SEPARATE searches for EACH worker workflow listed above - **IMPORTANT**: Workers may create different types of content (issues, PRs, discussions, comments). Search ALL content types to discover all worker outputs. @@ -674,16 +691,28 @@ jobs: - Combine results from all worker searches into a single list of discovered items - Note: Comments are discovered via their parent issue/PR - the issue/PR is what gets added to the board - 3. **Query current project state** - Read the GitHub Project board - - Retrieve all items currently on the project board - - For each item, record: issue URL, status field value, other predefined field values - - Create a snapshot of current board state + 4. **Merge and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) + - Items on the board are **in scope** by definition (canonical membership) + - Items from steps 2-3 not on board = **new work discovered** (report count) + - Items on board with state mismatch vs issue/PR state = **status updates needed** (report count) + - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** (report count) + - Items on board but no longer accessible = **check if archived/deleted** (report count) + - **CRITICAL**: This comparison is for reporting and planning only. In Phase 3, you MUST send ALL discovered items to update-project regardless of whether they appear to be on the board. The update-project tool handles duplicate detection automatically. + + 4.8 **Locate the campaign hub issue (optional but recommended)** - 4. **Compare and identify gaps** - Determine what needs updating - - Items from step 1 or 2 not on board = **new work to add** - - Items on board with state mismatch = **status to update** - - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** - - Items on board but no longer found = **check if archived/deleted** + If you have permission and comment writes are allowed, attempt to locate the campaign hub (“epic”) issue for posting per-run summaries. + + Deterministic matching rules: + - Prefer an issue that has BOTH labels: `campaign-tracker` AND `campaign:go-file-size-reduction-project64`. + - If none found, do NOT guess. Proceed without an epic issue comment. + - If multiple matches, treat as ambiguous and proceed without commenting (report ambiguity). + + If a single hub issue is found, treat it as an in-scope item for synchronization: + - Include it in the Phase 3 `update-project` operations so it is present on the Project board (idempotent). + - When updating the hub issue item, set (when the fields exist): + - `worker_workflow = "orchestrator"` + - `human_oversight_required = "Yes"` #### Phase 2: Make Decisions (Planning) @@ -707,32 +736,34 @@ jobs: } ``` - 5. **Decide additions (with pacing)** - For each new item discovered: - - **CRITICAL**: All discovered items (both tracker-labeled from step 1 AND worker-created from step 2) MUST be added to the board to maintain synchronization between the campaign state and the project board. - - Decision: Add to board? (Default: **YES** for all items with tracker label or worker tracker-id) - - If `governance.max-new-items-per-run` is set, add at most that many new items in this single run (remaining items will be added in subsequent runs) + 5. **Decide processing order (with pacing)** - For items discovered in steps 1-3: + - **CRITICAL**: ALL discovered items (project items from step 1, tracker-labeled from step 2, and worker-created from step 3) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. + - If `governance.max-new-items-per-run` is set, process at most that many items in this single run (remaining items will be processed in subsequent runs) - When applying the governance limit, prioritize in this order: - 1. Tracker-labeled items (campaign tasks) - add oldest first - 2. Worker-created items (worker outputs) - add oldest first - - Determine initial status field value based on item state: + 1. Project board items (canonical scope) - process oldest first + 2. Tracker-labeled items (optional ingestion) - process oldest first + 3. Worker-created items (worker outputs) - process oldest first + - Determine appropriate status field value based on item state: - Open issue/PR/discussion → "Todo" status - Closed issue/discussion → "Done" status - Merged PR → "Done" status + - **IMPORTANT**: Do NOT skip items that appear to be on the board already. Step 4 comparison is for reporting only. In Phase 3, send ALL items to update-project. - 6. **Decide updates (no downgrade)** - For each existing board item with mismatched state: - - Decision: Update status field? (Default: yes if item state changed) - - If `governance.do-not-downgrade-done-items` is true, do not move items from Done back to active status - - Determine new status field value: + 6. **Decide updates (no downgrade)** - For status field value determination: + - Determine appropriate status based on item state (open/closed/merged) + - If `governance.do-not-downgrade-done-items` is true, preserve "Done" status for items that are already marked as done on the board + - Status field mapping: - Open issue/PR/discussion → "In Progress" or "Todo" - Closed issue/discussion → "Done" - Merged PR → "Done" + - **IMPORTANT**: This is for determining what status value to send to update-project, not for deciding whether to send the request. Send ALL discovered items to update-project in Phase 3. - 6.5 **Decide field updates** - For each existing board item, check for missing custom fields: - - If item is missing `worker_workflow` field: - - Search item body (issue/PR/discussion) for tracker-id (e.g., ``) - - If tracker-id matches a worker in `workflows`, populate `worker_workflow` field with that worker ID - - Only update fields that exist on the project board - - Skip items that already have all required fields populated + 6.5 **Decide field values** - For custom field population: + - Determine which custom fields should be populated based on item metadata + - If item has a worker tracker-id in its body (e.g., ``): + - Extract the worker ID and prepare to populate `worker_workflow` field + - Prepare other custom field values based on item properties + - **IMPORTANT**: This is for determining field values to send, not for filtering items. Send ALL discovered items to update-project in Phase 3. 7. **Decide completion** - Check campaign completion criteria: - If all discovered items (issues/PRs/discussions) are closed/merged AND all board items are "Done" → Campaign complete @@ -740,49 +771,65 @@ jobs: #### Phase 3: Write State (Execution) - 8. **Execute additions** - Add new items to project board - - Add ALL items identified in step 5 (both tracker-labeled and worker-created), up to the governance limit if set - - Use `update-project` safe-output for each new item - - Set predefined fields: `status` (required), optionally `priority`, `size` - - If worker tracker-id is found in item body (issue/PR/discussion), populate `worker_workflow` field - - Record outcome: success or failure with error details - - If governance limit is reached, log remaining items and note they will be processed in the next run + **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-3, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. - 9. **Execute status updates** - Update existing board items with status changes - - Use `update-project` safe-output for each status change - - Update only predefined fields: `status` and related metadata - - Record outcome: success or failure with error details - - 9.5 **Execute field updates** - Update existing board items with missing custom fields - - Use `update-project` safe-output for each item with missing fields - - Populate missing fields identified in step 6.5 (e.g., `worker_workflow`) + 8. **Execute project updates** - Send update-project for ALL discovered items + - Process ALL items from steps 1-3 (project items, tracker-labeled, and worker-created), up to the governance limit if set + - Use `update-project` safe-output for EVERY discovered item + - Include fields from steps 5-6.5: `status`, `worker_workflow`, `human_oversight_required`, `priority`, `size`, etc. + - **The update-project tool will automatically**: + - Skip adding items that are already on the board (idempotent add) + - Update fields for items already on the board + - Add new items that are not yet on the board - Record outcome: success or failure with error details + - If governance limit is reached, log remaining items and note they will be processed in the next run + - **DO NOT**: Check if items are already on the board before sending requests - this causes synchronization bugs + - **DO NOT**: Skip items that appear to be on the board - send them all and let the tool handle idempotency - 10. **Record completion state** - If campaign is complete: + 9. **Record completion state** - If campaign is complete: - Mark project metadata field `campaign_status` as "completed" - Do NOT create new work or modify existing items - This is a terminal state #### Phase 4: Report (Output) - 11. **Generate status report** - Summarize execution results: - - Total items discovered via tracker label (by type: issues, PRs) - - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) - - Items added to board this run (count and URLs, broken down by: tracker-labeled vs worker-created) - - Items updated on board this run (count and status changes) - - Items with fields populated this run (count and which fields, e.g., worker_workflow) - - Items skipped due to governance limits (count, type, and why - noting they will be added in next run) + 10. **Generate status report** - Summarize execution results: + - Total items currently on the project board (canonical) + - Total items discovered via tracker label (optional ingestion, by type: issues, PRs) + - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) + - Items processed with update-project this run (count and URLs, broken down by: project-board vs tracker-labeled vs worker-created) + - Items skipped due to governance limits (count, type, and why - noting they will be processed in next run) - Current campaign metrics: open vs closed, progress percentage - - Any failures encountered during writes + - Any failures encountered during update-project operations - Campaign completion status + 11. **Post a hub issue comment (if hub issue was found and add-comment is allowed)** + + Post a short comment to the campaign hub issue that includes: + - Link to the Project board + - What was processed this run (counts + a few representative URLs) + - The current “Needs human” queue size (items with `human_oversight_required = Yes` if that field is used) + - If repo-memory metrics are enabled, include the path to the latest metrics snapshot written this run + + Do not paste large JSON blobs. Keep the comment concise and human-scannable. + ### Predefined Project Fields Only these fields may be updated on the project board: - - `status` (required) - Values: "Todo", "In Progress", "Done" + - `status` (required) - Values: "Todo", "In Progress", "Blocked", "Done" + - `worker_workflow` (optional) - String (recommended: the worker workflow ID/name) + - `human_oversight_required` (optional) - Values: "Yes", "No" (powers a dedicated human review queue) - `priority` (optional) - Values: "High", "Medium", "Low" - `size` (optional) - Values: "Small", "Medium", "Large" + - `start_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + - `end_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + PROMPT_EOF + - name: Append prompt (part 2) + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: | + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - `campaign_status` (metadata) - Values: "active", "completed" Do NOT update any other fields or create custom fields. @@ -798,21 +845,21 @@ jobs: ### Idempotency Guarantee - All operations must be idempotent: - - Adding an item (issue/PR/discussion) already on the board → No-op (do not duplicate) - - Updating a status that matches current value → No-op (no change recorded) + **The update-project tool handles idempotency automatically.** You MUST send update-project requests for ALL discovered items. The tool will: + - Adding an item already on the board → Skips the add operation, but still updates fields (handled by tool) + - Updating a status that matches current value → No-op (handled by tool) - Marking a completed campaign as completed → No-op (terminal state preserved) - Re-running the orchestrator produces consistent results regardless of how many times it executes. + **CRITICAL**: Do NOT try to implement idempotency in your orchestrator logic by checking if items are already on the board before sending requests. This causes synchronization bugs where items are discovered but not processed. Always send ALL discovered items to update-project and let the tool handle duplicate detection. + + Re-running the orchestrator produces consistent results regardless of how many times it executes, because the update-project tool is idempotent. ### Project Board Integration Execute state writes using the `update-project` safe-output. All writes must target this exact project URL: **Project URL**: https://github.com/orgs/githubnext/projects/64 - - **Campaign ID**: Extract from tracker label `campaign:go-file-size-reduction-project64` (format: `campaign:CAMPAIGN_ID`) - + **Campaign ID**: `go-file-size-reduction-project64` (recommended to tag items via the optional `campaign_id` field) #### Adding New Issues/PRs @@ -822,7 +869,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/64" content_type: "issue" # or "pull_request" content_number: 123 # Extract number from URL like https://github.com/owner/repo/issues/123 - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign fields: status: "Todo" # or "Done" if issue/PR is already closed/merged ``` @@ -842,18 +889,13 @@ jobs: ``` update-project: project: "https://github.com/orgs/githubnext/projects/64" - PROMPT_EOF - - name: Append prompt (part 2) - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: | - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL fields: status: "Todo" # or "In Progress", "Blocked", "Done" - campaign_id: "CAMPAIGN_ID" # Extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + human_oversight_required: "No" # or "Yes" - powers a dedicated human review queue priority: "High" # or "Medium", "Low" - enables priority-based views effort: "Medium" # or "Small", "Large" - enables capacity planning team: "TEAM_NAME" # Optional: for team-based grouping @@ -862,6 +904,7 @@ jobs: **Custom Field Benefits**: - `worker_workflow`: Groups items by workflow in Roadmap swimlanes; enables "Slice by" filtering in Table views (orchestrator populates this by discovering which worker created the item via tracker-id) + - `human_oversight_required`: Creates an explicit human review queue; use a "Needs human" view filtered to "Yes" - `priority`: Enables priority-based filtering and sorting - `effort`: Supports capacity planning and workload distribution - `team`: Enables team-based grouping for multi-team campaigns @@ -879,7 +922,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/64" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign fields: status: "Done" # or "In Progress", "Todo" ``` diff --git a/.github/workflows/playground-org-project-update-issue.lock.yml b/.github/workflows/playground-org-project-update-issue.lock.yml index 0d454e87830..1026bd2224e 100644 --- a/.github/workflows/playground-org-project-update-issue.lock.yml +++ b/.github/workflows/playground-org-project-update-issue.lock.yml @@ -313,9 +313,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -333,7 +344,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF diff --git a/pkg/cli/mcp_server_error_codes_test.go b/pkg/cli/mcp_server_error_codes_test.go index 76bb35937e2..4b14e3df86b 100644 --- a/pkg/cli/mcp_server_error_codes_test.go +++ b/pkg/cli/mcp_server_error_codes_test.go @@ -194,7 +194,7 @@ func TestMCPServer_ErrorCodes_InternalError(t *testing.T) { params := &mcp.CallToolParams{ Name: "audit", Arguments: map[string]any{ - "run_id": int64(1), // Invalid run ID + "run_id_or_url": "1", // Invalid run ID (as string) }, } diff --git a/pkg/cli/mcp_server_json_integration_test.go b/pkg/cli/mcp_server_json_integration_test.go index 98797204947..003f83c3dab 100644 --- a/pkg/cli/mcp_server_json_integration_test.go +++ b/pkg/cli/mcp_server_json_integration_test.go @@ -453,7 +453,7 @@ func TestMCPServer_AllToolsReturnContent(t *testing.T) { name: "audit", toolName: "audit", args: map[string]any{ - "run_id": int64(1), + "run_id_or_url": "1", }, expectJSON: false, // May return error message mayFailInTest: true, // Expected to fail with invalid run ID diff --git a/pkg/cli/run_command_test.go b/pkg/cli/run_command_test.go index ceaaec4290d..55c23ee8bfa 100644 --- a/pkg/cli/run_command_test.go +++ b/pkg/cli/run_command_test.go @@ -3,6 +3,7 @@ package cli import ( + "context" "fmt" "strings" "testing" @@ -135,10 +136,10 @@ func TestProgressFlagSignature(t *testing.T) { // This is a compile-time check more than a runtime check // RunWorkflowOnGitHub should NOT accept progress parameter anymore - _ = RunWorkflowOnGitHub("test", false, "", "", "", false, false, false, []string{}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "", false, false, false, []string{}, false) // RunWorkflowsOnGitHub should NOT accept progress parameter anymore - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "", false, false, []string{}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "", false, false, []string{}, false) // getLatestWorkflowRunWithRetry should NOT accept progress parameter anymore _, _ = getLatestWorkflowRunWithRetry("test.lock.yml", "", false) @@ -148,22 +149,22 @@ func TestProgressFlagSignature(t *testing.T) { func TestRefFlagSignature(t *testing.T) { // Test that RunWorkflowOnGitHub accepts refOverride parameter // This is a compile-time check that ensures the refOverride parameter exists - _ = RunWorkflowOnGitHub("test", false, "", "", "main", false, false, false, []string{}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "main", false, false, false, []string{}, false) // Test that RunWorkflowsOnGitHub accepts refOverride parameter - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "main", false, false, []string{}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "main", false, false, []string{}, false) } // TestRunWorkflowOnGitHubWithRef tests that the ref parameter is handled correctly func TestRunWorkflowOnGitHubWithRef(t *testing.T) { // Test with explicit ref override (should still fail for non-existent workflow, but syntax is valid) - err := RunWorkflowOnGitHub("nonexistent-workflow", false, "", "", "main", false, false, false, []string{}, false) + err := RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "", "main", false, false, false, []string{}, false) if err == nil { t.Error("RunWorkflowOnGitHub should return error for non-existent workflow even with ref flag") } // Test with ref override and repo override - err = RunWorkflowOnGitHub("nonexistent-workflow", false, "", "owner/repo", "feature-branch", false, false, false, []string{}, false) + err = RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "owner/repo", "feature-branch", false, false, false, []string{}, false) if err == nil { t.Error("RunWorkflowOnGitHub should return error for non-existent workflow with both ref and repo") } @@ -173,10 +174,10 @@ func TestRunWorkflowOnGitHubWithRef(t *testing.T) { func TestInputFlagSignature(t *testing.T) { // Test that RunWorkflowOnGitHub accepts inputs parameter // This is a compile-time check that ensures the inputs parameter exists - _ = RunWorkflowOnGitHub("test", false, "", "", "", false, false, false, []string{"key=value"}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "", false, false, false, []string{"key=value"}, false) // Test that RunWorkflowsOnGitHub accepts inputs parameter - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "", false, false, []string{"key=value"}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "", false, false, []string{"key=value"}, false) } // TestInputValidation tests that input validation works correctly @@ -231,7 +232,7 @@ func TestInputValidation(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Since we can't actually run workflows in tests, we'll just test the validation // by checking if the function would error before attempting to run - err := RunWorkflowOnGitHub("nonexistent-workflow", false, "", "owner/repo", "", false, false, false, tt.inputs, false) + err := RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "owner/repo", "", false, false, false, tt.inputs, false) if tt.shouldError { if err == nil { diff --git a/pkg/cli/templates/github-agentic-workflows.md b/pkg/cli/templates/github-agentic-workflows.md index 19bcb46b02c..f8052224b8e 100644 --- a/pkg/cli/templates/github-agentic-workflows.md +++ b/pkg/cli/templates/github-agentic-workflows.md @@ -115,6 +115,9 @@ The YAML frontmatter supports these fields: - **`roles:`** - Repository access roles that can trigger workflow (array or "all") - Default: `[admin, maintainer, write]` - Available roles: `admin`, `maintainer`, `write`, `read`, `all` +- **`bots:`** - Bot identifiers allowed to trigger workflow regardless of role permissions (array) + - Example: `bots: [dependabot[bot], renovate[bot], github-actions[bot]]` + - Bot must be active (installed) on repository to trigger workflow - **`strict:`** - Enable enhanced validation for production workflows (boolean, defaults to `true`) - When omitted, workflows enforce strict mode security constraints - Set to `false` to explicitly disable strict mode for development/testing @@ -251,6 +254,43 @@ The YAML frontmatter supports these fields: args: ["--custom-arg", "value"] # Optional: additional AWF arguments ``` +- **`sandbox:`** - Sandbox configuration for AI engines (string or object) + - String format: `"default"` (no sandbox), `"awf"` (Agent Workflow Firewall), `"srt"` or `"sandbox-runtime"` (Anthropic Sandbox Runtime) + - Object format for full configuration: + ```yaml + sandbox: + agent: awf # or "srt", or false to disable + mcp: # MCP Gateway configuration (requires mcp-gateway feature flag) + container: ghcr.io/githubnext/mcp-gateway + port: 8080 + api-key: ${{ secrets.MCP_GATEWAY_API_KEY }} + ``` + - **Agent sandbox options**: + - `awf`: Agent Workflow Firewall for domain-based access control + - `srt`: Anthropic Sandbox Runtime for filesystem and command sandboxing + - `false`: Disable agent firewall + - **AWF configuration**: + ```yaml + sandbox: + agent: + id: awf + mounts: + - "/host/data:/data:ro" + - "/host/bin/tool:/usr/local/bin/tool:ro" + ``` + - **SRT configuration**: + ```yaml + sandbox: + agent: + id: srt + config: + filesystem: + allowWrite: [".", "/tmp"] + denyRead: ["/etc/secrets"] + enableWeakerNestedSandbox: true + ``` + - **MCP Gateway**: Routes MCP server calls through unified HTTP gateway (experimental) + - **`tools:`** - Tool configuration for coding agent - `github:` - GitHub API tools - `allowed:` - Array of allowed GitHub API functions @@ -578,6 +618,43 @@ The YAML frontmatter supports these fields: ``` Useful when you need additional permissions or want to perform actions across repositories. +- **`safe-inputs:`** - Define custom lightweight MCP tools as JavaScript, shell, or Python scripts (object) + - Tools mounted in MCP server with access to specified secrets + - Each tool requires `description` and one of: `script` (JavaScript), `run` (shell), or `py` (Python) + - Tool configuration properties: + - `description:` - Tool description (required) + - `inputs:` - Input parameters with type and description (object) + - `script:` - JavaScript implementation (CommonJS format) + - `run:` - Shell script implementation + - `py:` - Python script implementation + - `env:` - Environment variables for secrets (supports `${{ secrets.* }}`) + - `timeout:` - Execution timeout in seconds (default: 60) + - Example: + ```yaml + safe-inputs: + search-issues: + description: "Search GitHub issues using API" + inputs: + query: + type: string + description: "Search query" + required: true + limit: + type: number + description: "Max results" + default: 10 + script: | + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: process.env.GH_TOKEN }); + const result = await octokit.search.issuesAndPullRequests({ + q: inputs.query, + per_page: inputs.limit + }); + return result.data.items; + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ``` + - **`slash_command:`** - Command trigger configuration for /mention workflows (replaces deprecated `command:`) - **`cache:`** - Cache configuration for workflow dependencies (object or array) - **`cache-memory:`** - Memory MCP server with persistent cache storage (boolean or object) diff --git a/pkg/workflow/safe_outputs_integration_test.go b/pkg/workflow/safe_outputs_integration_test.go index 0dd313a1882..414d96b6a7c 100644 --- a/pkg/workflow/safe_outputs_integration_test.go +++ b/pkg/workflow/safe_outputs_integration_test.go @@ -517,9 +517,8 @@ func TestConsolidatedSafeOutputsJobIntegration(t *testing.T) { "SHARED_VAR", }, expectedStepNames: []string{ - "create_issue", - "create_pull_request", - "add_comment", + "process_safe_outputs", // Handler manager consolidates create_issue and add_comment + "create_pull_request", // Create PR is handled separately // Note: "noop" is not included in consolidated job }, }, From 0e6234a92d56166752b2066e524cdcee4dcdef5b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:21:57 -0800 Subject: [PATCH 6/6] [WIP] Enhance campaign management with new features (#8686) --- .../patch-pass-mcp-env-vars-to-gateway.md | 10 + .../agent-performance-analyzer.lock.yml | 2 +- .github/workflows/ai-moderator.lock.yml | 2 +- .github/workflows/archie.lock.yml | 2 +- .github/workflows/artifacts-summary.lock.yml | 2 +- .github/workflows/audit-workflows.lock.yml | 6 +- .github/workflows/blog-auditor.lock.yml | 2 +- .github/workflows/brave.lock.yml | 2 +- .../breaking-change-checker.lock.yml | 2 +- .github/workflows/campaign-generator.lock.yml | 2 +- .github/workflows/campaign-manager.lock.yml | 2 +- .github/workflows/changeset.lock.yml | 2 +- .github/workflows/ci-coach.lock.yml | 2 +- .github/workflows/ci-doctor.lock.yml | 2 +- .../cli-consistency-checker.lock.yml | 2 +- .../workflows/cli-version-checker.lock.yml | 2 +- .github/workflows/cloclo.lock.yml | 2 +- .../commit-changes-analyzer.lock.yml | 2 +- .../workflows/copilot-agent-analysis.lock.yml | 2 +- .../copilot-pr-merged-report.lock.yml | 6 +- .../copilot-pr-nlp-analysis.lock.yml | 6 +- .../copilot-pr-prompt-analysis.lock.yml | 2 +- .../copilot-session-insights.lock.yml | 6 +- .github/workflows/craft.lock.yml | 2 +- .../daily-assign-issue-to-user.lock.yml | 2 +- .github/workflows/daily-choice-test.lock.yml | 2 +- .../workflows/daily-cli-performance.lock.yml | 2 +- .github/workflows/daily-code-metrics.lock.yml | 6 +- .../daily-copilot-token-report.lock.yml | 6 +- .github/workflows/daily-doc-updater.lock.yml | 2 +- .github/workflows/daily-fact.lock.yml | 2 +- .github/workflows/daily-file-diet.lock.yml | 6 +- .../workflows/daily-firewall-report.lock.yml | 6 +- .../workflows/daily-issues-report.lock.yml | 6 +- .../daily-malicious-code-scan.lock.yml | 2 +- .../daily-multi-device-docs-tester.lock.yml | 6 +- .github/workflows/daily-news.lock.yml | 6 +- .../daily-performance-summary.lock.yml | 8 +- .../workflows/daily-repo-chronicle.lock.yml | 6 +- .github/workflows/daily-team-status.lock.yml | 2 +- .../workflows/daily-workflow-updater.lock.yml | 2 +- .github/workflows/deep-report.lock.yml | 6 +- .../workflows/dependabot-go-checker.lock.yml | 2 +- .github/workflows/dev-hawk.lock.yml | 2 +- .github/workflows/dev.lock.yml | 2 +- .../developer-docs-consolidator.lock.yml | 2 +- .github/workflows/dictation-prompt.lock.yml | 2 +- .github/workflows/docs-noob-tester.lock.yml | 6 +- ...ty-maintenance-project67.campaign.lock.yml | 2 +- .../duplicate-code-detector.lock.yml | 2 +- .../example-workflow-analyzer.lock.yml | 2 +- .../github-mcp-structural-analysis.lock.yml | 6 +- .../github-mcp-tools-report.lock.yml | 2 +- .../workflows/glossary-maintainer.lock.yml | 2 +- .github/workflows/go-fan.lock.yml | 2 +- ...size-reduction-project64.campaign.lock.yml | 2 +- .github/workflows/go-logger.lock.yml | 2 +- .../workflows/go-pattern-detector.lock.yml | 2 +- .github/workflows/grumpy-reviewer.lock.yml | 2 +- .github/workflows/hourly-ci-cleaner.lock.yml | 2 +- .../workflows/human-ai-collaboration.lock.yml | 2 +- .github/workflows/incident-response.lock.yml | 2 +- .../workflows/instructions-janitor.lock.yml | 2 +- .github/workflows/intelligence.lock.yml | 6 +- .github/workflows/issue-arborist.lock.yml | 2 +- .github/workflows/issue-classifier.lock.yml | 2 +- .github/workflows/issue-monster.lock.yml | 2 +- .../issue-template-optimizer.lock.yml | 2 +- .github/workflows/issue-triage-agent.lock.yml | 2 +- .github/workflows/jsweep.lock.yml | 2 +- .../workflows/layout-spec-maintainer.lock.yml | 2 +- .github/workflows/lockfile-stats.lock.yml | 2 +- .github/workflows/mcp-inspector.lock.yml | 2 +- .github/workflows/mergefest.lock.yml | 2 +- .../workflows/notion-issue-summary.lock.yml | 2 +- .github/workflows/org-health-report.lock.yml | 6 +- .github/workflows/org-wide-rollout.lock.yml | 2 +- .github/workflows/pdf-summary.lock.yml | 2 +- .github/workflows/plan.lock.yml | 2 +- ...ayground-org-project-update-issue.lock.yml | 2 +- .../playground-snapshots-refresh.lock.yml | 2 +- .github/workflows/poem-bot.lock.yml | 6 +- .github/workflows/portfolio-analyst.lock.yml | 6 +- .../workflows/pr-nitpick-reviewer.lock.yml | 2 +- .../prompt-clustering-analysis.lock.yml | 2 +- .github/workflows/python-data-charts.lock.yml | 6 +- .github/workflows/q.lock.yml | 2 +- .github/workflows/release.lock.yml | 2 +- .github/workflows/repo-tree-map.lock.yml | 2 +- .../repository-quality-improver.lock.yml | 2 +- .github/workflows/research.lock.yml | 2 +- .github/workflows/safe-output-health.lock.yml | 2 +- .../schema-consistency-checker.lock.yml | 2 +- .github/workflows/scout.lock.yml | 2 +- .../workflows/security-compliance.lock.yml | 2 +- .github/workflows/security-fix-pr.lock.yml | 2 +- .../semantic-function-refactor.lock.yml | 2 +- .../workflows/slide-deck-maintainer.lock.yml | 2 +- .github/workflows/smoke-claude.lock.yml | 2 +- .../workflows/smoke-codex-firewall.lock.yml | 2 +- .github/workflows/smoke-codex.lock.yml | 2 +- .../smoke-copilot-no-firewall.lock.yml | 15 +- .../smoke-copilot-playwright.lock.yml | 8 +- .../smoke-copilot-safe-inputs.lock.yml | 6 +- .github/workflows/smoke-copilot.lock.yml | 2 +- .github/workflows/smoke-detector.lock.yml | 2 +- .github/workflows/smoke-srt.lock.yml | 2 +- .github/workflows/spec-kit-execute.lock.yml | 2 +- .github/workflows/spec-kit-executor.lock.yml | 2 +- .github/workflows/speckit-dispatcher.lock.yml | 2 +- .../workflows/stale-repo-identifier.lock.yml | 6 +- .../workflows/static-analysis-report.lock.yml | 2 +- .github/workflows/sub-issue-closer.lock.yml | 2 +- .github/workflows/super-linter.lock.yml | 2 +- .../workflows/technical-doc-writer.lock.yml | 6 +- .github/workflows/terminal-stylist.lock.yml | 2 +- .github/workflows/tidy.lock.yml | 2 +- .github/workflows/typist.lock.yml | 2 +- .github/workflows/unbloat-docs.lock.yml | 6 +- .github/workflows/video-analyzer.lock.yml | 2 +- .../workflows/weekly-issue-summary.lock.yml | 6 +- .github/workflows/workflow-generator.lock.yml | 2 +- .../workflow-health-manager.lock.yml | 2 +- pkg/cli/logs_orchestrator_test.go | 16 +- pkg/workflow/gateway.go | 36 ++- pkg/workflow/gateway_test.go | 84 ++++-- pkg/workflow/mcp_servers.go | 199 ++++++------- specs/safe-output-handler-manager.md | 269 ------------------ 128 files changed, 388 insertions(+), 595 deletions(-) create mode 100644 .changeset/patch-pass-mcp-env-vars-to-gateway.md delete mode 100644 specs/safe-output-handler-manager.md diff --git a/.changeset/patch-pass-mcp-env-vars-to-gateway.md b/.changeset/patch-pass-mcp-env-vars-to-gateway.md new file mode 100644 index 00000000000..9462f8a54ea --- /dev/null +++ b/.changeset/patch-pass-mcp-env-vars-to-gateway.md @@ -0,0 +1,10 @@ +--- +"gh-aw": patch +--- + +Pass MCP environment variables through to the MCP gateway (awmg) so the gateway process has access to the same secrets and env vars configured in the "Setup MCPs" step. This centralizes env var collection and updates gateway step generation and tests. + +Files changed (PR #8677): +- pkg/workflow/mcp_servers.go +- pkg/workflow/gateway.go +- pkg/workflow/gateway_test.go diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index e410c9b4f12..b68b258aad9 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -442,8 +442,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index 2bcdd84a3a0..096a099fe84 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -340,8 +340,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index aa4c0973571..ffb1c9f4c18 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -347,8 +347,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 5dda65594ef..8eefa82208b 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -308,8 +308,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 4460f1adbbe..63840521b17 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -403,11 +403,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index 783180610ee..3a740f403bb 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -304,8 +304,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index c45109a5fcb..23696f9e53f 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -325,8 +325,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index 7218ac524ac..e30b9defc4a 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -326,8 +326,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/campaign-generator.lock.yml b/.github/workflows/campaign-generator.lock.yml index 6acb3542040..8fbb760b93a 100644 --- a/.github/workflows/campaign-generator.lock.yml +++ b/.github/workflows/campaign-generator.lock.yml @@ -365,8 +365,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/campaign-manager.lock.yml b/.github/workflows/campaign-manager.lock.yml index e9af039e9ed..11d057fda56 100644 --- a/.github/workflows/campaign-manager.lock.yml +++ b/.github/workflows/campaign-manager.lock.yml @@ -528,8 +528,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 6f228e67325..01d2be32241 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -402,8 +402,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml index 22df28796d9..c902901bb86 100644 --- a/.github/workflows/ci-coach.lock.yml +++ b/.github/workflows/ci-coach.lock.yml @@ -371,8 +371,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index ead08a6fc76..3e9a04f4cc7 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -390,8 +390,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index 306d5b7df6f..623db017d59 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -327,8 +327,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index fcba7228df9..c37a019327b 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -339,8 +339,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index d14a77888cf..74998c38cda 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -457,8 +457,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index fd4bdaeb095..e01b1392463 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -306,8 +306,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 4fedfc6f28a..1c29c2553fb 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -337,8 +337,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index a63111096bb..10d2faab22b 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -394,10 +394,10 @@ jobs: - name: Setup MCPs env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_DEBUG: 1 run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 06a4752af33..a506aca8a84 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -400,11 +400,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 263b6d2eb2a..522d3a979c6 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -342,8 +342,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 61fc7c085f1..d3560050870 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -389,11 +389,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 073e4a7a0fb..e3bd5e4a3a4 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -374,8 +374,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 88ac4db3a5f..a5ac35eddd5 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -337,8 +337,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-choice-test.lock.yml b/.github/workflows/daily-choice-test.lock.yml index f5e0961363d..077cb8a3c72 100644 --- a/.github/workflows/daily-choice-test.lock.yml +++ b/.github/workflows/daily-choice-test.lock.yml @@ -276,8 +276,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index d189b887916..b1fa123a074 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -375,8 +375,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index c017dadfe67..4851abc2f93 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -378,11 +378,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index 43e0cebcb29..25b21d2ab4b 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -398,11 +398,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index d6e1d8e554d..bab2a388b99 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -321,8 +321,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index 4668c386732..8cb632c155d 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -275,8 +275,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index 45c1b2b36c2..774ebadae7e 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -419,11 +419,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 593e5fbc770..7411c6a0413 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -401,11 +401,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 36f626377ef..7f8cc26af7e 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -441,11 +441,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml index 2d98c17501e..b45599287e2 100644 --- a/.github/workflows/daily-malicious-code-scan.lock.yml +++ b/.github/workflows/daily-malicious-code-scan.lock.yml @@ -346,8 +346,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index a02991014b3..2fff119017e 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -355,11 +355,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 98491ed4729..0b96c734c60 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -396,11 +396,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index b81a84fcbed..f399d2ccdfa 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -818,14 +818,14 @@ jobs: - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 82e6db3a5b3..2f3a08d2661 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -375,11 +375,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index 7bf7f0330e1..911003f02c9 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -339,8 +339,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml index 033d64ffa19..fdb878edacb 100644 --- a/.github/workflows/daily-workflow-updater.lock.yml +++ b/.github/workflows/daily-workflow-updater.lock.yml @@ -315,8 +315,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 951a8747470..e66c2d04749 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -385,11 +385,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index e7c0ef57b84..8be304250e0 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -367,8 +367,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 33f37f10979..e70f5bfe7af 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -319,8 +319,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index f87c31382f8..518e0726a04 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -289,8 +289,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 47700636843..730fbdbf7dd 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -392,8 +392,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index d3f0cbaef8f..b8df7cee518 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -318,8 +318,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 5f6d09f66be..f4593ca46f9 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -334,11 +334,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml index 4a75c022759..70429c063fd 100644 --- a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml +++ b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml @@ -403,8 +403,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 2920b647a6d..ec7169afd68 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -333,8 +333,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index f9c87907a67..4a7f67531a9 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -319,8 +319,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index cd72ce92e81..5eaab7e2309 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -373,11 +373,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index abde0ede039..657bc2a6f3d 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -381,8 +381,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 6800857021e..a8456824675 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -344,8 +344,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index c7e71fbd601..7fa532f8ecf 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -328,8 +328,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml index 3d014fa7828..29223c257e3 100644 --- a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml +++ b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml @@ -403,8 +403,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index a4dcf0c0b8e..f7bc68af9a3 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -337,8 +337,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 0190fab11ab..1cbaaf06d3f 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -327,8 +327,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index c4a51979bc4..bbb21761db1 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -410,8 +410,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index c6f450514ef..789a1612668 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -345,8 +345,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/human-ai-collaboration.lock.yml b/.github/workflows/human-ai-collaboration.lock.yml index 7ffccd239d9..8d8e3c5e390 100644 --- a/.github/workflows/human-ai-collaboration.lock.yml +++ b/.github/workflows/human-ai-collaboration.lock.yml @@ -335,8 +335,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/incident-response.lock.yml b/.github/workflows/incident-response.lock.yml index 0a81a3e0967..3089812a6ef 100644 --- a/.github/workflows/incident-response.lock.yml +++ b/.github/workflows/incident-response.lock.yml @@ -487,8 +487,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index fd5772d2457..ca295c6351f 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -321,8 +321,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/intelligence.lock.yml b/.github/workflows/intelligence.lock.yml index 4fc2e7e1414..3503c67de30 100644 --- a/.github/workflows/intelligence.lock.yml +++ b/.github/workflows/intelligence.lock.yml @@ -411,11 +411,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index c4ac12977ba..04e6ed1e8d3 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -425,8 +425,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index f23f7c834d4..1e60e0f9c64 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -300,8 +300,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 9813d740aca..323a1db6a06 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -337,8 +337,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/issue-template-optimizer.lock.yml b/.github/workflows/issue-template-optimizer.lock.yml index 503d0e0c457..bfa82371710 100644 --- a/.github/workflows/issue-template-optimizer.lock.yml +++ b/.github/workflows/issue-template-optimizer.lock.yml @@ -327,8 +327,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml index 178de0cb852..b2d591c7f59 100644 --- a/.github/workflows/issue-triage-agent.lock.yml +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -306,8 +306,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 6a8961e2af9..8b6ebb99b44 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -341,8 +341,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml index 8e150e3c31f..acd2c5e6a50 100644 --- a/.github/workflows/layout-spec-maintainer.lock.yml +++ b/.github/workflows/layout-spec-maintainer.lock.yml @@ -317,8 +317,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 5110bb2b958..b7d39b6a6c4 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -315,8 +315,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 63d475051c2..d8356e8663e 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -401,8 +401,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index c14ca118629..e91394022d7 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -325,8 +325,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index ed246d5780c..46a00e90628 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -276,8 +276,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 21aafc86fc0..712980e0a42 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -373,11 +373,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/org-wide-rollout.lock.yml b/.github/workflows/org-wide-rollout.lock.yml index 3005d8b4fa2..676cd717d01 100644 --- a/.github/workflows/org-wide-rollout.lock.yml +++ b/.github/workflows/org-wide-rollout.lock.yml @@ -494,8 +494,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index eec2c5e1f0f..feebf0c8266 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -361,8 +361,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 7434879dccb..f15526c3da9 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -420,8 +420,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/playground-org-project-update-issue.lock.yml b/.github/workflows/playground-org-project-update-issue.lock.yml index 1026bd2224e..c7ce9d5d346 100644 --- a/.github/workflows/playground-org-project-update-issue.lock.yml +++ b/.github/workflows/playground-org-project-update-issue.lock.yml @@ -351,8 +351,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.TEST_ORG_PROJECT_WRITE }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.TEST_ORG_PROJECT_WRITE }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/playground-snapshots-refresh.lock.yml b/.github/workflows/playground-snapshots-refresh.lock.yml index 4162d60eed5..ebae02b855d 100644 --- a/.github/workflows/playground-snapshots-refresh.lock.yml +++ b/.github/workflows/playground-snapshots-refresh.lock.yml @@ -330,8 +330,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index a27f5c4cd3f..af0ae7b23c0 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -871,11 +871,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 5c4aa5d9d12..8bf418e5f79 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -401,11 +401,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 38d32672d82..516f855cdf4 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -481,8 +481,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index c8bde4b5b13..3c533fdfb50 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -389,8 +389,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 649a34cd800..eda3ba53462 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -386,11 +386,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index b50dc086675..44d7dfccf37 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -454,8 +454,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index a16137ae745..64482023dfc 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -320,8 +320,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 5c0aab5bd5d..d0b446b19bd 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -309,8 +309,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index c04d1aa1e34..555f9bf80e3 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -335,8 +335,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index dd63ae3bc19..93e6b6cc65e 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -312,8 +312,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index bd5d46267c4..53c83ba2ec1 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -341,8 +341,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 7cd85adc8d1..99653286063 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -317,8 +317,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index ae9c41a2d57..78490387661 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -384,8 +384,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index 52b0dc2ffbb..0a0b6b6c564 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -340,8 +340,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index a8a50e0cce1..c3b6d700fb9 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -329,8 +329,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index b152b84f613..48813796014 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -363,8 +363,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index ed6682bef1f..6def135ff91 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -344,8 +344,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 0f691ef4a89..90d12b353e6 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -449,8 +449,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/smoke-codex-firewall.lock.yml b/.github/workflows/smoke-codex-firewall.lock.yml index 659e185918e..cd0d08be158 100644 --- a/.github/workflows/smoke-codex-firewall.lock.yml +++ b/.github/workflows/smoke-codex-firewall.lock.yml @@ -450,8 +450,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 9cb0f979687..5d96849b130 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -473,8 +473,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF diff --git a/.github/workflows/smoke-copilot-no-firewall.lock.yml b/.github/workflows/smoke-copilot-no-firewall.lock.yml index ab7cb7c7669..10dda3a6409 100644 --- a/.github/workflows/smoke-copilot-no-firewall.lock.yml +++ b/.github/workflows/smoke-copilot-no-firewall.lock.yml @@ -456,12 +456,12 @@ jobs: - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_DEBUG: 1 + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -549,6 +549,13 @@ jobs: echo "HOME: $HOME" echo "GITHUB_COPILOT_CLI_MODE: $GITHUB_COPILOT_CLI_MODE" - name: Start MCP Gateway + env: + GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_DEBUG: 1 + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-gateway-logs echo 'Starting MCP Gateway...' diff --git a/.github/workflows/smoke-copilot-playwright.lock.yml b/.github/workflows/smoke-copilot-playwright.lock.yml index f3bf957b1de..db9649df512 100644 --- a/.github/workflows/smoke-copilot-playwright.lock.yml +++ b/.github/workflows/smoke-copilot-playwright.lock.yml @@ -549,12 +549,12 @@ jobs: - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_DEBUG: 1 + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/smoke-copilot-safe-inputs.lock.yml b/.github/workflows/smoke-copilot-safe-inputs.lock.yml index 7caf6e5e42f..4f3e23cdc30 100644 --- a/.github/workflows/smoke-copilot-safe-inputs.lock.yml +++ b/.github/workflows/smoke-copilot-safe-inputs.lock.yml @@ -440,10 +440,10 @@ jobs: - name: Setup MCPs env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_DEBUG: 1 run: | mkdir -p /tmp/gh-aw/mcp-config diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 4a8c0677d3e..8762a9b1c92 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -438,8 +438,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index 47b1f280c85..a3e8e5a6464 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -434,8 +434,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/smoke-srt.lock.yml b/.github/workflows/smoke-srt.lock.yml index cb897e7eb5f..96b7bfd2ac4 100644 --- a/.github/workflows/smoke-srt.lock.yml +++ b/.github/workflows/smoke-srt.lock.yml @@ -266,8 +266,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/spec-kit-execute.lock.yml b/.github/workflows/spec-kit-execute.lock.yml index 692c3fd6e6e..74e688869e5 100644 --- a/.github/workflows/spec-kit-execute.lock.yml +++ b/.github/workflows/spec-kit-execute.lock.yml @@ -332,8 +332,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/spec-kit-executor.lock.yml b/.github/workflows/spec-kit-executor.lock.yml index 0249eea7278..f76462efec8 100644 --- a/.github/workflows/spec-kit-executor.lock.yml +++ b/.github/workflows/spec-kit-executor.lock.yml @@ -335,8 +335,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/speckit-dispatcher.lock.yml b/.github/workflows/speckit-dispatcher.lock.yml index ecddd6ca4a4..baa7b09a608 100644 --- a/.github/workflows/speckit-dispatcher.lock.yml +++ b/.github/workflows/speckit-dispatcher.lock.yml @@ -463,8 +463,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 67c728f089e..2f298001afa 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -436,11 +436,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index fcf59ef69ab..a2d91e34b86 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -340,8 +340,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml index 6863b45be9f..4e425468e43 100644 --- a/.github/workflows/sub-issue-closer.lock.yml +++ b/.github/workflows/sub-issue-closer.lock.yml @@ -346,8 +346,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 1e18e4690ab..c1abb42e89f 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -350,8 +350,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 532dcd051f2..bec10dbf979 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -416,11 +416,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml index c8a55f10756..db11fff00e3 100644 --- a/.github/workflows/terminal-stylist.lock.yml +++ b/.github/workflows/terminal-stylist.lock.yml @@ -313,8 +313,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index e659c0c1278..6a972ecb5cc 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -410,8 +410,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 5b75d75ab73..9ce20550d07 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -315,8 +315,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 3d04bfe90c0..60ed698ca8c 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -432,11 +432,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index ff2951530c0..d6c02fd1544 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -340,8 +340,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 1e058b297a4..405762d404a 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -353,11 +353,11 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} - GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index fa35abfbe96..fc37e6b9bfd 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -363,8 +363,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index 771d2b30e21..6b4c5f3651e 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -434,8 +434,8 @@ jobs: EOF - name: Setup MCPs env: - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot diff --git a/pkg/cli/logs_orchestrator_test.go b/pkg/cli/logs_orchestrator_test.go index b398b77ee4d..0cfb210088c 100644 --- a/pkg/cli/logs_orchestrator_test.go +++ b/pkg/cli/logs_orchestrator_test.go @@ -6,8 +6,6 @@ import ( "fmt" "os" "path/filepath" - "sync" - "sync/atomic" "testing" "time" @@ -240,11 +238,6 @@ func TestDownloadRunArtifactsConcurrent_ConcurrencyLimit(t *testing.T) { } } - // Track concurrent executions - var currentConcurrent int64 - var maxConcurrent int64 - var mu sync.Mutex - // We can't directly test the pool's behavior without mocking, // but we can verify the limit is configured correctly tmpDir := testutil.TempDir(t, "test-orchestrator-*") @@ -254,11 +247,8 @@ func TestDownloadRunArtifactsConcurrent_ConcurrencyLimit(t *testing.T) { // The actual concurrency limit enforcement is handled by the conc pool internally // We've verified the limit is set correctly via getMaxConcurrentDownloads() - t.Logf("Processed %d runs with limit %d, max concurrent observed: %d", - len(results), limit, atomic.LoadInt64(&maxConcurrent)) - - _ = currentConcurrent // Used in real implementation - _ = mu // Used for synchronization + t.Logf("Processed %d runs with limit %d", + len(results), limit) }) } } @@ -304,7 +294,7 @@ func TestDownloadRunArtifactsConcurrent_ErrorHandling(t *testing.T) { for _, result := range results { // Either skipped with error or has an error set if result.Skipped { - assert.NotNil(t, result.Error, "Skipped result should have error for run %d", result.Run.DatabaseID) + assert.Error(t, result.Error, "Skipped result should have error for run %d", result.Run.DatabaseID) } // Note: Without actual GitHub API access, we can't guarantee errors, // but the structure should handle them properly diff --git a/pkg/workflow/gateway.go b/pkg/workflow/gateway.go index 37969d38afd..5d6d0fa761b 100644 --- a/pkg/workflow/gateway.go +++ b/pkg/workflow/gateway.go @@ -2,6 +2,7 @@ package workflow import ( "fmt" + "sort" "strings" "github.com/githubnext/gh-aw/pkg/logger" @@ -44,7 +45,7 @@ func getMCPGatewayConfig(workflowData *WorkflowData) *MCPGatewayRuntimeConfig { } // generateMCPGatewaySteps generates the steps to start and verify the MCP gateway -func generateMCPGatewaySteps(workflowData *WorkflowData, mcpServersConfig map[string]any) []GitHubActionStep { +func generateMCPGatewaySteps(workflowData *WorkflowData, mcpEnvVars map[string]string) []GitHubActionStep { if !isMCPGatewayEnabled(workflowData) { return nil } @@ -54,13 +55,13 @@ func generateMCPGatewaySteps(workflowData *WorkflowData, mcpServersConfig map[st return nil } - gatewayLog.Printf("Generating MCP gateway steps: port=%d, container=%s, command=%s, servers=%d", - config.Port, config.Container, config.Command, len(mcpServersConfig)) + gatewayLog.Printf("Generating MCP gateway steps: port=%d, container=%s, command=%s, env_vars=%d", + config.Port, config.Container, config.Command, len(mcpEnvVars)) var steps []GitHubActionStep // Step 1: Start MCP Gateway (background process) - startStep := generateMCPGatewayStartStep(config, mcpServersConfig) + startStep := generateMCPGatewayStartStep(config, mcpEnvVars) steps = append(steps, startStep) // Step 2: Health check to verify gateway is running @@ -71,7 +72,7 @@ func generateMCPGatewaySteps(workflowData *WorkflowData, mcpServersConfig map[st } // generateMCPGatewayStartStep generates the step that starts the MCP gateway -func generateMCPGatewayStartStep(config *MCPGatewayRuntimeConfig, mcpServersConfig map[string]any) GitHubActionStep { +func generateMCPGatewayStartStep(config *MCPGatewayRuntimeConfig, mcpEnvVars map[string]string) GitHubActionStep { gatewayLog.Print("Generating MCP gateway start step") port, err := validateAndNormalizePort(config.Port) @@ -87,11 +88,32 @@ func generateMCPGatewayStartStep(config *MCPGatewayRuntimeConfig, mcpServersConf stepLines := []string{ " - name: Start MCP Gateway", + } + + // Add env block if there are environment variables to pass through + if len(mcpEnvVars) > 0 { + stepLines = append(stepLines, " env:") + + // Sort environment variable names for consistent output + envVarNames := make([]string, 0, len(mcpEnvVars)) + for envVarName := range mcpEnvVars { + envVarNames = append(envVarNames, envVarName) + } + sort.Strings(envVarNames) + + // Write environment variables in sorted order + for _, envVarName := range envVarNames { + envVarValue := mcpEnvVars[envVarName] + stepLines = append(stepLines, fmt.Sprintf(" %s: %s", envVarName, envVarValue)) + } + } + + stepLines = append(stepLines, " run: |", - " mkdir -p " + MCPGatewayLogsFolder, + " mkdir -p "+MCPGatewayLogsFolder, " echo 'Starting MCP Gateway...'", " ", - } + ) // Check which mode to use: container, command, or default (awmg binary) if config.Container != "" { diff --git a/pkg/workflow/gateway_test.go b/pkg/workflow/gateway_test.go index c2499929a37..0950fa0e6a0 100644 --- a/pkg/workflow/gateway_test.go +++ b/pkg/workflow/gateway_test.go @@ -197,13 +197,13 @@ func TestGenerateMCPGatewaySteps(t *testing.T) { tests := []struct { name string data *WorkflowData - mcpServers map[string]any + mcpEnvVars map[string]string expectSteps int }{ { name: "gateway disabled returns no steps", data: &WorkflowData{}, - mcpServers: map[string]any{}, + mcpEnvVars: map[string]string{}, expectSteps: 0, }, { @@ -218,16 +218,14 @@ func TestGenerateMCPGatewaySteps(t *testing.T) { "mcp-gateway": true, }, }, - mcpServers: map[string]any{ - "github": map[string]any{}, - }, + mcpEnvVars: map[string]string{}, expectSteps: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - steps := generateMCPGatewaySteps(tt.data, tt.mcpServers) + steps := generateMCPGatewaySteps(tt.data, tt.mcpEnvVars) assert.Len(t, steps, tt.expectSteps) }) } @@ -237,11 +235,9 @@ func TestGenerateMCPGatewayStartStep(t *testing.T) { config := &MCPGatewayRuntimeConfig{ Port: 8080, } - mcpServers := map[string]any{ - "github": map[string]any{}, - } + mcpEnvVars := map[string]string{} - step := generateMCPGatewayStartStep(config, mcpServers) + step := generateMCPGatewayStartStep(config, mcpEnvVars) stepStr := strings.Join(step, "\n") assert.Contains(t, stepStr, "Start MCP Gateway") @@ -529,11 +525,9 @@ func TestGenerateMCPGatewayStartStep_ContainerMode(t *testing.T) { EntrypointArgs: []string{"--config-stdin"}, Port: 8000, } - mcpServers := map[string]any{ - "github": map[string]any{}, - } + mcpEnvVars := map[string]string{} - step := generateMCPGatewayStartStep(config, mcpServers) + step := generateMCPGatewayStartStep(config, mcpEnvVars) stepStr := strings.Join(step, "\n") // Should use container mode @@ -549,11 +543,9 @@ func TestGenerateMCPGatewayStartStep_CommandMode(t *testing.T) { Args: []string{"--debug"}, Port: 9000, } - mcpServers := map[string]any{ - "github": map[string]any{}, - } + mcpEnvVars := map[string]string{} - step := generateMCPGatewayStartStep(config, mcpServers) + step := generateMCPGatewayStartStep(config, mcpEnvVars) stepStr := strings.Join(step, "\n") // Should use command mode @@ -567,11 +559,9 @@ func TestGenerateMCPGatewayStartStep_DefaultMode(t *testing.T) { config := &MCPGatewayRuntimeConfig{ Port: 8080, } - mcpServers := map[string]any{ - "github": map[string]any{}, - } + mcpEnvVars := map[string]string{} - step := generateMCPGatewayStartStep(config, mcpServers) + step := generateMCPGatewayStartStep(config, mcpEnvVars) stepStr := strings.Join(step, "\n") // Should use default awmg mode @@ -676,11 +666,9 @@ func TestGenerateMCPGatewayStartStepWithInvalidPort(t *testing.T) { config := &MCPGatewayRuntimeConfig{ Port: tt.port, } - mcpServers := map[string]any{ - "github": map[string]any{}, - } + mcpEnvVars := map[string]string{} - step := generateMCPGatewayStartStep(config, mcpServers) + step := generateMCPGatewayStartStep(config, mcpEnvVars) stepStr := strings.Join(step, "\n") // Should still generate valid step with default port @@ -763,3 +751,47 @@ func TestGetMCPGatewayURLWithInvalidPort(t *testing.T) { }) } } + +func TestGenerateMCPGatewayStartStep_WithEnvVars(t *testing.T) { + config := &MCPGatewayRuntimeConfig{ + Port: 8080, + } + mcpEnvVars := map[string]string{ + "GITHUB_MCP_SERVER_TOKEN": "${{ secrets.GITHUB_TOKEN }}", + "GH_AW_SAFE_OUTPUTS": "${{ env.GH_AW_SAFE_OUTPUTS }}", + "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}", + } + + step := generateMCPGatewayStartStep(config, mcpEnvVars) + stepStr := strings.Join(step, "\n") + + // Should include env block + assert.Contains(t, stepStr, "env:") + // Should include environment variables in alphabetical order + assert.Contains(t, stepStr, "GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}") + assert.Contains(t, stepStr, "GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GITHUB_TOKEN }}") + assert.Contains(t, stepStr, "GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}") + + // Verify alphabetical ordering (GH_AW_SAFE_OUTPUTS should come before GITHUB_*) + ghAwPos := strings.Index(stepStr, "GH_AW_SAFE_OUTPUTS") + githubMcpPos := strings.Index(stepStr, "GITHUB_MCP_SERVER_TOKEN") + githubTokenPos := strings.Index(stepStr, "GITHUB_TOKEN") + assert.Less(t, ghAwPos, githubMcpPos, "GH_AW_SAFE_OUTPUTS should come before GITHUB_MCP_SERVER_TOKEN") + assert.Less(t, githubMcpPos, githubTokenPos, "GITHUB_MCP_SERVER_TOKEN should come before GITHUB_TOKEN") +} + +func TestGenerateMCPGatewayStartStep_WithoutEnvVars(t *testing.T) { + config := &MCPGatewayRuntimeConfig{ + Port: 8080, + } + mcpEnvVars := map[string]string{} + + step := generateMCPGatewayStartStep(config, mcpEnvVars) + stepStr := strings.Join(step, "\n") + + // Should NOT include env block when no environment variables + assert.NotContains(t, stepStr, "env:") + // Should still have the run block + assert.Contains(t, stepStr, "run: |") + assert.Contains(t, stepStr, "Start MCP Gateway") +} diff --git a/pkg/workflow/mcp_servers.go b/pkg/workflow/mcp_servers.go index 71374f7dfba..0b902b9082a 100644 --- a/pkg/workflow/mcp_servers.go +++ b/pkg/workflow/mcp_servers.go @@ -50,6 +50,92 @@ func HasMCPServers(workflowData *WorkflowData) bool { return false } +// collectMCPEnvironmentVariables collects all MCP-related environment variables +// from the workflow configuration to be passed to both Setup MCPs and MCP Gateway steps +func collectMCPEnvironmentVariables(tools map[string]any, mcpTools []string, workflowData *WorkflowData, hasAgenticWorkflows bool) map[string]string { + envVars := make(map[string]string) + + // Check for GitHub MCP server token + hasGitHub := false + for _, toolName := range mcpTools { + if toolName == "github" { + hasGitHub = true + break + } + } + if hasGitHub { + githubTool := tools["github"] + customGitHubToken := getGitHubToken(githubTool) + effectiveToken := getEffectiveGitHubToken(customGitHubToken, workflowData.GitHubToken) + envVars["GITHUB_MCP_SERVER_TOKEN"] = effectiveToken + } + + // Check for safe-outputs env vars + hasSafeOutputs := false + for _, toolName := range mcpTools { + if toolName == "safe-outputs" { + hasSafeOutputs = true + break + } + } + if hasSafeOutputs { + envVars["GH_AW_SAFE_OUTPUTS"] = "${{ env.GH_AW_SAFE_OUTPUTS }}" + // Only add upload-assets env vars if upload-assets is configured + if workflowData.SafeOutputs.UploadAssets != nil { + envVars["GH_AW_ASSETS_BRANCH"] = "${{ env.GH_AW_ASSETS_BRANCH }}" + envVars["GH_AW_ASSETS_MAX_SIZE_KB"] = "${{ env.GH_AW_ASSETS_MAX_SIZE_KB }}" + envVars["GH_AW_ASSETS_ALLOWED_EXTS"] = "${{ env.GH_AW_ASSETS_ALLOWED_EXTS }}" + } + } + + // Check for safe-inputs env vars + hasSafeInputs := false + for _, toolName := range mcpTools { + if toolName == "safe-inputs" { + hasSafeInputs = true + break + } + } + if hasSafeInputs { + // Add server configuration env vars from step outputs + envVars["GH_AW_SAFE_INPUTS_PORT"] = "${{ steps.safe-inputs-start.outputs.port }}" + envVars["GH_AW_SAFE_INPUTS_API_KEY"] = "${{ steps.safe-inputs-start.outputs.api_key }}" + + // Add tool-specific env vars (secrets passthrough) + safeInputsSecrets := collectSafeInputsSecrets(workflowData.SafeInputs) + for envVarName, secretExpr := range safeInputsSecrets { + envVars[envVarName] = secretExpr + } + } + + // Check for agentic-workflows GITHUB_TOKEN + if hasAgenticWorkflows { + envVars["GITHUB_TOKEN"] = "${{ secrets.GITHUB_TOKEN }}" + } + + // Check for Playwright domain secrets + hasPlaywright := false + for _, toolName := range mcpTools { + if toolName == "playwright" { + hasPlaywright = true + break + } + } + if hasPlaywright { + // Extract all expressions from playwright arguments using ExpressionExtractor + if playwrightTool, ok := tools["playwright"]; ok { + allowedDomains := generatePlaywrightAllowedDomains(playwrightTool) + customArgs := getPlaywrightCustomArgs(playwrightTool) + playwrightAllowedDomainsSecrets := extractExpressionsFromPlaywrightArgs(allowedDomains, customArgs) + for envVarName, originalExpr := range playwrightAllowedDomainsSecrets { + envVars[envVarName] = originalExpr + } + } + } + + return envVars +} + // generateMCPSetup generates the MCP server configuration setup func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, engine CodingAgentEngine, workflowData *WorkflowData) { mcpServersLog.Print("Generating MCP server configuration setup") @@ -331,109 +417,24 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Use the engine's RenderMCPConfig method yaml.WriteString(" - name: Setup MCPs\n") - // Add env block for environment variables to prevent template injection - needsEnvBlock := false - hasGitHub := false - hasSafeOutputs := false - hasSafeInputs := false - hasPlaywright := false - var playwrightAllowedDomainsSecrets map[string]string - // Note: hasAgenticWorkflows is already declared earlier in this function + // Collect all MCP-related environment variables using centralized helper + mcpEnvVars := collectMCPEnvironmentVariables(tools, mcpTools, workflowData, hasAgenticWorkflows) - for _, toolName := range mcpTools { - if toolName == "github" { - hasGitHub = true - needsEnvBlock = true - } - if toolName == "safe-outputs" { - hasSafeOutputs = true - needsEnvBlock = true - } - if toolName == "safe-inputs" { - hasSafeInputs = true - // Safe-inputs now always needs env block for port and API key - needsEnvBlock = true - } - if toolName == "agentic-workflows" { - needsEnvBlock = true - } - if toolName == "playwright" { - hasPlaywright = true - // Extract all expressions from playwright arguments using ExpressionExtractor - if playwrightTool, ok := tools["playwright"]; ok { - allowedDomains := generatePlaywrightAllowedDomains(playwrightTool) - customArgs := getPlaywrightCustomArgs(playwrightTool) - playwrightAllowedDomainsSecrets = extractExpressionsFromPlaywrightArgs(allowedDomains, customArgs) - if len(playwrightAllowedDomainsSecrets) > 0 { - needsEnvBlock = true - } - } - } - } - - if needsEnvBlock { + // Add env block if any environment variables are needed + if len(mcpEnvVars) > 0 { yaml.WriteString(" env:\n") - // Add GitHub token env var if GitHub tool is present - if hasGitHub { - githubTool := tools["github"] - customGitHubToken := getGitHubToken(githubTool) - effectiveToken := getEffectiveGitHubToken(customGitHubToken, workflowData.GitHubToken) - yaml.WriteString(" GITHUB_MCP_SERVER_TOKEN: " + effectiveToken + "\n") - } - - // Add safe-outputs env vars if present - if hasSafeOutputs { - yaml.WriteString(" GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}\n") - // Only add upload-assets env vars if upload-assets is configured - if workflowData.SafeOutputs.UploadAssets != nil { - yaml.WriteString(" GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }}\n") - yaml.WriteString(" GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }}\n") - yaml.WriteString(" GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }}\n") - } + // Sort environment variable names for consistent output + envVarNames := make([]string, 0, len(mcpEnvVars)) + for envVarName := range mcpEnvVars { + envVarNames = append(envVarNames, envVarName) } + sort.Strings(envVarNames) - // Add safe-inputs env vars if present - if hasSafeInputs { - // Add server configuration env vars from step outputs - yaml.WriteString(" GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }}\n") - yaml.WriteString(" GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }}\n") - - // Add tool-specific env vars (secrets passthrough) - safeInputsSecrets := collectSafeInputsSecrets(workflowData.SafeInputs) - if len(safeInputsSecrets) > 0 { - // Sort env var names for consistent output - envVarNames := make([]string, 0, len(safeInputsSecrets)) - for envVarName := range safeInputsSecrets { - envVarNames = append(envVarNames, envVarName) - } - sort.Strings(envVarNames) - - for _, envVarName := range envVarNames { - secretExpr := safeInputsSecrets[envVarName] - fmt.Fprintf(yaml, " %s: %s\n", envVarName, secretExpr) - } - } - } - - // Add GITHUB_TOKEN for agentic-workflows if present - if hasAgenticWorkflows { - yaml.WriteString(" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n") - } - - // Add Playwright expression environment variables if present - if hasPlaywright && len(playwrightAllowedDomainsSecrets) > 0 { - // Sort env var names for consistent output - envVarNames := make([]string, 0, len(playwrightAllowedDomainsSecrets)) - for envVarName := range playwrightAllowedDomainsSecrets { - envVarNames = append(envVarNames, envVarName) - } - sort.Strings(envVarNames) - - for _, envVarName := range envVarNames { - originalExpr := playwrightAllowedDomainsSecrets[envVarName] - fmt.Fprintf(yaml, " %s: %s\n", envVarName, originalExpr) - } + // Write environment variables in sorted order + for _, envVarName := range envVarNames { + envVarValue := mcpEnvVars[envVarName] + fmt.Fprintf(yaml, " %s: %s\n", envVarName, envVarValue) } } @@ -442,7 +443,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, engine.RenderMCPConfig(yaml, tools, mcpTools, workflowData) // Generate MCP gateway steps if configured (after Setup MCPs completes) - gatewaySteps := generateMCPGatewaySteps(workflowData, nil) + gatewaySteps := generateMCPGatewaySteps(workflowData, mcpEnvVars) for _, step := range gatewaySteps { for _, line := range step { yaml.WriteString(line + "\n") diff --git a/specs/safe-output-handler-manager.md b/specs/safe-output-handler-manager.md deleted file mode 100644 index 36c20a484ee..00000000000 --- a/specs/safe-output-handler-manager.md +++ /dev/null @@ -1,269 +0,0 @@ -# Safe Output Handler Manager Implementation - -## Overview - -This document describes the safe output handler manager implementation, which refactors the safe output processing system from multiple individual steps to a single dispatcher step. - -## Architecture - -### Current Multi-Step Approach - -Each safe output type (create_issue, add_comment, etc.) generates a separate step in the GitHub Actions workflow: - -```yaml -jobs: - safe_outputs: - steps: - - name: Setup Scripts - - name: Download agent output - - name: Create Issue - if: contains(fromJSON(env.GH_AW_AGENT_OUTPUT).items[*].type, 'create_issue') - uses: actions/github-script@... - with: - script: | - const { main } = require('./create_issue.cjs'); - await main(); - - name: Add Comment - if: contains(fromJSON(env.GH_AW_AGENT_OUTPUT).items[*].type, 'add_comment') - uses: actions/github-script@... - with: - script: | - const { main } = require('./add_comment.cjs'); - await main(); - # ... more steps for each safe output type -``` - -### New Handler Manager Approach - -A single step uses the handler manager to dispatch to appropriate handlers: - -```yaml -jobs: - safe_outputs: - steps: - - name: Setup Scripts - - name: Download agent output - - name: Process Safe Outputs - uses: actions/github-script@... - with: - script: | - const { main } = require('./safe_output_handler_manager.cjs'); - await main(); -``` - -## Implementation Details - -### Handler Manager (`safe_output_handler_manager.cjs`) - -The handler manager is responsible for: - -1. **Configuration Loading**: - ```javascript - function loadConfig() { - // Read from /tmp/gh-aw/safeoutputs/config.json - // Normalize keys (create-issue → create_issue) - return config; - } - ``` - -2. **Handler Registration**: - ```javascript - const HANDLER_MAP = { - create_issue: "./create_issue.cjs", - add_comment: "./add_comment.cjs", - create_discussion: "./create_discussion.cjs", - close_issue: "./close_issue.cjs", - close_discussion: "./close_discussion.cjs", - }; - - function loadHandlers(config) { - // Only load handlers for enabled types - // Store in Map - } - ``` - -3. **Message Processing**: - ```javascript - function processMessages(handlers, messages) { - // Process messages in order of appearance - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - const handler = handlers.get(message.type); - - if (handler) { - await handler.main(); - } - } - } - ``` - -### Go Compiler Integration - -The Go compiler generates the handler manager step: - -```go -func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string { - var steps []string - - steps = append(steps, " - name: Process Safe Outputs\n") - steps = append(steps, " id: process_safe_outputs\n") - steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/github-script"))) - - // Add environment variables for all enabled handlers - c.addAllSafeOutputConfigEnvVars(&steps, data) - - // Call handler manager - steps = append(steps, " script: |\n") - steps = append(steps, " const { main } = require('"+SetupActionDestination+"/safe_output_handler_manager.cjs');\n") - steps = append(steps, " await main();\n") - - return steps -} -``` - -## Handler Compatibility - -All existing handlers are compatible without modification because they: - -1. **Export a `main()` function**: - ```javascript - async function main() { - // Handler logic - } - module.exports = { main }; - ``` - -2. **Access agent output internally**: - ```javascript - const result = loadAgentOutput(); - const items = result.items.filter(item => item.type === "create_issue"); - ``` - -3. **Read configuration from environment and config file**: - ```javascript - const titlePrefix = process.env.GH_AW_ISSUE_TITLE_PREFIX; - const config = getSafeOutputConfig("create_issue"); - ``` - -4. **Set outputs independently**: - ```javascript - core.setOutput("issue_number", issueNumber); - core.setOutput("issue_url", issueUrl); - ``` - -## Benefits - -### 1. Reduced Workflow Complexity -- **Before**: N steps (one per safe output type) -- **After**: 1 step (handler manager) -- Easier to read and understand workflow YAML - -### 2. Better Temporary ID Management -- Shared temporary ID map across all handlers -- Consistent ID resolution for cross-references -- Simpler debugging of ID-related issues - -### 3. Sequential Processing in Order of Appearance -- Messages are processed in the order they appear in the agent output file -- Preserves the natural flow and dependencies as written by the agent -- Simpler logic without complex ordering rules - -### 4. Easier Extensibility -- Add new handlers by updating `HANDLER_MAP` -- No Go code changes needed for new handler types -- Handlers remain isolated and testable - -### 5. Improved Error Handling -- Centralized error reporting -- Handlers can fail independently -- Better logging and diagnostics - -## Testing - -### Unit Tests (`safe_output_handler_manager.test.cjs`) - -Tests cover: -- Configuration loading and normalization -- Handler registration based on configuration -- Sequential message processing in order of appearance -- Error handling and recovery - -### Integration Testing - -To test the handler manager: - -1. Create a workflow with multiple safe output types -2. Compile the workflow -3. Verify single "Process Safe Outputs" step is generated -4. Run the workflow and verify correct behavior -5. Check that outputs match existing implementation - -## Migration Path - -### Phase 1: Foundation (Current) -- [x] Implement handler manager -- [x] Add tests -- [x] Add Go compiler functions - -### Phase 2: Integration (Next) -- [ ] Modify `buildConsolidatedSafeOutputsJob` to use handler manager -- [ ] Keep complex operations (PRs, assets) as separate steps -- [ ] Run integration tests - -### Phase 3: Validation -- [ ] Test with real workflows -- [ ] Compare outputs with existing implementation -- [ ] Performance testing - -### Phase 4: Cleanup -- [ ] Remove old multi-step approach -- [ ] Update documentation -- [ ] Release - -## Configuration Example - -```json -{ - "create-issue": { - "enabled": true, - "max": 5, - "title-prefix": "[AI]", - "labels": ["ai-generated"] - }, - "add-comment": { - "enabled": true, - "max": 3, - "target": "triggering" - }, - "create-discussion": { - "enabled": true - }, - "close-issue": { - "enabled": false - } -} -``` - -With this configuration, the handler manager will: -1. Load handlers for create_issue, add_comment, and create_discussion -2. Skip close_issue (disabled) -3. Process messages in the order they appear in the agent output file - -## Performance Considerations - -- **Handler Loading**: Handlers are loaded once at startup, not per message -- **Sequential Processing**: O(n) operation to process messages in order -- **Processing**: Each message is processed individually by its handler -- **Memory**: Temporary ID map is maintained in memory for the duration of the step - -## Future Enhancements - -1. **Batching**: Group consecutive messages of same type for batch processing -2. **Retry Logic**: Add retry for transient failures -3. **Metrics**: Collect processing metrics per handler -4. **Dynamic Loading**: Load handlers on-demand rather than upfront -5. **Handler Plugins**: Support external handler modules - -## Conclusion - -The handler manager implementation provides a cleaner, more maintainable architecture for safe output processing while maintaining full backward compatibility with existing handlers.