diff --git a/docs/bootstrap-implementation-summary.md b/docs/bootstrap-implementation-summary.md index 8c61e24583..3e1b2fc214 100644 --- a/docs/bootstrap-implementation-summary.md +++ b/docs/bootstrap-implementation-summary.md @@ -165,7 +165,7 @@ Workers guarantee outputs are discoverable via: #### Tracker Label -Format: `campaign:{campaign_id}` +Format: `z_campaign_{campaign_id}` - Applied to ALL worker-created items - Enables discovery by campaign orchestrator @@ -232,7 +232,7 @@ workers: idempotency-strategy: pr-title-based priority: 10 -tracker-label: campaign:security-q1-2025 +tracker-label: z_campaign_security-q1-2025 ``` Note: The tracker-label is defined once at the campaign level and automatically applied by all workers. @@ -244,7 +244,7 @@ Note: The tracker-label is defined once at the campaign level and automatically - Dispatches `security-scanner` with `{severity: "high", max-alerts: 20}` - Scanner finds 15 high-severity alerts - Scanner creates 15 issues with: - - Label: `campaign:security-q1-2025` + - Label: `z_campaign_security-q1-2025` - Title: `[scan-owner-repo] High severity alerts found` 2. **Run 2: Discovery (discovery = 15)** diff --git a/docs/campaign-workers.md b/docs/campaign-workers.md index 99088d32f7..94e8df623c 100644 --- a/docs/campaign-workers.md +++ b/docs/campaign-workers.md @@ -183,7 +183,7 @@ on: The `campaign_id` identifies the campaign orchestrating this worker. Use it to: -- Label created items: `campaign:${campaign_id}` +- Label created items: `z_campaign_${campaign_id}` - Generate deterministic keys: `campaign-${campaign_id}-${work_item_id}` - Track work in repo-memory: `memory/campaigns/${campaign_id}/` @@ -233,7 +233,7 @@ Use this key in: Before creating any GitHub resource: 1. **Search for existing items** with the deterministic key -2. **Filter by campaign label**: `campaign:${campaign_id}` +2. **Filter by campaign tracker label**: `z_campaign_${campaign_id}` 3. **If found**: Skip or update existing item 4. **If not found**: Proceed with creation @@ -256,7 +256,7 @@ if (existingPRs.total_count > 0) { Apply the campaign tracker label to all created items: -- Label format: `campaign:${campaign_id}` +- Label format: `z_campaign_${campaign_id}` - Prevents interference from other workflows - Enables discovery by orchestrator @@ -330,7 +330,7 @@ If no existing work found: 1. Create branch with deterministic name 2. Make required changes 3. Create PR with deterministic title -4. Apply labels: `campaign:${campaignId}`, [additional labels] +4. Apply labels: `z_campaign_${campaignId}`, [additional labels] ## Step 4: Report Status @@ -362,7 +362,7 @@ PR title pattern: `[${workKey}] ${description}` Before creating PR: 1. Search for PRs with `${workKey}` in title -2. Filter by `campaign:${campaignId}` label +2. Filter by `z_campaign_${campaignId}` label 3. If found: Update with comment or skip 4. If not: Create new PR ``` @@ -390,7 +390,7 @@ Issue title pattern: `[${workItemId}] ${description}` Before creating issue: 1. Search for issues with `[${workItemId}]` in title -2. Filter by `campaign:${campaignId}` label +2. Filter by `z_campaign_${campaignId}` label 3. If found: Update existing issue 4. If not: Create new issue ``` @@ -496,7 +496,7 @@ await github.issues.addLabels({ owner: payload.repository.split('/')[0], repo: payload.repository.split('/')[1], issue_number: pr.number, - labels: [`campaign:${campaignId}`, 'security', 'automated'] + labels: [`z_campaign_${campaignId}`, 'security', 'automated'] }); console.log(`Created PR: ${pr.html_url}`); @@ -564,7 +564,7 @@ Before using a worker in a campaign: - Second run should skip/update without errors 3. **Check labels** on created items: - - Verify `campaign:test-campaign` label is applied +- Verify `z_campaign_test-campaign` label is applied - Confirm tracker-id is in description (if applicable) 4. **Test error cases**: diff --git a/docs/src/content/docs/examples/campaigns.md b/docs/src/content/docs/examples/campaigns.md index f8742ea3aa..e08ce416e9 100644 --- a/docs/src/content/docs/examples/campaigns.md +++ b/docs/src/content/docs/examples/campaigns.md @@ -21,18 +21,17 @@ The campaign coordinates three workers (scanner, fixer, reviewer) that create se ### Key patterns -**Orchestrator responsibilities:** +**Orchestrator responsibilities (dispatch-only):** +- Runs discovery precomputation (writes a manifest for the agent) - Dispatches workers on schedule -- Discovers worker outputs via labels -- Updates project board -- Reports progress metrics +- Coordinates work by dispatching allowlisted workflows **Worker responsibilities:** - Accepts `workflow_dispatch` only - Uses standardized inputs - Generates deterministic keys - Checks for existing work -- Labels outputs with `campaign:` +- Labels outputs with the campaign tracker label (defaults to `z_campaign_`) ## Security Scanner Worker @@ -58,7 +57,7 @@ The worker: 3. Generates deterministic key: `campaign-{id}-{repo}-{vuln_id}` 4. Checks for existing PR with that key 5. Creates PR only if none exists -6. Labels PR with `campaign:{id}` +6. Labels PR with `z_campaign_{id}` ## Worker design patterns @@ -168,7 +167,7 @@ Workers are regular workflows, not in campaign-specific folders. The dispatch-on type: create_pull_request title: "[$KEY] Fix vulnerability 123" body: "Automated security fix" - labels: "campaign:${{ inputs.campaign_id }}" + labels: "z_campaign_${{ inputs.campaign_id }}" ``` ## Independent workflows @@ -183,7 +182,9 @@ on: workflow_dispatch: # Creates items with campaign label for discovery -labels: ["campaign:security-audit", "security"] +labels: ["z_campaign_security-audit", "security"] + +The recommended campaign tracking label format is `z_campaign_`. If you need compatibility with older tooling that filters `campaign:*` labels, you can optionally apply both labels. ``` The campaign discovers these via tracker labels without controlling execution. diff --git a/docs/src/content/docs/examples/issue-pr-events/projectops.md b/docs/src/content/docs/examples/issue-pr-events/projectops.md index 3c462925dc..df38a02c92 100644 --- a/docs/src/content/docs/examples/issue-pr-events/projectops.md +++ b/docs/src/content/docs/examples/issue-pr-events/projectops.md @@ -140,7 +140,7 @@ ProjectOps complements [GitHub's built-in Projects automation](https://docs.gith **Use descriptive project names** that clearly indicate purpose and scope. Prefer "Performance Optimization Q1 2026" over "Project 1". -**Leverage tracking labels** (`campaign:`) for grouping related work across issues and PRs, enabling orchestrator discovery. +**Leverage tracking labels** (`z_campaign_`) for grouping related work across issues and PRs, enabling campaign discovery. **Set meaningful field values** like status, priority, and effort to enable effective filtering and sorting on boards. diff --git a/docs/src/content/docs/guides/campaigns/index.mdx b/docs/src/content/docs/guides/campaigns/index.mdx index be7f033035..da4b6a6a21 100644 --- a/docs/src/content/docs/guides/campaigns/index.mdx +++ b/docs/src/content/docs/guides/campaigns/index.mdx @@ -9,39 +9,91 @@ banner: ## What are Agentic Campaigns? -**GitHub agentic campaigns** are AI-powered orchestration workflows that coordinate multiple **[agentic workflows](/gh-aw/reference/glossary/#agentic-workflow)** toward a shared goal across one or more repositories. +**[Agentic campaigns](/gh-aw/reference/glossary/#agentic-campaign)** are initiatives that coordinate multiple [agentic workflows](/gh-aw/reference/glossary/#agentic-workflow) toward a shared goal. -Just as [agentic workflows](/gh-aw/introduction/overview/) use natural language instructions to give an AI agent tasks and [safe outputs](/gh-aw/reference/safe-outputs/) to enable controlled GitHub operations, campaigns use **[campaign specs](/gh-aw/guides/campaigns/specs/)** to give an **orchestrator agent** high-level strategic instructions. The orchestrator agent interprets your campaign's objective and KPIs, then autonomously manages worker workflows to achieve the goal. +In practice, a campaign runs on a schedule, kicks off worker workflows in small batches, and tracks progress in [GitHub Projects](/gh-aw/reference/glossary/#github-projects-projects-v2). -## When to Use Campaigns +## When to use Campaigns -Use campaigns when your goal requires: -- **Multiple workflow runs** across one or more repositories -- **Coordination** of worker workflows doing similar tasks -- **Tracking** of created issues and pull requests -- **Progress reporting** against strategic objectives -- **Governance** with pacing controls and safety limits +Choose a campaign when you need coordination across repos or multiple workflows, with progress tracked in one place. -If a single workflow is enough (even on a schedule), start with a standalone agentic workflow. +- **Coordinate multiple worker workflows** that each do part of the job +- **Work across many repositories** (using campaign scope) +- **Track progress in one place** (a single [GitHub Project](/gh-aw/reference/glossary/#github-projects-projects-v2)) +- **Roll out changes in batches across repos** (repeatable runs with per-run limits and central tracking) -## How Campaigns Work +If you only need one workflow in one repo, a single scheduled agentic workflow is usually simpler. -Campaigns provide an orchestration layer on top of your existing workflows: +If you're not sure yet, start small (one repo, one worker), then expand scope or add workers once you know what you need. -1. **Define your goal** in a [campaign spec](/gh-aw/guides/campaigns/specs/) with objectives, KPIs, and governance rules -2. **Compile to orchestrator** – the spec becomes a workflow that runs on schedule (daily by default) -3. **Orchestrator coordinates** – dispatches worker workflows, tracks their outputs, updates a GitHub Project board -4. **Progress tracked** – campaign publishes metrics and status updates against your KPIs +## Campaign Specs -**Key concepts:** -- **Campaign spec**: YAML configuration defining goals, governance, and worker coordination -- **Orchestrator agent**: AI agent managing campaign execution, worker dispatch, and progress tracking -- **Worker workflows**: Your existing dispatchable workflows that do the actual work +To create a campaign, you write a single markdown file: + +- `.github/workflows/.campaign.md` + +That file has two parts: + +- **[Frontmatter](/gh-aw/reference/glossary/#frontmatter)**: the configuration ([GitHub Project URL](/gh-aw/reference/glossary/#github-projects-projects-v2), scope, worker list, governance limits) +- **Markdown body**: the narrative (objective, KPIs, prioritization, constraints) + +Here’s a small example: + +```markdown wrap +--- +id: dependency-upgrades +project-url: "https://github.com/orgs/ORG/projects/1" +scope: + - "org:ORG" +workflows: + - dependency-scanner + - dependency-upgrader +--- + +# Dependency Upgrades + +Upgrade dependencies across repos. +``` + +See: [Campaign specs](/gh-aw/guides/campaigns/specs/). + +## From markdown to GitHub Actions + +Just like regular workflows, campaigns get compiled: + +- You run `gh aw compile` +- The compiler produces a **[workflow lock file](/gh-aw/reference/glossary/#workflow-lock-file-lockyml)** (`.campaign.lock.yml`) that GitHub Actions runs + +This is the same idea as [compilation](/gh-aw/reference/glossary/#compilation) for normal workflows: you edit human-friendly markdown, GitHub Actions runs the compiled YAML. + +## Orchestrator + workers (who does what) + +Campaigns split responsibilities on purpose: + +- **Orchestrator (generated from the spec)** + - Runs on a schedule + - Coordinates and decides what to do next + - Uses **[safe outputs](/gh-aw/reference/glossary/#safe-outputs)** to dispatch worker workflows +- **Workers (you provide these)** + - Are normal agentic workflows triggered via [`workflow_dispatch`](/gh-aw/reference/glossary/#workflow_dispatch) + - Do the actual GitHub writes (issues/PRs/Projects/comments) using their own safe outputs + +## What you’ll see in GitHub + +When a campaign is working, you’ll usually see: + +- A compiled workflow named `.campaign.lock.yml` in `.github/workflows/` +- A GitHub Project filling up with issues/PRs created by workers +- Tracking labels on those issues/PRs like `z_campaign_` +- Optional [labels](/gh-aw/reference/glossary/#labels) you can use to group and filter related workflows +- Worker workflow runs that create PRs and move items through statuses + +If you want the full execution story, see: [Campaign lifecycle](/gh-aw/guides/campaigns/lifecycle/). ## Next steps -- [Creating campaigns](/gh-aw/guides/campaigns/creating-campaigns/) – issue-based or CLI creation - [Getting started](/gh-aw/guides/campaigns/getting-started/) – step-by-step tutorial +- [Creating campaigns](/gh-aw/guides/campaigns/creating-campaigns/) – issue-based or CLI creation - [Campaign specs](/gh-aw/guides/campaigns/specs/) – configuration reference - [Campaign Lifecycle](/gh-aw/guides/campaigns/lifecycle/) – execution model - [CLI commands](/gh-aw/guides/campaigns/cli-commands/) – command reference diff --git a/docs/src/content/docs/guides/campaigns/lifecycle.md b/docs/src/content/docs/guides/campaigns/lifecycle.md index f27ea78024..d489ea3ee9 100644 --- a/docs/src/content/docs/guides/campaigns/lifecycle.md +++ b/docs/src/content/docs/guides/campaigns/lifecycle.md @@ -5,26 +5,23 @@ banner: content: 'Do not use. Campaigns are still incomplete and may produce unreliable or unintended results.' --- -Campaign orchestrators execute on a schedule to coordinate worker workflows, discover outputs, and update project boards. This page explains the execution model, state management, and workflow coordination. +Campaign orchestrators execute on a schedule to coordinate worker workflows and discover outputs. Orchestrators are dispatch-only: they can coordinate, but all GitHub writes (Projects, issues/PRs, comments) happen in worker workflows. ## Execution flow ```mermaid graph TD - A[Orchestrator Triggered] --> B[Phase 0: Dispatch Workers] - B --> C[Phase 1: Discover Items] - C --> D[Phase 2: Plan Updates] - D --> E[Phase 3: Apply Updates] - E --> F[Phase 4: Status Report] + A[Orchestrator Triggered] --> B[Pre-step: Discovery Precomputation] + B --> C[Agent: Decide & Dispatch Workers] + C --> D[Workers: Apply Side Effects] + D --> E[Next Run: Discover Outputs] ``` Each run follows this sequence: -1. **Phase 0** - Dispatches worker workflows via `workflow_dispatch` (if configured) -2. **Phase 1** - Discovers issues and pull requests with campaign tracker labels -3. **Phase 2** - Plans project board updates within governance limits -4. **Phase 3** - Applies updates to project board -5. **Phase 4** - Posts status update to project with progress summary +1. **Pre-step** - A deterministic discovery script runs (via `actions/github-script`) and writes `./.gh-aw/campaign.discovery.json` +2. **Agent** - Reads the discovery manifest and campaign spec, then dispatches worker workflows via `safe-outputs.dispatch-workflow` +3. **Workers** - Create/update issues/PRs, apply labels, and update Project boards using their own safe-outputs ## Campaign states @@ -140,9 +137,9 @@ Completed 2025-03-15. Final metrics: - Confirm discovery scope includes correct repos/orgs **Project updates hit limit** -- Increase `max-project-updates-per-run` in governance +- Increase `max-project-updates-per-run` in governance (used as a pacing signal for generated instructions) - Accept incremental processing across multiple runs -- Verify project token has required permissions +- Verify the worker workflow token has required Projects permissions **Items processed multiple times** - Ensure workers use deterministic keys diff --git a/specs/campaigns-files.md b/specs/campaigns-files.md index 5b80f6e905..47c4e6bca0 100644 --- a/specs/campaigns-files.md +++ b/specs/campaigns-files.md @@ -71,7 +71,7 @@ state: active # Project integration project-url: https://github.com/orgs/ORG/projects/1 -tracker-label: campaign:security-q1-2025 +tracker-label: z_campaign_security-q1-2025 # Associated workflows workflows: @@ -136,13 +136,13 @@ steps: env: GH_AW_CAMPAIGN_ID: security-q1-2025 GH_AW_WORKFLOWS: "vulnerability-scanner,dependency-updater" - GH_AW_TRACKER_LABEL: campaign:security-q1-2025 + GH_AW_TRACKER_LABEL: z_campaign_security-q1-2025 GH_AW_PROJECT_URL: https://github.com/orgs/ORG/projects/1 GH_AW_MAX_DISCOVERY_ITEMS: 200 GH_AW_MAX_DISCOVERY_PAGES: 10 GH_AW_CURSOR_PATH: /tmp/gh-aw/repo-memory/campaigns/security-q1-2025/cursor.json with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN || secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); @@ -169,9 +169,6 @@ data := &workflow.WorkflowData{ ```go Tools: map[string]any{ - "github": map[string]any{ - "toolsets": []any{"default", "actions", "code_security"}, - }, "repo-memory": []any{ map[string]any{ "id": "campaigns", @@ -185,20 +182,24 @@ Tools: map[string]any{ } ``` +Note: orchestrators deliberately omit GitHub tool access. All writes and GitHub API operations should be performed by dispatched worker workflows. + #### D. Safe Outputs Configuration ```go -safeOutputs := &workflow.SafeOutputsConfig{ - CreateIssues: &workflow.CreateIssuesConfig{Max: 1}, - AddComments: &workflow.AddCommentsConfig{Max: maxComments}, - UpdateProjects: &workflow.UpdateProjectConfig{Max: maxProjectUpdates}, - CreateProjectStatusUpdates: &workflow.CreateProjectStatusUpdateConfig{Max: 1}, +safeOutputs := &workflow.SafeOutputsConfig{} + +// Campaign orchestrators are dispatch-only: they may only dispatch allowlisted +// workflows via the dispatch-workflow safe output. +if len(spec.Workflows) > 0 { + safeOutputs.DispatchWorkflow = &workflow.DispatchWorkflowConfig{ + BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: 3}, + Workflows: spec.Workflows, + } } ``` -Custom GitHub tokens for Projects v2 operations: -- If `spec.ProjectGitHubToken` is set, it's passed to `UpdateProjects` and `CreateProjectStatusUpdates` -- Allows using a different token with appropriate project permissions +Workers are responsible for side effects (Projects, issues/PRs, comments) using their own tool configuration and safe-outputs. #### E. Prompt Section @@ -279,30 +280,22 @@ The discovery script is copied to `/opt/gh-aw/actions/` during the `actions/setu } ``` -3. **Search for items by tracker-id**: - ```javascript - // For each workflow in spec.workflows: - const searchQuery = `"tracker-id: ${trackerId}" type:issue`; - const response = await octokit.rest.search.issuesAndPullRequests({ - q: searchQuery, - per_page: 100, - page: page, - sort: "updated", - order: "asc", // Stable ordering - }); - ``` +3. **Primary discovery: search by campaign-specific label**: + - Derived label: `z_campaign_` + - Query: `label:"z_campaign_"` scoped to repos/orgs -4. **Search for items by tracker label** (if configured): - ```javascript - const searchQuery = `label:"${label}"`; - const response = await octokit.rest.search.issuesAndPullRequests({ - q: searchQuery, - per_page: 100, - page: page, - sort: "updated", - order: "asc", - }); - ``` +4. **Secondary discovery: search by generic `agentic-campaign` label**: + - Query: `label:"agentic-campaign"` scoped to repos/orgs + +5. **Fallback discovery: search by tracker-id markers**: + ```javascript + // For each workflow in spec.workflows: + const searchQuery = `"gh-aw-tracker-id: ${trackerId}" type:issue`; + // (scoped with repo: and/or org: terms) + ``` + +6. **Legacy discovery: search by configured tracker label** (if provided): + - Query: `label:"${label}"` 5. **Normalize discovered items**: ```javascript @@ -437,44 +430,22 @@ memory/campaigns//cursor.json ### Campaign Item Protection -Campaign items are protected from being picked up by other workflows to prevent conflicts and ensure proper campaign orchestration. +The current campaign system’s primary tracking label format is: -**Protection Mechanism**: -- Items with `campaign:*` labels are automatically excluded from non-campaign workflows -- The `update-project` safe output automatically applies `campaign:` labels when adding items to campaign projects -- Workflows like `issue-monster` check for campaign labels and skip those issues -- Additional opt-out labels (`no-bot`, `no-campaign`) provide manual protection - -**Label Format**: ``` -campaign:my-campaign-id -campaign:security-q1-2025 -campaign:docs-quality-maintenance-project73 +z_campaign_ ``` -**How it's enforced**: +Workers should apply this label to all created issues/PRs so discovery can find them reliably. -1. **Automatic labeling**: When campaign orchestrators add items to projects, they apply the `campaign:` label -2. **Workflow filtering**: Other workflows (like issue-monster) filter out issues with `campaign:` prefix -3. **Opt-out labels**: Items with `no-bot` or `no-campaign` labels are also excluded +Some workflows in this repo also treat `campaign:*` labels as a “do not touch” signal (legacy convention). If you need that compatibility, have workers apply both labels: -**Example filtering logic** (from issue-monster workflow): -```javascript -// Exclude issues with campaign labels (campaign:*) -// Campaign items are managed by campaign orchestrators -if (issueLabels.some(label => label.startsWith('campaign:'))) { - core.info(`Skipping #${issue.number}: has campaign label (managed by campaign orchestrator)`); - return false; -} ``` - -**Governance Configuration**: -```yaml -governance: - opt-out-labels: ["no-campaign", "no-bot"] +z_campaign_ +campaign: ``` -This ensures that campaign items remain under the control of their respective campaign orchestrators and aren't interfered with by other automated workflows. +Campaign specs can also define `governance.opt-out-labels` (for example: `no-bot`, `no-campaign`) to let humans opt items out of automated handling. ## Campaign Workers @@ -603,8 +574,8 @@ Before creating a new branch: ```yaml # In worker prompt Use a deterministic PR title prefix: -- Format: `[campaign:${campaign_id}] ${work_item_description}` -- Example: `[campaign:security-q1-2025] Fix SQL injection in user.go` +- Format: `[campaign-${campaign_id}] ${work_item_description}` +- Example: `[campaign-security-q1-2025] Fix SQL injection in user.go` Before creating a PR: 1. Search for open PRs with this title prefix in the target repo @@ -622,7 +593,7 @@ Use a deterministic issue title with key: Before creating an issue: 1. Search for issues with `[${work_item_id}]` in title -2. Filter by label: `campaign:${campaign_id}` +2. Filter by label: `z_campaign_${campaign_id}` 3. If found: Update existing issue with new information 4. If not: Create new issue ``` @@ -760,7 +731,7 @@ gh extension install githubnext/gh-aw name: My Campaign version: v1 project-url: https://github.com/orgs/ORG/projects/1 - tracker-label: campaign:my-campaign + tracker-label: z_campaign_my-campaign workflows: - my-worker-workflow memory-paths: