From c3f6862ead805b3f5fdb23b8ff183cf9f1e08602 Mon Sep 17 00:00:00 2001 From: Mara Nikola Kiefer Date: Thu, 29 Jan 2026 22:27:31 +0100 Subject: [PATCH] chore: add scheduling and skip-if-no-match conditions --- .github/workflows/dependabot-burner.lock.yml | 168 ++++++++++++++++++- .github/workflows/dependabot-burner.md | 9 +- 2 files changed, 164 insertions(+), 13 deletions(-) diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml index bbc146abf4..2925287d8f 100644 --- a/.github/workflows/dependabot-burner.lock.yml +++ b/.github/workflows/dependabot-burner.lock.yml @@ -27,6 +27,10 @@ name: "Dependabot Burner" "on": + schedule: + - cron: "32 23 * * *" + # Friendly format: daily (scattered) + # skip-if-no-match: is:pr is:open author:app/dependabot label:dependencies label:javascript # Skip-if-no-match processed as search check in pre-activation job workflow_dispatch: permissions: {} @@ -38,6 +42,8 @@ run-name: "Dependabot Burner" jobs: activation: + needs: pre_activation + if: (needs.pre_activation.result == 'skipped') || (needs.pre_activation.outputs.activated == 'true') runs-on: ubuntu-slim permissions: contents: read @@ -796,13 +802,120 @@ jobs: PROMPT_EOF cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + # Campaign Orchestrator Core Rules + These are generic orchestrator rules intended to be included via `{{#runtime-import ...}}`. - # Dependabot Burner + ## Operating Model + + - 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//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//metrics/*.json` + - Write **one new** append-only JSON snapshot per run (do not rewrite history). + - Use UTC date in the filename (example: `metrics/.json`). + + ## Correlation & Status Mapping + + - 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` + + ## Execution Phases (Required Order) + + 1. Read state (discovery) — NO WRITES + 2. Decide (planning) — NO WRITES + 3. Apply updates (write phase) — WRITES + 4. Dispatch workers (optional) + 5. Report + + ## Writes (Safe-Outputs Only) + + - Use only allowlisted safe outputs. + - Keep writes deterministic and minimal. + - Do not interleave reads and writes. + + ## 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"`). - {{#runtime-import aw/campaign.md}} + Optional, best-effort fields (only if the Project has them): - ## Config + - `priority`: High/Medium/Low + - `size`: Small/Medium/Large + - `start_date`: YYYY-MM-DD + + # Dependabot Burner - Project URL: https://github.com/orgs/githubnext/projects/144 - Campaign ID: dependabot-burner @@ -1317,6 +1430,49 @@ jobs: path: /tmp/gh-aw/threat-detection/detection.log if-no-files-found: ignore + pre_activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + activated: ${{ (steps.check_membership.outputs.is_team_member == 'true') && (steps.check_skip_if_no_match.outputs.skip_no_match_check_ok == 'true') }} + steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: /opt/gh-aw/actions + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + - name: Check skip-if-no-match query + id: check_skip_if_no_match + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_SKIP_QUERY: "is:pr is:open author:app/dependabot label:dependencies label:javascript" + GH_AW_WORKFLOW_NAME: "Dependabot Burner" + GH_AW_SKIP_MIN_MATCHES: "1" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_skip_if_no_match.cjs'); + await main(); + safe_outputs: needs: - agent @@ -1363,11 +1519,11 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_PROJECT_HANDLER_CONFIG: "{\"create_project_status_update\":{\"github-token\":\"${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\",\"max\":1},\"update_project\":{\"github-token\":\"${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\",\"max\":100}}" + GH_AW_SAFE_OUTPUTS_PROJECT_HANDLER_CONFIG: "{\"create_project_status_update\":{\"max\":1},\"update_project\":{\"max\":100}}" GH_AW_ASSIGN_COPILOT: "true" - GH_AW_PROJECT_GITHUB_TOKEN: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GH_AW_PROJECT_GITHUB_TOKEN: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} with: - github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io); diff --git a/.github/workflows/dependabot-burner.md b/.github/workflows/dependabot-burner.md index 421da24c52..d5a1037f0a 100644 --- a/.github/workflows/dependabot-burner.md +++ b/.github/workflows/dependabot-burner.md @@ -3,9 +3,8 @@ name: Dependabot Burner description: Burns down Dependabot security alert work items on: - #schedule: daily - #skip-if-not-match: prnwith dependabot label - workflow_dispatch: + schedule: daily + skip-if-no-match: 'is:pr is:open author:app/dependabot' permissions: issues: read @@ -19,10 +18,6 @@ imports: # Dependabot Burner -{{#runtime-import aw/campaign.md}} - -## Config - - Project URL: https://github.com/orgs/githubnext/projects/144 - Campaign ID: dependabot-burner - Target repo: githubnext/gh-aw