From 5b441b193f7dc2b75f667f04f908f98f3e70b65f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:43:45 +0000 Subject: [PATCH 1/5] Initial plan From 6249db3639f80102ef775aa5788b1cca5f644d60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:48:43 +0000 Subject: [PATCH 2/5] Initial exploration of campaign documentation structure Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- .github/workflows/code-simplifier.lock.yml | 117 +++++++++------------ 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml index e96a36cc15..420aab3450 100644 --- a/.github/workflows/code-simplifier.lock.yml +++ b/.github/workflows/code-simplifier.lock.yml @@ -87,7 +87,7 @@ jobs: GH_AW_ASSETS_BRANCH: "" GH_AW_ASSETS_MAX_SIZE_KB: 0 GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /tmp/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json outputs: @@ -143,19 +143,7 @@ jobs: env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI - run: | - # Download official Copilot CLI installer script - curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh - - # Execute the installer with the specified version - # Pass VERSION directly to sudo to ensure it's available to the installer script - sudo VERSION=0.0.388 bash /tmp/copilot-install.sh - - # Cleanup - rm -f /tmp/copilot-install.sh - - # Verify installation - copilot --version + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.389 - name: Install awf binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.10.0 - name: Determine automatic lockdown mode for GitHub MCP server @@ -169,7 +157,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/github-mcp-server:v0.29.0 ghcr.io/githubnext/gh-aw-mcpg:v0.0.74 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/github-mcp-server:v0.29.0 ghcr.io/githubnext/gh-aw-mcpg:v0.0.76 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -350,10 +338,49 @@ jobs: } } EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + API_KEY="" + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + PORT=3001 + + # Register API key as secret to mask it from logs + echo "::add-mask::${API_KEY}" + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + - name: Start MCP gateway id: start-mcp-gateway env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | @@ -370,7 +397,7 @@ jobs: # Register API key as secret to mask it from logs echo "::add-mask::${MCP_GATEWAY_API_KEY}" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e DEBUG="*" -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/githubnext/gh-aw-mcpg:v0.0.74' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e DEBUG="*" -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/githubnext/gh-aw-mcpg:v0.0.76' mkdir -p /home/runner/.copilot cat << MCPCONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -387,42 +414,10 @@ jobs: } }, "safeoutputs": { - "type": "stdio", - "container": "node:lts-alpine", - "entrypoint": "node", - "entrypointArgs": ["/opt/gh-aw/safeoutputs/mcp-server.cjs"], - "mounts": ["/opt/gh-aw:/opt/gh-aw:ro", "/tmp/gh-aw:/tmp/gh-aw:rw", "${{ github.workspace }}:${{ github.workspace }}:rw"], - "env": { - "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", - "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", - "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", - "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", - "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", - "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", - "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", - "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", - "GITHUB_SERVER_URL": "\${GITHUB_SERVER_URL}", - "GITHUB_SHA": "\${GITHUB_SHA}", - "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", - "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", - "GITHUB_RUN_ID": "\${GITHUB_RUN_ID}", - "GITHUB_RUN_NUMBER": "\${GITHUB_RUN_NUMBER}", - "GITHUB_RUN_ATTEMPT": "\${GITHUB_RUN_ATTEMPT}", - "GITHUB_JOB": "\${GITHUB_JOB}", - "GITHUB_ACTION": "\${GITHUB_ACTION}", - "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", - "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", - "GITHUB_ACTOR": "\${GITHUB_ACTOR}", - "GITHUB_ACTOR_ID": "\${GITHUB_ACTOR_ID}", - "GITHUB_TRIGGERING_ACTOR": "\${GITHUB_TRIGGERING_ACTOR}", - "GITHUB_WORKFLOW": "\${GITHUB_WORKFLOW}", - "GITHUB_WORKFLOW_REF": "\${GITHUB_WORKFLOW_REF}", - "GITHUB_WORKFLOW_SHA": "\${GITHUB_WORKFLOW_SHA}", - "GITHUB_REF": "\${GITHUB_REF}", - "GITHUB_REF_NAME": "\${GITHUB_REF_NAME}", - "GITHUB_REF_TYPE": "\${GITHUB_REF_TYPE}", - "GITHUB_HEAD_REF": "\${GITHUB_HEAD_REF}", - "GITHUB_BASE_REF": "\${GITHUB_BASE_REF}" + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" } } }, @@ -445,7 +440,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.388", + agent_version: "0.0.389", workflow_name: "Code Simplifier", experimental: false, supports_tools_allowlist: true, @@ -463,7 +458,7 @@ jobs: allowed_domains: [], firewall_enabled: true, awf_version: "v0.10.0", - awmg_version: "v0.0.74", + awmg_version: "v0.0.76", steps: { firewall: "squid" }, @@ -1379,19 +1374,7 @@ jobs: env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI - run: | - # Download official Copilot CLI installer script - curl -fsSL https://raw.githubusercontent.com/github/copilot-cli/main/install.sh -o /tmp/copilot-install.sh - - # Execute the installer with the specified version - # Pass VERSION directly to sudo to ensure it's available to the installer script - sudo VERSION=0.0.388 bash /tmp/copilot-install.sh - - # Cleanup - rm -f /tmp/copilot-install.sh - - # Verify installation - copilot --version + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.389 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): From deaed167cb7f7a9557e126ba2f49640ccd7d357d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:54:18 +0000 Subject: [PATCH 3/5] Update campaign documentation: fix specs and rename flow to lifecycle - Updated Campaign Spec page with accurate field descriptions - Fixed discovery-repos/discovery-orgs vs allowed-repos/allowed-orgs confusion - Added required fields section for discovery configuration - Updated KPI section with all required fields (baseline, target, time-window-days) - Added proper YAML frontmatter delimiters to examples - Removed duplicate governance section - Renamed "Campaign Flow & Lifecycle" to "Campaign Lifecycle" - Updated all references from /flow/ to /lifecycle/ Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- docs/src/content/docs/examples/campaigns.md | 2 +- .../docs/guides/campaigns/getting-started.md | 2 +- .../content/docs/guides/campaigns/index.mdx | 4 +- .../campaigns/{flow.md => lifecycle.md} | 2 +- .../content/docs/guides/campaigns/specs.md | 106 ++++++++++++------ 5 files changed, 79 insertions(+), 37 deletions(-) rename docs/src/content/docs/guides/campaigns/{flow.md => lifecycle.md} (99%) diff --git a/docs/src/content/docs/examples/campaigns.md b/docs/src/content/docs/examples/campaigns.md index 116d41f6ef..77ef5c46fd 100644 --- a/docs/src/content/docs/examples/campaigns.md +++ b/docs/src/content/docs/examples/campaigns.md @@ -95,7 +95,7 @@ Workers are stored alongside regular workflows, not in campaign-specific folders ## Learn More - [Campaign Guides](/gh-aw/guides/campaigns/) - Campaign setup and configuration -- [Flow & lifecycle](/gh-aw/guides/campaigns/flow/) - How the orchestrator runs +- [Campaign lifecycle](/gh-aw/guides/campaigns/lifecycle/) - How the orchestrator runs - [Safe Outputs](/gh-aw/reference/safe-outputs/) - dispatch_workflow configuration ## Pattern Analysis diff --git a/docs/src/content/docs/guides/campaigns/getting-started.md b/docs/src/content/docs/guides/campaigns/getting-started.md index 3a063a5b91..dd3a8576b0 100644 --- a/docs/src/content/docs/guides/campaigns/getting-started.md +++ b/docs/src/content/docs/guides/campaigns/getting-started.md @@ -42,7 +42,7 @@ The orchestrator runs on a schedule (daily by default) and will: - sync issues/PRs into the Project - post a Project status update each run -For details, see [Flow & lifecycle](/gh-aw/guides/campaigns/flow/). +For details, see [Campaign lifecycle](/gh-aw/guides/campaigns/lifecycle/). ## Keep it simple (best practices) diff --git a/docs/src/content/docs/guides/campaigns/index.mdx b/docs/src/content/docs/guides/campaigns/index.mdx index 9aebf71a37..38ff18c905 100644 --- a/docs/src/content/docs/guides/campaigns/index.mdx +++ b/docs/src/content/docs/guides/campaigns/index.mdx @@ -81,11 +81,11 @@ governance: The spec is validated and compiled to create the campaign's orchestrator workflow (`.campaign.lock.yml`) that GitHub Actions can execute. Think of it like compiling code: you write a human-friendly campaign definition, and the compiler produces a hardened, machine-executable workflow. -**What happens next**: Once merged, the campaign orchestrator runs on schedule (daily by default) to [dispatch worker workflows, discover items, update the Project board, and report status](/gh-aw/guides/campaigns/flow/). The campaign coordinates all work toward your objective while respecting governance limits. +**What happens next**: Once merged, the campaign orchestrator runs on schedule (daily by default) to [dispatch worker workflows, discover items, update the Project board, and report status](/gh-aw/guides/campaigns/lifecycle/). The campaign coordinates all work toward your objective while respecting governance limits. ## Next steps - [Getting started](/gh-aw/guides/campaigns/getting-started/) – create your first campaign - [Campaign specs](/gh-aw/guides/campaigns/specs/) – fields you can configure -- [Flow & Lifecycle](/gh-aw/guides/campaigns/flow/) – what the orchestrator does each run +- [Campaign Lifecycle](/gh-aw/guides/campaigns/lifecycle/) – what the orchestrator does each run - [CLI commands](/gh-aw/guides/campaigns/cli-commands/) – inspect and validate campaigns diff --git a/docs/src/content/docs/guides/campaigns/flow.md b/docs/src/content/docs/guides/campaigns/lifecycle.md similarity index 99% rename from docs/src/content/docs/guides/campaigns/flow.md rename to docs/src/content/docs/guides/campaigns/lifecycle.md index 8b3e3feb09..dd76b2c3f8 100644 --- a/docs/src/content/docs/guides/campaigns/flow.md +++ b/docs/src/content/docs/guides/campaigns/lifecycle.md @@ -1,5 +1,5 @@ --- -title: Campaign Flow & Lifecycle +title: Campaign Lifecycle description: What happens when the campaign orchestrator runs, and how to pause or finish a campaign. banner: content: 'Do not use. Campaigns are still incomplete and may produce unreliable or unintended results.' diff --git a/docs/src/content/docs/guides/campaigns/specs.md b/docs/src/content/docs/guides/campaigns/specs.md index 9c8881a5e3..5d18f22313 100644 --- a/docs/src/content/docs/guides/campaigns/specs.md +++ b/docs/src/content/docs/guides/campaigns/specs.md @@ -13,6 +13,7 @@ The campaign spec is a reviewable configuration file that: - names the campaign - points to a GitHub Project for tracking +- defines where to discover worker-created items - lists the workflows the orchestrator should dispatch - defines goals (objective + KPIs) @@ -22,6 +23,7 @@ Most users should create specs via the [Getting started flow](/gh-aw/guides/camp ```yaml # .github/workflows/framework-upgrade.campaign.md +--- id: framework-upgrade version: "v1" name: "Framework Upgrade" @@ -30,16 +32,22 @@ description: "Move services to Framework vNext" project-url: "https://github.com/orgs/ORG/projects/1" tracker-label: "campaign:framework-upgrade" -# Optional: Custom GitHub token for Projects v2 operations -# project-github-token: "${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}" - -## Optional: Repositories this campaign can operate on -## If omitted, defaults to the repository where the campaign spec lives. -allowed-repos: +# Discovery: Where to find worker-created issues/PRs +discovery-repos: - "myorg/service-a" - "myorg/service-b" +# Or use discovery-orgs for organization-wide discovery: +# discovery-orgs: +# - "myorg" -# Optional: Organizations this campaign can operate on +# Optional: Custom GitHub token for Projects v2 operations +# project-github-token: "${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}" + +# Optional: Restrict which repos this campaign can operate on +# If omitted, defaults to the repository where the campaign spec lives. +# allowed-repos: +# - "myorg/service-a" +# - "myorg/service-b" # allowed-orgs: # - "myorg" @@ -48,12 +56,19 @@ kpis: - id: services_upgraded name: "Services upgraded" priority: primary - direction: "increase" + unit: count + baseline: 0 target: 50 + time-window-days: 30 + direction: "increase" - id: incidents name: "Incidents caused" - direction: "decrease" + priority: supporting + unit: count + baseline: 5 target: 0 + time-window-days: 30 + direction: "decrease" workflows: - framework-upgrade @@ -61,29 +76,48 @@ workflows: state: "active" owners: - "platform-team" +--- ``` ## Core fields (what they do) ### Required -- `id`: stable identifier used for file naming and reporting. -- `name`: human-friendly name. -- `project-url`: GitHub Project used for tracking. -- `objective`: one sentence describing what “done” means. -- `kpis`: measures used in status updates. -- `workflows`: workflow IDs the orchestrator can dispatch (via `workflow_dispatch`). -- `state`: lifecycle state, typically `active`. +- `id`: Stable identifier used for file naming and reporting (lowercase letters, digits, hyphens only). +- `name`: Human-friendly name for the campaign. +- `project-url`: GitHub Project URL used for tracking. + +### Required for discovery + +When your campaign uses `workflows` or `tracker-label`, you must specify where to discover worker-created items: + +- `discovery-repos`: List of repositories (in `owner/repo` format) where worker workflows create issues/PRs. +- `discovery-orgs`: List of GitHub organizations where worker workflows operate (searches all repos in those orgs). + +At least one of `discovery-repos` or `discovery-orgs` is required when using workflows or tracker labels. + +### Commonly used + +- `objective`: One sentence describing what success means for this campaign. +- `kpis`: List of 1-3 KPIs used to measure progress toward the objective. +- `workflows`: Workflow IDs the orchestrator can dispatch via `workflow_dispatch`. +- `tracker-label`: Label used to discover worker-created issues/PRs (commonly `campaign:`). +- `state`: Lifecycle state (`planned`, `active`, `paused`, `completed`, or `archived`). ### Optional -- `tracker-label`: label used to help discovery across runs (commonly `campaign:`). -- `allowed-repos`: repositories the campaign can operate on (defaults to the repo containing the spec). -- `allowed-orgs`: organizations the campaign can operate on. -- `project-github-token`: token to use for Projects operations when `GITHUB_TOKEN` isn’t enough. +- `allowed-repos`: Repositories this campaign can operate on (defaults to the repo containing the spec). +- `allowed-orgs`: Organizations this campaign can operate on. +- `project-github-token`: Token expression for Projects v2 operations (e.g., `${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }}`). +- `description`: Brief description of the campaign. +- `version`: Spec version (defaults to `v1`). +- `owners`: Primary human owners for this campaign. +- `governance`: Pacing and opt-out policies (see Governance section below). > [!IMPORTANT] -> Use `priority: primary` (not `primary: true`) to mark your primary KPI. +> - Use `priority: primary` (not `primary: true`) to mark your primary KPI. +> - The `discovery-*` fields control WHERE to search for worker outputs. +> - The `allowed-*` fields control WHERE the campaign can operate. ## Strategic goals (objective + KPIs) @@ -94,13 +128,25 @@ Use `objective` and `kpis` to define what “done” means and how progress shou ## KPIs (recommended shape) -Keep KPIs small and crisp: +Each KPI requires these fields: -- Use 1 primary KPI + a few supporting KPIs. -- Use `direction: increase|decrease|maintain` to describe the desired trend. -- Use `target` when there is a clear threshold. +- `name`: Human-readable KPI name. +- `baseline`: Starting value. +- `target`: Goal value. +- `time-window-days`: Rolling window for measurement (e.g., 7, 14, 30 days). -If you define `kpis`, also define `objective` (and vice versa). It keeps the spec reviewable and makes reports consistent. +Optional fields: + +- `id`: Stable identifier (lowercase letters, digits, hyphens). +- `priority`: `primary` or `supporting` (exactly one KPI should be primary). +- `unit`: Unit of measurement (e.g., `count`, `percent`, `days`). +- `direction`: `increase` or `decrease` (describes improvement direction). +- `source`: Signal source (`ci`, `pull_requests`, `code_security`, or `custom`). + +Keep KPIs small and crisp: + +- Use 1 primary KPI + up to 2 supporting KPIs (maximum 3 total). +- When you define `kpis`, also define `objective` (and vice versa). ## Unified tracking (GitHub Project) @@ -115,11 +161,7 @@ Project updates are applied by the orchestrator using safe outputs; see [Update Use `workflows` to list the dispatchable workflows (“workers”) the orchestrator can trigger via `workflow_dispatch`. -For worker requirements and dispatch behavior, see [Dispatching worker workflows](/gh-aw/guides/campaigns/flow/#dispatching-worker-workflows). - -## Governance (pacing) - -Use `governance` to cap how much the orchestrator updates per run. +For worker requirements and dispatch behavior, see [Dispatching worker workflows](/gh-aw/guides/campaigns/lifecycle/#dispatching-worker-workflows). ## Governance (pacing & safety) @@ -147,5 +189,5 @@ governance: ## Next -- See [Flow & lifecycle](/gh-aw/guides/campaigns/flow/) for what happens each run. +- See [Campaign lifecycle](/gh-aw/guides/campaigns/lifecycle/) for what happens each run. - See [CLI commands](/gh-aw/guides/campaigns/cli-commands/) to validate and inspect campaigns. From 71c135e722d0bcff0c26ebb8bd6d763a6a8b9923 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:55:10 +0000 Subject: [PATCH 4/5] Update navigation config to reference Campaign Lifecycle page --- docs/astro.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 645ee9bb8b..05097a202c 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -193,7 +193,7 @@ export default defineConfig({ { label: 'About campaigns', link: '/guides/campaigns/' }, { label: 'Getting Started', link: '/guides/campaigns/getting-started/' }, { label: 'Campaign Specs', link: '/guides/campaigns/specs/' }, - { label: 'Flow & Lifecycle', link: '/guides/campaigns/flow/' }, + { label: 'Campaign Lifecycle', link: '/guides/campaigns/lifecycle/' }, { label: 'CLI Commands', link: '/guides/campaigns/cli-commands/' }, ], }, From b2d0c90deedc9f4d3c5ae219a0cf7046c7b8108a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:56:51 +0000 Subject: [PATCH 5/5] Address code review: rename "Minimal spec" to "Complete spec example" The example includes all commonly-used fields and represents a complete, working campaign configuration rather than a truly minimal one. This is more useful for users getting started. Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- docs/src/content/docs/guides/campaigns/specs.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/guides/campaigns/specs.md b/docs/src/content/docs/guides/campaigns/specs.md index 5d18f22313..54747dd06d 100644 --- a/docs/src/content/docs/guides/campaigns/specs.md +++ b/docs/src/content/docs/guides/campaigns/specs.md @@ -19,7 +19,9 @@ The campaign spec is a reviewable configuration file that: Most users should create specs via the [Getting started flow](/gh-aw/guides/campaigns/getting-started/). -## Minimal spec +## Complete spec example + +This example shows a complete, working campaign spec with all commonly-used fields: ```yaml # .github/workflows/framework-upgrade.campaign.md