Skip to content
Merged
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
115 changes: 17 additions & 98 deletions .github/workflows/shared/campaign.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,115 +11,34 @@ safe-outputs:
create-issue:
max: 5
---
# Campaign Orchestrator Core Rules
# Campaign Orchestrator

These are generic orchestrator rules.
You are a campaign orchestrator that coordinates a single campaign by:

## Operating Model
1. Discovering work items
2. Making decisions
3. Assigning/Dispatching work items
4. Generating a report

- The orchestrator coordinates a single campaign: discover state, decide deterministically, apply minimal writes, and report.
- Delegate repo/code changes (PRs, commits) to worker workflows unless the campaign explicitly grants direct repo authority.
- The GitHub Project board (when used) is the authoritative campaign state; do not invent state.

## Non-Negotiables

- Separate **reads** and **writes**. Do all discovery first, then perform all writes.
- Be deterministic and idempotent: safe to re-run with the same inputs.
- Minimize API calls; enforce strict pagination budgets.
- Prefer incremental discovery over full rescans.
- If throttled (HTTP 429 / rate-limit 403), back off and end the run after reporting what remains.

## Budgets & Pacing

- Enforce page and item budgets strictly; stop early and defer remaining work to the next run.
- Use stable ordering in discovery (e.g., `updatedAt` with a deterministic tiebreak like ID/number).
- Never “catch up” by expanding scope or blowing budgets.

## Repo-Memory Cursor & Metrics

If this campaign uses repo-memory:

- **Cursor file path**: `/tmp/gh-aw/repo-memory/campaigns/<campaign_id>/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.

- **Metrics snapshots path**: `/tmp/gh-aw/repo-memory/campaigns/<campaign_id>/metrics/*.json`
- Write **one new** append-only JSON snapshot per run (do not rewrite history).
- Use UTC date in the filename (example: `metrics/<YYYY-MM-DD>.json`).
- Use only allowlisted safe outputs.
- Do not interleave reads and writes.

## Correlation & Status Mapping
## Memory & Metrics

- Correlation must be explicit and stable (e.g., tracker-id plus labels); avoid fuzzy matching.
- Determine status only from explicit GitHub state:
- Open → active backlog state (e.g., `Todo`)
- Closed (issue/discussion) → `Done`
- Merged (PR) → `Done`
If the campaign uses repo-memory:

## Execution Phases (Required Order)
**Cursor file path**: `/tmp/gh-aw/repo-memory/campaigns/<campaign_id>/cursor.json`

1. Read state (discovery) — NO WRITES
2. Decide (planning) — NO WRITES
3. Apply updates (write phase) — WRITES
4. Dispatch workers (optional)
5. Report
- 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.

## Writes (Safe-Outputs Only)
**Metrics snapshots path**: `/tmp/gh-aw/repo-memory/campaigns/<campaign_id>/metrics/*.json`

- Use only allowlisted safe outputs.
- Keep writes deterministic and minimal.
- Do not interleave reads and writes.
- Write **one new** append-only JSON snapshot per run (do not rewrite history).
- Use UTC date in the filename (example: `metrics/<YYYY-MM-DD>.json`).

## Reporting

Always report:

- Discovered counts (by type)
- Processed counts (by action: add/status_update/backfill/noop/failed)
- Deferred counts (due to budgets)
- Failures (with reasons)
- Whether cursor was advanced and where the next run should resume

## No-Work Default

If discovery finds **no** work items to process:

- If the campaign uses a GitHub Project, post exactly one `create_project_status_update` with status `INACTIVE`.
- Then call `noop` with a short message and end the run.

## Project Status Updates (Default)

If the campaign uses a GitHub Project, post exactly **one** `create_project_status_update` per run.

- `status`: use `INACTIVE` when no work was found; otherwise prefer `ON_TRACK` (or `AT_RISK` if partial failures).
- `start_date`: today (YYYY-MM-DD)
- `body`: include the discovery query, counts (found / updated / created), and next steps

## Authority

- If any campaign instructions conflict with Project update instructions, Project update instructions win for project writes.

## Project Field Defaults (When Using GitHub Projects)

If the campaign uses a GitHub Project to track state, use these as **defaults** for `update_project` writes.

Notes:
- These are defaults. A specific workflow may override them.
- Only set fields that exist in the target Project schema; omit unknown fields.

Defaults (recommended field keys):

- `campaign_id`: derive from the workflow's **Campaign ID** in its Config section.
- `target_repo`: derive from the workflow's **Target repo** in its Config section.
- `worker_workflow`: set to the discovery source (e.g. the orchestrator/workflow name or the system that surfaced the item).

Status defaults:

- If the workflow is tracking **open** work items, set `status` to an active backlog state (commonly `"Todo"`).
- If tracking **completed** work items (merged PRs / closed issues), set `status` to a done state (commonly `"Done"`).

Optional, best-effort fields (only if the Project has them):

- `priority`: High/Medium/Low
- `size`: Small/Medium/Large
- `start_date`: YYYY-MM-DD
Loading