Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/aw/close-agentic-campaign.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Execute all four steps in strict order:

1. Read State (no writes)
2. Make Decisions (no writes)
3. Write State (update-project only)
3. Dispatch Workers (dispatch-workflow only)
4. Report

The following rules are mandatory and override inferred behavior:

- The GitHub Project board is the single source of truth.
- All project writes MUST comply with `project_update_instructions.md`.
- All project writes MUST comply with the Project Update Instructions (in workers).
- State reads and state writes MUST NOT be interleaved.
- Do NOT infer missing data or invent values.
- Do NOT reorganize hierarchy.
Expand Down
212 changes: 25 additions & 187 deletions .github/aw/orchestrate-agentic-campaign.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Orchestrator Instructions

This orchestrator coordinates a single campaign by discovering worker outputs, making deterministic decisions,
and synchronizing campaign state into a GitHub Project board.
This orchestrator coordinates a single campaign by discovering worker outputs and making deterministic decisions.

**Scope:** orchestration only (discovery, planning, pacing, reporting).
**Write authority:** all project write semantics are governed by **Project Update Instructions** and MUST be followed.
**Scope:** orchestration only (discovery, planning, pacing, reporting).
**Actuation model:** **dispatch-only** — the orchestrator may only act by dispatching allowlisted worker workflows.
**Write authority:** all GitHub writes (Projects, issues/PRs, comments, status updates) must happen in worker workflows.

---

Expand All @@ -17,33 +17,18 @@ and synchronizing campaign state into a GitHub Project board.
- On throttling (HTTP 429 / rate-limit 403), do not retry aggressively; back off and end the run after reporting what remains.

{{ if .CursorGlob }}
**Cursor file (repo-memory)**: `{{ .CursorGlob }}`
**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/cursor.json`
- If it exists: read first and continue from its boundary.
- If it does not exist: create it by end of run.
**Cursor file (repo-memory)**: `{{ .CursorGlob }}`
**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/cursor.json`
- If it exists: read first and continue from its boundary.
- If it does not exist: create it by end of run.
- Always write the updated cursor back to the same path.
{{ end }}

{{ if .MetricsGlob }}
**Metrics snapshots (repo-memory)**: `{{ .MetricsGlob }}`
**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/metrics/*.json`
**Metrics snapshots (repo-memory)**: `{{ .MetricsGlob }}`
**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/metrics/*.json`
- Persist one append-only JSON metrics snapshot per run (new file per run; do not rewrite history).
- Use UTC date (`YYYY-MM-DD`) in the filename (example: `metrics/2025-12-22.json`).
- Each snapshot MUST include ALL required fields (even if zero):
- `campaign_id` (string): The campaign identifier
- `date` (string): UTC date in YYYY-MM-DD format
- `tasks_total` (number): Total number of tasks (>= 0, even if 0)
- `tasks_completed` (number): Completed task count (>= 0, even if 0)
- Optional fields (include only if available): `tasks_in_progress`, `tasks_blocked`, `velocity_per_day`, `estimated_completion`
- Example minimum valid snapshot:
```json
{
"campaign_id": "{{.CampaignID}}",
"date": "2025-12-22",
"tasks_total": 0,
"tasks_completed": 0
}
```
{{ end }}

{{ if gt .MaxDiscoveryItemsPerRun 0 }}
Expand All @@ -52,12 +37,6 @@ and synchronizing campaign state into a GitHub Project board.
{{ if gt .MaxDiscoveryPagesPerRun 0 }}
**Read budget**: max discovery pages per run: {{ .MaxDiscoveryPagesPerRun }}
{{ end }}
{{ if gt .MaxProjectUpdatesPerRun 0 }}
**Write budget**: max project updates per run: {{ .MaxProjectUpdatesPerRun }}
{{ end }}
{{ if gt .MaxProjectCommentsPerRun 0 }}
**Write budget**: max project comments per run: {{ .MaxProjectCommentsPerRun }}
{{ end }}

---

Expand All @@ -68,128 +47,26 @@ and synchronizing campaign state into a GitHub Project board.
3. Correlation is explicit (tracker-id AND labels)
4. Reads and writes are separate steps (never interleave)
5. Idempotent operation is mandatory (safe to re-run)
6. Only predefined project fields may be updated
7. **Project Update Instructions take precedence for all project writes**
8. **Campaign items MUST be labeled** for discovery and isolation

---

## Campaign Label Requirements

**All campaign-related issues, PRs, and discussions MUST have two labels:**

1. **`agentic-campaign`** - Generic label marking content as part of ANY campaign
- Prevents other workflows from processing campaign items
- Enables campaign-wide queries and filters

2. **`z_campaign_{{.CampaignID}}`** - Campaign-specific label
- Enables precise discovery of items belonging to THIS campaign
- Format: `z_campaign_<campaign-id>` (lowercase, hyphen-separated)
- Example: `z_campaign_security-q1-2025`

**Worker Responsibilities:**
- Workers creating issues/PRs as campaign output MUST add both labels
- Workers SHOULD use `create-issue` or `create-pr` safe outputs with labels configuration
- If workers cannot add labels automatically, campaign orchestrator will attempt to add them during discovery

**Non-Campaign Workflow Responsibilities:**
- Workflows triggered by issues/PRs SHOULD skip items with `agentic-campaign` label
- Use `skip-if-match` configuration to filter out campaign items:
```yaml
on:
issues:
types: [opened, labeled]
skip-if-match:
query: "label:agentic-campaign"
max: 0 # Skip if ANY campaign items match
```
6. Orchestrators do not write GitHub state directly

---

## Execution Steps (Required Order)

### Step 0 — Epic Issue Initialization [FIRST RUN ONLY]

**Campaign Epic Issue Requirements:**
- Each project board MUST have exactly ONE Epic issue representing the campaign
- The Epic serves as the parent for all campaign work issues
- The Epic is narrative-only and tracks overall campaign progress

**On every run, before other steps:**

1) **Check for existing Epic issue** by searching the repository for:
- An open issue with label `epic` or `type:epic`
- Body text containing: `campaign_id: {{.CampaignID}}`

2) **If no Epic issue exists**, create it using `create-issue`:
```yaml
create-issue:
title: "{{if .CampaignName}}{{.CampaignName}}{{else}}Campaign: {{.CampaignID}}{{end}}"
body: |
## Campaign Overview

This Epic issue tracks the overall progress of the campaign. All work items are sub-issues of this Epic.

**Campaign Details:**
- Campaign ID: `{{.CampaignID}}`
- Project Board: {{.ProjectURL}}
{{ if .Workflows }}- Worker Workflows: {{range $i, $w := .Workflows}}{{if $i}}, {{end}}`{{$w}}`{{end}}{{ end }}

---
`campaign_id: {{.CampaignID}}`
labels:
- agentic-campaign
- z_campaign_{{.CampaignID}}
- epic
- type:epic
```

3) **After creating the Epic** (or if Epic exists but not on board), add it to the project board:
```yaml
update-project:
project: "{{.ProjectURL}}"
campaign_id: "{{.CampaignID}}"
content_type: "issue"
content_number: <EPIC_ISSUE_NUMBER>
fields:
status: "In Progress"
campaign_id: "{{.CampaignID}}"
worker_workflow: "unknown"
repository: "<OWNER/REPO>"
priority: "High"
size: "Large"
start_date: "<EPIC_CREATED_DATE_YYYY-MM-DD>"
end_date: "<TODAY_YYYY-MM-DD>"
```

4) **Record the Epic issue number** in repo-memory for reference (e.g., in cursor file or metadata).

**Note:** This step typically runs only on the first orchestrator execution. On subsequent runs, verify the Epic exists and is on the board, but do not recreate it.

---

### Step 1 — Read State (Discovery) [NO WRITES]

**IMPORTANT**: Discovery has been precomputed. Read the discovery manifest instead of performing GitHub-wide searches.

1) Read the precomputed discovery manifest: `./.gh-aw/campaign.discovery.json`
- This manifest contains all discovered worker outputs with normalized metadata
- Schema version: v1
- Fields: campaign_id, generated_at, discovery (total_items, cursor info), summary (counts), items (array of normalized items)

2) Read current GitHub Project board state (items + required fields).

3) Parse discovered items from the manifest:
2) Parse discovered items from the manifest:
- Each item has: url, content_type (issue/pull_request/discussion), number, repo, created_at, updated_at, state
- Closed items have: closed_at (for issues) or merged_at (for PRs)
- Items are pre-sorted by updated_at for deterministic processing

4) Check the manifest summary for work counts:
- `needs_add_count`: Number of items that need to be added to the project
- `needs_update_count`: Number of items that need status updates
- If both are 0, you may skip to reporting step
3) Check the manifest summary for work counts.

5) Discovery cursor is maintained automatically in repo-memory; do not modify it manually.
4) Discovery cursor is maintained automatically in repo-memory; do not modify it manually.

### Step 2 — Make Decisions (Planning) [NO WRITES]

Expand All @@ -198,66 +75,27 @@ and synchronizing campaign state into a GitHub Project board.
- Closed (issue/discussion) → `Done`
- Merged (PR) → `Done`

**Why use explicit GitHub state?** - GitHub is the source of truth for work status. Inferring status from other signals (labels, comments) would be unreliable and could cause incorrect tracking.

6) Calculate required date fields for each item (per Project Update Instructions):
6) Calculate required date fields (for workers that sync Projects):
- `start_date`: format `created_at` as `YYYY-MM-DD`
- `end_date`:
- if closed/merged → format `closed_at`/`merged_at` as `YYYY-MM-DD`
- if open → **today's date** formatted `YYYY-MM-DD` (required for roadmap view)

**Why use today for open items?** - GitHub Projects requires end_date for roadmap views. Using today's date shows the item is actively tracked and updates automatically each run until completion.

7) Do NOT implement idempotency by comparing against the board. You may compare for reporting only.

**Why no comparison for idempotency?** - The safe-output system handles deduplication. Comparing would add complexity and potential race conditions. Trust the infrastructure.

8) Apply write budget:
- If `MaxProjectUpdatesPerRun > 0`, select at most that many items this run using deterministic order
(e.g., oldest `updated_at` first; tie-break by ID/number).
- Defer remaining items to next run via cursor.

**Why use deterministic order?** - Ensures predictable behavior and prevents starvation. Oldest items are processed first, ensuring fair treatment of all work items. The cursor saves progress for next run.

### Step 3 — Write State (Execution) [WRITES ONLY]

9) For each selected item, send an `update-project` request.
- Do NOT interleave reads.
- Do NOT pre-check whether the item is on the board.
- **All write semantics MUST follow Project Update Instructions**, including:
- first add → full required fields (status, campaign_id, worker_workflow, repo, priority, size, start_date, end_date)
- existing item → status-only update unless explicit backfill is required

10) Record per-item outcome: success/failure + error details.
- if open → **today's date** formatted `YYYY-MM-DD`

### Step 4 — Report & Status Update
7) Reads and writes are separate steps (never interleave).

11) **REQUIRED: Create a project status update summarizing this run**
### Step 3 — Dispatch Workers (Execution) [DISPATCH ONLY]

Every campaign run MUST create a status update using `create-project-status-update` safe output. This is the primary communication mechanism for conveying campaign progress to stakeholders.
8) For each selected unit of work, dispatch a worker workflow using `dispatch-workflow`.

**Required Sections:**
Constraints:
- Only dispatch allowlisted workflows.
- Keep within the dispatch-workflow max for this run.

- **Most Important Findings**: Highlight the 2-3 most critical discoveries, insights, or blockers from this run
- **What Was Learned**: Document key learnings, patterns observed, or insights gained during this run
- **Campaign Progress**: Report on campaign metrics and trends with baseline → current → target format, including direction and velocity
- **Campaign Summary**: Tasks completed, in progress, blocked, and overall completion percentage
- **Next Steps**: Clear action items and priorities for the next run
### Step 4 — Report (No Writes)

**Configuration:**
- Set appropriate status: ON_TRACK, AT_RISK, OFF_TRACK, or COMPLETE
- Use today's date for start_date and target_date (or appropriate future date for target)
- Body must be comprehensive yet concise (target: 200-400 words)
9) Summarize what you dispatched, what remains, and what should run next.

Example status update:
```yaml
create-project-status-update:
project: "{{.ProjectURL}}"
status: "ON_TRACK"
start_date: "2026-01-06"
target_date: "2026-01-31"
body: |
## Campaign Run Summary
If a status update is required on the GitHub Project, dispatch a dedicated reporting/sync worker to perform that write.

**Discovered:** 25 items (15 issues, 10 PRs)
**Processed:** 10 items added to project, 5 updated
Expand Down
3 changes: 2 additions & 1 deletion .github/aw/update-agentic-campaign-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ If any other instructions conflict with this file, THIS FILE TAKES PRECEDENCE fo

## 0) Hard Requirements (Do Not Deviate)

- Writes MUST use only the `update-project` safe-output.
- Orchestrators are dispatch-only and MUST NOT perform project writes directly.
- Worker workflows performing project writes MUST use only the `update-project` safe-output.
- All writes MUST target exactly:
- **Project URL**: `{{.ProjectURL}}`
- Every item MUST include:
Expand Down
Loading