diff --git a/.github/maintainer/.version b/.github/maintainer/.version index 68d0e0a..d21d277 100644 --- a/.github/maintainer/.version +++ b/.github/maintainer/.version @@ -1 +1 @@ -0.19.6 +0.25.0 diff --git a/.github/workflows/maintainer.yml b/.github/workflows/maintainer.yml index b40dc8d..f43bc15 100644 --- a/.github/workflows/maintainer.yml +++ b/.github/workflows/maintainer.yml @@ -1,266 +1,83 @@ +# Caretaker — thin streaming workflow. +# +# This workflow is the bare minimum needed to invoke the backend. +# Webhooks installed via the caretaker GitHub App are the primary path +# (real-time, event-driven). This workflow exists for two narrower jobs: +# +# 1. Operator-triggered runs — kick a run from the GitHub UI via +# workflow_dispatch (the "Run workflow" button). +# 2. Webhook-miss recovery — sparse cron that reconciles state if a +# delivery dropped. The backend's reconciliation scheduler does +# most of the heavy lifting; this is a redundant safety net. +# +# No pip install of caretaker, no checkout, no LLM keys, no Copilot PAT — +# the backend holds everything. This file should change rarely. +# +# Required repo variable: +# CARETAKER_BACKEND_URL — e.g. https://caretaker.example.com +# +# Optional repo variable: +# CARETAKER_OIDC_AUDIENCE — defaults to "caretaker-backend" + name: Caretaker on: schedule: - - cron: "0 8 * * *" - pull_request: - types: [opened, synchronize, reopened] - pull_request_review: - types: [submitted] - check_suite: - types: [completed] - issues: - types: [opened, labeled] - issue_comment: - types: [created] + # Sparse cron — webhook-miss recovery only. The backend has its own + # reconciliation scheduler running on a 30-minute interval, so a + # 6-hour cadence here is plenty as a belt-and-suspenders fallback. + - cron: "37 */6 * * *" workflow_dispatch: inputs: mode: - description: "Run mode" + description: "Run mode the backend should execute" required: false default: "full" type: choice - options: [full, pr-only, issue-only, upgrade, dry-run] - -# Prevent concurrent caretaker runs so each run sees the up-to-date memory -# store written by the previous run. + options: + - full + - pr-only + - issue-only + - upgrade + - security + - deps + - stale + +# Serialise concurrent runs so an operator-triggered run does not stack +# on top of a sparse-cron run. Cancellation off — webhooks are the +# real-time path, this workflow is the safety net. concurrency: group: caretaker cancel-in-progress: false permissions: - contents: write - issues: write - pull-requests: write + # OIDC: enables the runner to mint a GitHub Actions JWT bound to the + # backend's audience, which the backend exchanges for a per-run + # ingest token. No GITHUB_TOKEN, no PAT, no LLM secrets. + id-token: write + contents: read jobs: - # Short-circuit comment events that caretaker itself produced. Without this - # filter, every status / readiness / task comment caretaker writes triggers - # another caretaker run via the issue_comment webhook, producing a feedback - # loop. Comments are identified by a caretaker:* HTML-comment marker and - # by known bot logins. - dispatch-guard: + stream: runs-on: ubuntu-latest - outputs: - should_run: ${{ steps.guard.outputs.should_run }} + timeout-minutes: 15 + env: + CARETAKER_BACKEND_URL: ${{ vars.CARETAKER_BACKEND_URL }} + CARETAKER_OIDC_AUDIENCE: ${{ vars.CARETAKER_OIDC_AUDIENCE }} steps: - - id: guard - uses: actions/github-script@v7 - with: - script: | - const ev = context.eventName; - if (ev !== "issue_comment" && ev !== "pull_request_review") { - core.setOutput("should_run", "true"); - return; - } - const payload = context.payload || {}; - if (ev === "issue_comment") { - const body = payload.comment?.body || ""; - if (/