diff --git a/.github/scripts/__tests__/sync_dependabot_campaign.test.js b/.github/scripts/__tests__/sync_dependency_campaign.test.js similarity index 99% rename from .github/scripts/__tests__/sync_dependabot_campaign.test.js rename to .github/scripts/__tests__/sync_dependency_campaign.test.js index 891e3309f..033fbb5fc 100644 --- a/.github/scripts/__tests__/sync_dependabot_campaign.test.js +++ b/.github/scripts/__tests__/sync_dependency_campaign.test.js @@ -20,7 +20,7 @@ const { replaceCampaignMarker, verboseDryRunLoggingEnabled, validateCampaignState, -} = require('../sync_dependabot_campaign.js'); +} = require('../sync_dependency_campaign.js'); test('formats and parses campaign marker', () => { const state = { diff --git a/.github/scripts/sync_dependabot_campaign.js b/.github/scripts/sync_dependency_campaign.js similarity index 99% rename from .github/scripts/sync_dependabot_campaign.js rename to .github/scripts/sync_dependency_campaign.js index 3d9a18c6d..f29094cd8 100644 --- a/.github/scripts/sync_dependabot_campaign.js +++ b/.github/scripts/sync_dependency_campaign.js @@ -932,7 +932,7 @@ function formatCampaignBody(state) { const lines = [ '# Sync/Dependabot Campaign Queue', '', - '> **Durable tracker** — see [`docs/ops/DURABLE_TRACKING_ISSUES.md`](https://github.com/stranske/Workflows/blob/main/docs/ops/DURABLE_TRACKING_ISSUES.md). The body below is regenerated each cycle by `maint-82-sync-dependabot-campaign.yml`; do not close as part of routine triage.', + '> **Durable tracker** — see [`docs/ops/DURABLE_TRACKING_ISSUES.md`](https://github.com/stranske/Workflows/blob/main/docs/ops/DURABLE_TRACKING_ISSUES.md). The body below is regenerated each cycle by `maint-82-sync-dependency-campaign.yml`; do not close as part of routine triage.', '', 'Remote GitHub Actions owns discovery for sync-generated and Dependabot PR rounds. Local Codex should only claim items from this issue when `needs-local-codex` work is queued.', '', diff --git a/.github/sync-manifest.yml b/.github/sync-manifest.yml index af8f8dcae..5c21b7855 100644 --- a/.github/sync-manifest.yml +++ b/.github/sync-manifest.yml @@ -130,10 +130,6 @@ workflows: description: "Renovate config - extends stranske/Workflows//renovate-presets/fleet (shared fleet preset)" sync_mode: create_only - - source: .github/workflows/dependabot-automerge.yml - description: "Dependabot auto-merge - automatically merges dependabot PRs when checks pass" - - # Reusable PR context workflow (medium-effort rate limit optimization) - source: .github/workflows/reusable-pr-context.yml description: "Reusable PR context workflow - centralized PR data fetching via GraphQL" @@ -148,6 +144,8 @@ workflows: # Files to remove from consumer repos (obsolete / harmful when present) removals: + - target: .github/workflows/dependabot-automerge.yml + description: "Remove the retired Dependabot auto-merge workflow - the fleet is on Renovate (platformAutomerge covers auto-merge); see renovate-presets/fleet.json." - target: .github/workflows/agents-63-issue-intake.yml description: "Remove deprecated issue intake workflow (replaced by agents-64/70)" - target: .workflows-lib diff --git a/.github/workflows/maint-82-sync-dependabot-campaign.yml b/.github/workflows/maint-82-sync-dependency-campaign.yml similarity index 93% rename from .github/workflows/maint-82-sync-dependabot-campaign.yml rename to .github/workflows/maint-82-sync-dependency-campaign.yml index 6436a86ba..d93c61942 100644 --- a/.github/workflows/maint-82-sync-dependabot-campaign.yml +++ b/.github/workflows/maint-82-sync-dependency-campaign.yml @@ -1,4 +1,8 @@ -name: Sync/Dependabot Campaign +name: Sync/Dependency Campaign +# Watches dependency-bot PRs (renovate[bot]; legacy dependabot[bot]) + consumer +# sync drift. Internal identifiers (repository_dispatch type, concurrency group, +# and artifact/state-file names below) intentionally keep the historical +# "sync-dependabot-campaign" slug for dispatcher + persisted-state continuity. on: schedule: @@ -95,7 +99,7 @@ jobs: const { formatCampaignRunSummaryMarkdown, runCampaign, - } = require('./.github/scripts/sync_dependabot_campaign.js'); + } = require('./.github/scripts/sync_dependency_campaign.js'); const fs = require('fs'); const registeredRepos = '${{ steps.registered.outputs.repos }}' @@ -136,7 +140,7 @@ jobs: .join(', ') || '-'; const unpublishedResults = stats.items_unpublished_source_results || 0; await core.summary - .addHeading('Sync/Dependabot Campaign') + .addHeading('Sync/Dependency Campaign') .addList([ `Current sync hash: ${result.state.current_sync_hash || '-'}`, `Repos checked: ${stats.repos_checked || 0}/${stats.repos_requested || 0}`, diff --git a/.github/workflows/maint-dependabot-auto-lock.yml b/.github/workflows/maint-auto-lock-deps.yml similarity index 99% rename from .github/workflows/maint-dependabot-auto-lock.yml rename to .github/workflows/maint-auto-lock-deps.yml index 86146f6ba..95d5d6ee2 100644 --- a/.github/workflows/maint-dependabot-auto-lock.yml +++ b/.github/workflows/maint-auto-lock-deps.yml @@ -1,4 +1,4 @@ -name: Dependabot Auto-Lock +name: Auto-lock dependency PRs # Backstop that regenerates requirements.lock when a dependency bot updates a # declared lock input file, so the checked-in lock stays in sync. # diff --git a/.github/workflows/maint-dependabot-weekly-sweep.yml b/.github/workflows/maint-dependabot-weekly-sweep.yml deleted file mode 100644 index 060efb83e..000000000 --- a/.github/workflows/maint-dependabot-weekly-sweep.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Dependabot Weekly Sweep (Consumers) - -on: - schedule: - # Mondays after Dependabot weekly updates (UTC) - - cron: "0 9 * * 1" - workflow_dispatch: {} - -permissions: - contents: write - pull-requests: write - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - sweep: - name: Enable Dependabot auto-merge - runs-on: ubuntu-latest - env: - REPO_TOKEN: ${{ secrets.OWNER_PR_PAT || secrets.SERVICE_BOT_PAT }} - steps: - - name: Checkout Workflows repo - uses: actions/checkout@v6 - - - name: Load registered consumer repos - run: | - repos=$(python scripts/list_registered_consumer_repos.py) - { - echo "REGISTERED_CONSUMER_REPOS<> "$GITHUB_ENV" - - - name: Validate token - run: | - if [ -z "${REPO_TOKEN:-}" ]; then - echo "::error::OWNER_PR_PAT or SERVICE_BOT_PAT is required for cross-repo merges." - exit 1 - fi - - - name: Sweep consumer repos - env: - GH_TOKEN: ${{ env.REPO_TOKEN }} - run: | - set -euo pipefail - - repos=$(echo "$REGISTERED_CONSUMER_REPOS" | sed '/^$/d') - - while read -r repo; do - echo "=== ${repo} ===" - prs=$(gh pr list \ - --repo "$repo" \ - --author app/dependabot \ - --state open \ - --json number \ - --jq '.[].number' \ - || true) - - if [ -z "$prs" ]; then - echo "No Dependabot PRs." - continue - fi - - for pr in $prs; do - echo "Processing ${repo}#${pr}" - - auto_out=$(gh pr merge "$pr" --repo "$repo" --auto --merge --delete-branch 2>&1) || true - echo "$auto_out" - - if echo "$auto_out" | grep -qi "Protected branch rules not configured"; then - checks_json=$(gh pr view "$pr" \ - --repo "$repo" \ - --json statusCheckRollup,mergeable,mergeStateStatus) - mergeable=$(echo "$checks_json" | jq -r '.mergeable') - merge_state=$(echo "$checks_json" | jq -r '.mergeStateStatus') - - if [ "$mergeable" != "MERGEABLE" ]; then - echo "PR not mergeable; skipping." - continue - fi - if [ "$merge_state" = "BLOCKED" ]; then - echo "PR merge state blocked (reviews or policy required); skipping." - continue - fi - - failed=$(echo "$checks_json" | jq -r ' - [.statusCheckRollup[] - | select( - .status=="COMPLETED" and - ( - .conclusion != "SUCCESS" and - .conclusion != "SKIPPED" and - .conclusion != "NEUTRAL" - ) - ) - ] | length - ') - pending=$(echo "$checks_json" | jq -r ' - [.statusCheckRollup[] | select(.status != "COMPLETED")] | length - ') - - if [ "$failed" -eq 0 ] && [ "$pending" -eq 0 ]; then - if ! gh pr merge "$pr" --repo "$repo" --merge --delete-branch; then - echo "Merge failed; continuing to next PR." - continue - fi - else - echo "Checks pending or failing; leaving open." - fi - fi - done - done <<< "$repos" diff --git a/docs/ci/WORKFLOWS.md b/docs/ci/WORKFLOWS.md index 92f50fedd..ecd2ee2ab 100644 --- a/docs/ci/WORKFLOWS.md +++ b/docs/ci/WORKFLOWS.md @@ -160,8 +160,7 @@ Consumer default note: `agents-pr-meta-v4.yml` is a Workflows-repo service workf * [`agents-verify-to-issue-v2.yml`](../../.github/workflows/agents-verify-to-issue-v2.yml) creates follow-up issues from verification feedback when PRs receive CONCERNS or FAIL verdicts using the enhanced LangChain analyzer. The legacy v1 issue workflow has been removed. * [`agents-verify-to-new-pr.yml`](../../.github/workflows/agents-verify-to-new-pr.yml) creates a follow-up issue from verification feedback, enforces the follow-up chain-depth limit, emits verifier follow-up ledger records, and kicks off a new PR when policy allows it. * [`maint-auto-label-dep-prs.yml`](../../.github/workflows/maint-auto-label-dep-prs.yml) automatically applies the `agents:allow-change` label to dependency-bot PRs (Dependabot + Renovate). -* [`maint-dependabot-auto-lock.yml`](../../.github/workflows/maint-dependabot-auto-lock.yml) automatically regenerates requirements.lock when dependabot updates pyproject.toml. -* [`maint-dependabot-weekly-sweep.yml`](../../.github/workflows/maint-dependabot-weekly-sweep.yml) sweeps registered consumer repos weekly to enable Dependabot auto-merge and merge eligible PRs when checks are green, requesting branch deletion for merged Dependabot branches. +* [`maint-auto-lock-deps.yml`](../../.github/workflows/maint-auto-lock-deps.yml) regenerates requirements.lock (re-running the command recorded in the lock header) when a dependency bot updates an input — a Workflows-local backstop to the fleet Renovate pip-compile manager. * [`agents-verifier.yml`](../../.github/workflows/agents-verifier.yml) runs when `verify:*` labels are applied to a pull request (or via manual dispatch) to assemble acceptance/task context, execute LLM-based verifier modes, and post a verdict. `checkbox` mode uses the Codex CLI to drive checklist-style verification, while `evaluate` and `compare` run non-Codex verifier flows. In `compare` mode, two LLM providers (gpt-5.4 + claude-sonnet-4-6) evaluate independently with unanimous-PASS consensus. On CONCERNS or FAIL, maintainers (or follow-up automation) can apply the `verify:create-new-pr` label to trigger `agents-verify-to-new-pr.yml`, which uses a 4-round LLM pipeline to generate a follow-up issue when chain-depth policy allows it. Follow-up chain depth must not exceed 2; the workflow records policy/disposition metadata and applies `needs-human` at the limit. See [`docs/analysis/verify-compare-40pr-evaluation-feb-2026.md`](../analysis/verify-compare-40pr-evaluation-feb-2026.md) for the Feb 2026 evaluation baseline. * [`agents-weekly-metrics.yml`](../../.github/workflows/agents-weekly-metrics.yml) aggregates agent metrics (keepalive, autofix, verifier) on a weekly schedule and generates a markdown summary. * [`agents-debug-issue-event.yml`](../../.github/workflows/agents-debug-issue-event.yml) dumps the GitHub event context for debugging issue triggers. @@ -204,7 +203,7 @@ Scheduled health jobs keep the automation ecosystem aligned: * [`maint-74-ledger-base-sync.yml`](../../.github/workflows/maint-74-ledger-base-sync.yml) aligns `.agents` ledger base entries to the repository default branch on a weekly schedule or manual dispatch. * [`maint-80-langsmith-metrics-dashboard.yml`](../../.github/workflows/maint-80-langsmith-metrics-dashboard.yml) generates weekly LangSmith trace coverage dashboard - downloads metrics from autopilot artifacts, computes coverage, creates issue report (scheduled Monday 9AM UTC, manual dispatch). * [`maint-81-langsmith-fleet-conformance.yml`](../../.github/workflows/maint-81-langsmith-fleet-conformance.yml) validates fleet artifact coverage against `config/langsmith_fleet_registry.json` and reports missing/stale/invalid records (scheduled Monday 9:30AM UTC, manual dispatch with optional enforcement). -* [`maint-82-sync-dependabot-campaign.yml`](../../.github/workflows/maint-82-sync-dependabot-campaign.yml) refreshes a GitHub-visible sync/Dependabot campaign issue so local Codex only claims queued bot-review work when remote discovery finds active review threads. +* [`maint-82-sync-dependency-campaign.yml`](../../.github/workflows/maint-82-sync-dependency-campaign.yml) refreshes a GitHub-visible sync/dependency campaign issue so local Codex only claims queued bot-review work when remote discovery finds active review threads. * [`maint-83-bootstrap-consumer.yml`](../../.github/workflows/maint-83-bootstrap-consumer.yml) applies the manual GitHub-settings bootstrap toggles a freshly-registered consumer needs (SETUP_CHECKLIST §3.1/§3.3/§3.3.1: `default_workflow_permissions=write`, `USE_CONSOLIDATED_WORKFLOWS` + `ALLOWED_KEEPALIVE_LOGINS` variables, and the `stranske-automation-bot` push-collaborator invite) via `scripts/bootstrap_consumer_settings.py` (manual dispatch, dry-run by default). * [`maint-84-prune-agent-stubs.yml`](../../.github/workflows/maint-84-prune-agent-stubs.yml) garbage-collects `agents/-.md` bootstrap stubs whose issue `#N` is closed, preventing unbounded accumulation; runs weekly (Monday 08:00 UTC) via `scripts/prune_agent_stubs.py`. diff --git a/docs/ci/WORKFLOW_SYSTEM.md b/docs/ci/WORKFLOW_SYSTEM.md index c4e095d77..bb50cf90e 100644 --- a/docs/ci/WORKFLOW_SYSTEM.md +++ b/docs/ci/WORKFLOW_SYSTEM.md @@ -703,8 +703,7 @@ Keep this table handy when you are triaging automation: it confirms which workfl | **Maint 50 Tool Version Check** (`maint-50-tool-version-check.yml`, maintenance bucket) | `schedule` (Mondays 8:00 AM UTC), `workflow_dispatch` | Check PyPI for new versions of CI/autofix tools and create/update an issue when updates are available. | ⚪ Scheduled | [Maint 50 version checks](https://github.com/stranske/Workflows/actions/workflows/maint-50-tool-version-check.yml) | | **Maint 51 Dependency Refresh** (`maint-51-dependency-refresh.yml`, maintenance bucket) | `schedule` (1st & 15th at 04:00 UTC), `workflow_dispatch` | Regenerate `requirements.lock` with `uv pip compile`, verify tool-pin alignment, and open a refresh PR when dependency updates are detected (supports dry-run previews). | ⚪ Scheduled | [Maint 51 dependency refresh](https://github.com/stranske/Workflows/actions/workflows/maint-51-dependency-refresh.yml) | | **Auto-label dependency PRs** (`maint-auto-label-dep-prs.yml`, maintenance bucket) | `pull_request_target` (`opened`) | Apply the `agents:allow-change` label to dependency-bot PRs (Dependabot + Renovate) so protected-workflow changes can be reviewed without manual label work. | ⚪ Automatic on PR open | [Auto-label runs](https://github.com/stranske/Workflows/actions/workflows/maint-auto-label-dep-prs.yml) | -| **Dependabot Auto-Lock** (`maint-dependabot-auto-lock.yml`, maintenance bucket) | `pull_request` (Dependabot branches, `pyproject.toml` changes) | Regenerate `requirements.lock` when Dependabot updates `pyproject.toml`, commit the updated lockfile back to the Dependabot PR branch, and keep dependency pins in sync. | ⚪ Automatic on Dependabot PRs | [Dependabot auto-lock runs](https://github.com/stranske/Workflows/actions/workflows/maint-dependabot-auto-lock.yml) | -| **Dependabot Weekly Sweep (Consumers)** (`maint-dependabot-weekly-sweep.yml`, maintenance bucket) | `schedule` (Mondays 09:00 UTC), `workflow_dispatch` | Sweep registered consumer repos weekly to enable Dependabot auto-merge and merge eligible PRs when checks are green. | ⚪ Scheduled/manual | [Dependabot weekly sweep runs](https://github.com/stranske/Workflows/actions/workflows/maint-dependabot-weekly-sweep.yml) | +| **Auto-lock dependency PRs** (`maint-auto-lock-deps.yml`, maintenance bucket) | `pull_request` (dependency-bot branches `dependabot/**` + `renovate/**`, lock-input changes) | Regenerate `requirements.lock` by re-running the command recorded in the lock header when a dependency bot updates an input, committing the refreshed lock back to the PR branch. Workflows-local backstop to the fleet Renovate pip-compile manager. | ⚪ Automatic on dependency-bot PRs | [Auto-lock runs](https://github.com/stranske/Workflows/actions/workflows/maint-auto-lock-deps.yml) | | **Maint Sync pyproject.toml from versions.env** (`maint-sync-env-from-pyproject.yml`, maintenance bucket) | `push` (`main`, autofix pin/template/lock paths), `workflow_dispatch` | Sync `pyproject.toml`, templates, and direct `requirements.lock` pins from canonical `autofix-versions.env` after changes land. | ⚪ Automatic on main | [Maint sync env runs](https://github.com/stranske/Workflows/actions/workflows/maint-sync-env-from-pyproject.yml) | | **Maint 52 Validate Workflows** (`maint-52-validate-workflows.yml`, maintenance bucket) | `pull_request`, `push` (`main`) | Parse every workflow file with `yq`, honour the Actionlint allowlist, and fail fast when syntax errors or lint violations appear. | ⚪ Automatic on PR/main | [Maint 52 workflow validations](https://github.com/stranske/Workflows/actions/workflows/maint-52-validate-workflows.yml) | | **Maint 52 Sync Dev Versions** (`maint-52-sync-dev-versions.yml`, maintenance bucket) | `schedule` (Sundays 01:00 UTC), `push` (`autofix-versions.env`), `workflow_dispatch` | Sync dev tool versions from `autofix-versions.env` to consumer repository `pyproject.toml` files. | ⚪ Scheduled/manual | [Sync dev versions runs](https://github.com/stranske/Workflows/actions/workflows/maint-52-sync-dev-versions.yml) | @@ -743,7 +742,7 @@ Keep this table handy when you are triaging automation: it confirms which workfl | **Maint 74 Ledger Base Sync** (`maint-74-ledger-base-sync.yml`, maintenance bucket) | `workflow_dispatch`, `schedule` (Mondays 06:00 UTC) | Align `.agents` ledger base entries to the repository default branch. | ⚪ Manual/scheduled | [Ledger base sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-74-ledger-base-sync.yml) | | **LangSmith Metrics Dashboard** (`maint-80-langsmith-metrics-dashboard.yml`, maintenance bucket) | `workflow_dispatch`, `schedule` (Mondays 09:00 UTC) | Generates weekly LangSmith trace coverage dashboard by downloading metrics from autopilot artifacts, computing coverage, and creating issue reports. | ⚪ Manual/scheduled | [LangSmith metrics runs](https://github.com/stranske/Workflows/actions/workflows/maint-80-langsmith-metrics-dashboard.yml) | | **LangSmith Fleet Conformance** (`maint-81-langsmith-fleet-conformance.yml`, maintenance bucket) | `workflow_dispatch`, `schedule` (Mondays 09:30 UTC) | Validates LangSmith fleet artifact coverage against `config/langsmith_fleet_registry.json`, emits markdown/JSON reports, and can optionally enforce non-valid rows. | ⚪ Manual/scheduled | [LangSmith fleet conformance runs](https://github.com/stranske/Workflows/actions/workflows/maint-81-langsmith-fleet-conformance.yml) | -| **Sync/Dependabot Campaign** (`maint-82-sync-dependabot-campaign.yml`, maintenance bucket) | `schedule`, `workflow_dispatch`, `repository_dispatch` | Refreshes a GitHub-visible campaign issue for sync-generated and Dependabot PRs with active bot review threads so local Codex only claims queued work when remote discovery finds it. | ⚪ Scheduled/manual | [Sync/Dependabot campaign runs](https://github.com/stranske/Workflows/actions/workflows/maint-82-sync-dependabot-campaign.yml) | +| **Sync/Dependency Campaign** (`maint-82-sync-dependency-campaign.yml`, maintenance bucket) | `schedule`, `workflow_dispatch`, `repository_dispatch` | Refreshes a GitHub-visible campaign issue for sync-generated and dependency-bot PRs with active bot review threads so local Codex only claims queued work when remote discovery finds it. | ⚪ Scheduled/manual | [Sync/Dependency campaign runs](https://github.com/stranske/Workflows/actions/workflows/maint-82-sync-dependency-campaign.yml) | | **Maint 83 Bootstrap Consumer** (`maint-83-bootstrap-consumer.yml`, maintenance bucket) | `workflow_dispatch` | Applies the manual GitHub-settings bootstrap toggles a freshly-registered consumer needs (SETUP_CHECKLIST §3.1/§3.3/§3.3.1: `default_workflow_permissions=write`, `USE_CONSOLIDATED_WORKFLOWS` + `ALLOWED_KEEPALIVE_LOGINS` variables, `stranske-automation-bot` push-collaborator invite) via `scripts/bootstrap_consumer_settings.py`; dry-run by default. | ⚪ Manual | [Bootstrap consumer runs](https://github.com/stranske/Workflows/actions/workflows/maint-83-bootstrap-consumer.yml) | | **Maint 84 Prune Agent Stubs** (`maint-84-prune-agent-stubs.yml`, maintenance bucket) | `schedule` (Mondays 08:00 UTC), `workflow_dispatch` | Garbage-collects `agents/-.md` bootstrap stubs whose issue `#N` is closed via `scripts/prune_agent_stubs.py`, preventing unbounded accumulation; dry-run by default on manual dispatch. | ⚪ Scheduled/manual | [Prune agent stubs runs](https://github.com/stranske/Workflows/actions/workflows/maint-84-prune-agent-stubs.yml) | | **Maint 60 Release** (`maint-60-release.yml`, maintenance bucket) | `push` (tags `v*`) | Create GitHub releases automatically when version tags are pushed. | ⚪ Tag-triggered | [Release workflow runs](https://github.com/stranske/Workflows/actions/workflows/maint-60-release.yml) | @@ -827,7 +826,7 @@ snapshots for audit trails. `maint-51-dependency-refresh.yml`, the Gate summary job (inline), `maint-coverage-guard.yml`, health 40/41/42/43/44, agents 70/63, `agents-moderate-connector.yml`, `agents-debug-issue-event.yml`, `agents-guard.yml`, reusable 10/12/16/18, and - `maint-auto-label-dep-prs.yml`, `maint-dependabot-auto-lock.yml`, and `selftest-reusable-ci.yml`. + `maint-auto-label-dep-prs.yml`, `maint-auto-lock-deps.yml`, and `selftest-reusable-ci.yml`. - **Retire.** `pr-14-docs-only.yml`, `maint-47-check-failure-tracker.yml`, the removed Agents 61/62 consumer workflows, and the legacy `selftest-*` wrappers superseded by `selftest-reusable-ci.yml`. diff --git a/docs/ops/DURABLE_TRACKING_ISSUES.md b/docs/ops/DURABLE_TRACKING_ISSUES.md index 35b2e70ff..f1c242fc6 100644 --- a/docs/ops/DURABLE_TRACKING_ISSUES.md +++ b/docs/ops/DURABLE_TRACKING_ISSUES.md @@ -32,7 +32,7 @@ next cycle. | Issue | Title | Source workflow | Cadence | Update style | |-------|-------|-----------------|---------|--------------| | [#1796](https://github.com/stranske/Workflows/issues/1796) | Agent metrics weekly summary | [`agents-weekly-metrics.yml`](../../.github/workflows/agents-weekly-metrics.yml) | Mondays 06:00 UTC | New comment per run | -| [#1836](https://github.com/stranske/Workflows/issues/1836) | Sync/Dependabot campaign queue | [`maint-82-sync-dependabot-campaign.yml`](../../.github/workflows/maint-82-sync-dependabot-campaign.yml) + [`.github/scripts/sync_dependabot_campaign.js`](../../.github/scripts/sync_dependabot_campaign.js) | Every 6h + Mondays 10:30 UTC | Body rewritten in place | +| [#1836](https://github.com/stranske/Workflows/issues/1836) | Sync/Dependency campaign queue | [`maint-82-sync-dependency-campaign.yml`](../../.github/workflows/maint-82-sync-dependency-campaign.yml) + [`.github/scripts/sync_dependency_campaign.js`](../../.github/scripts/sync_dependency_campaign.js) | Every 6h + Mondays 10:30 UTC | Body rewritten in place | | [#1868](https://github.com/stranske/Workflows/issues/1868) | 🔄 Consumer repo drift detected | [`health-68-consumer-sync-drift.yml`](../../.github/workflows/health-68-consumer-sync-drift.yml) | Daily 05:10 UTC | Body rewritten in place | The signal flow each tracker carries: diff --git a/docs/ops/debouncing-run-counts.md b/docs/ops/debouncing-run-counts.md index 3ba25dbf9..60a533ff7 100644 --- a/docs/ops/debouncing-run-counts.md +++ b/docs/ops/debouncing-run-counts.md @@ -53,6 +53,6 @@ concurrency groups and set cancel-in-progress: true (see docs/ops/debouncing-run | `.github/workflows/maint-69-sync-integration-repo.yml` | `${{ github.workflow }}-${{ github.ref }}` | | `.github/workflows/maint-71-auto-fix-integration.yml` | `${{ github.workflow }}-issue-${{ github.event.issue.number || github.ref }}` | | `.github/workflows/maint-auto-label-dep-prs.yml` | `${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.ref }}` | -| `.github/workflows/maint-dependabot-auto-lock.yml` | `${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.ref }}` | +| `.github/workflows/maint-auto-lock-deps.yml` | `${{ github.workflow }}-pr-${{ github.event.pull_request.number || github.ref }}` | | `.github/workflows/maint-sync-action-versions.yml` | `${{ github.workflow }}-${{ github.ref }}` | | `.github/workflows/maint-sync-env-from-pyproject.yml` | `${{ github.workflow }}-${{ github.ref }}` | diff --git a/templates/consumer-repo/.github/workflows/dependabot-automerge.yml b/templates/consumer-repo/.github/workflows/dependabot-automerge.yml deleted file mode 100644 index 296b7964f..000000000 --- a/templates/consumer-repo/.github/workflows/dependabot-automerge.yml +++ /dev/null @@ -1,197 +0,0 @@ -name: Dependabot Auto-merge - -on: - pull_request_target: - types: - - opened - - synchronize - - reopened - -permissions: - contents: write - pull-requests: write - -jobs: - automerge: - runs-on: ubuntu-latest - # Security: pull_request_target requires extra validation - # Verify this is genuinely from dependabot before proceeding - if: | - github.actor == 'dependabot[bot]' && - github.event.pull_request.user.login == 'dependabot[bot]' - steps: - - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get PR metadata - id: metadata - uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup API client - uses: ./.github/actions/setup-api-client - with: - secrets: ${{ toJSON(secrets) }} - github_token: ${{ github.token }} - - - name: Wait for checks - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 - with: - script: | - const { createTokenAwareRetry } = require('./.github/scripts/github-api-with-retry.js'); - const { withRetry } = await createTokenAwareRetry({ - github, - core, - env: process.env, - task: 'dependabot-automerge', - capabilities: ['checks:read'], - }); - const pr = context.payload.pull_request; - const owner = context.repo.owner; - const repo = context.repo.repo; - - console.log(`Waiting for checks on PR #${pr.number}`); - - // These checks are excluded because they are internal/non-blocking - // and should not prevent Dependabot PRs from being auto-merged. - const EXCLUDED_CHECK_NAMES = [ - 'Detect keepalive', - 'pr_meta', - 'resolve_pr', - 'Cleanup', - 'Dependabot Auto-merge', - ]; - - // Wait up to 10 minutes for checks to complete - const maxWaitTime = 10 * 60 * 1000; // 10 minutes - const startTime = Date.now(); - const pollInterval = 30000; // 30 seconds - - while (Date.now() - startTime < maxWaitTime) { - const { data: checkRuns } = await withRetry((client) => - client.rest.checks.listForRef({ - owner, - repo, - ref: pr.head.sha - }) - ); - - const allChecks = checkRuns.check_runs || []; - const requiredChecks = allChecks.filter(c => { - const isExcludedByName = EXCLUDED_CHECK_NAMES.some( - name => c.name.includes(name) - ); - const isNonBlockingConclusion = - c.conclusion === 'skipped' || c.conclusion === 'neutral'; - return !isExcludedByName && !isNonBlockingConclusion; - }); - - const pendingChecks = requiredChecks.filter(c => - c.status !== 'completed' - ); - const failedChecks = requiredChecks.filter(c => - c.conclusion !== 'success' && c.conclusion !== null - ); - - console.log( - `Checks: ${requiredChecks.length} total, ` + - `${pendingChecks.length} pending, ` + - `${failedChecks.length} failed` - ); - - // Fail fast if any checks have failed - if (failedChecks.length > 0) { - console.log('Some checks failed:'); - failedChecks.forEach(c => { - console.log(` - ${c.name}: ${c.conclusion}`); - }); - core.setFailed( - `${failedChecks.length} check(s) failed - ` + - 'will not auto-merge' - ); - return; - } - - // All checks passed - success! - if (pendingChecks.length === 0 && requiredChecks.length > 0) { - console.log( - 'All required checks have completed successfully.' - ); - return; - } - - // Still waiting for checks - console.log( - `Waiting ${pollInterval/1000}s for pending checks...` - ); - await new Promise(resolve => setTimeout(resolve, pollInterval)); - } - - core.setFailed('Timeout waiting for checks to complete'); - - - name: Enable auto-merge - if: success() - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 - env: - SERVICE_BOT_PAT: ${{ secrets.SERVICE_BOT_PAT }} - with: - github-token: ${{ secrets.SERVICE_BOT_PAT || secrets.GITHUB_TOKEN }} - script: | - const { createTokenAwareRetry } = require('./.github/scripts/github-api-with-retry.js'); - const { withRetry } = await createTokenAwareRetry({ - github, - core, - env: process.env, - task: 'dependabot-automerge', - capabilities: ['pull-requests:write', 'issues:write'], - }); - const pr = context.payload.pull_request; - const hasCrossRepoToken = !!process.env.SERVICE_BOT_PAT; - - if (!hasCrossRepoToken) { - core.warning( - 'SERVICE_BOT_PAT secret is not configured; ' + - 'falling back to GITHUB_TOKEN. Cross-repo auto-merge ' + - 'may fail due to insufficient permissions.' - ); - } - - try { - await withRetry((client) => client.graphql(` - mutation { - enablePullRequestAutoMerge(input: { - pullRequestId: "${pr.node_id}", - mergeMethod: MERGE - }) { - pullRequest { - number - autoMergeRequest { - enabledAt - } - } - } - } - `)); - - console.log(`✓ Auto-merge enabled for PR #${pr.number}`); - - // Add a comment - await withRetry((client) => client.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: - '🤖 Auto-merge enabled - this PR will be ' + - 'automatically merged when all checks pass.' - })); - } catch (error) { - console.error('Failed to enable auto-merge:', error); - // Do not attempt direct merge - rely on branch protections - // and auto-merge only. This ensures branch protection rules - // are not bypassed. A maintainer can merge manually if needed. - core.setFailed( - 'Failed to enable auto-merge. Branch protections or ' + - 'permissions may prevent automatic merging.' - ); - } diff --git a/templates/consumer-repo/WORKFLOW_USER_GUIDE.md b/templates/consumer-repo/WORKFLOW_USER_GUIDE.md index e944df756..d36bf1d4a 100644 --- a/templates/consumer-repo/WORKFLOW_USER_GUIDE.md +++ b/templates/consumer-repo/WORKFLOW_USER_GUIDE.md @@ -855,18 +855,17 @@ The Workflows repository includes maintenance workflows that handle sync, update --- -### `maint-dependabot-auto-lock.yml` - Lock Dependabot PRs -**Purpose:** Prevents auto-merge for major version updates +### `maint-auto-lock-deps.yml` - Auto-lock dependency PRs +**Purpose:** Keeps `requirements.lock` in sync on dependency-bot PRs -**Trigger:** When Dependabot PR is major version +**Trigger:** A `dependabot[bot]`/`renovate[bot]` PR changes a lock input (`pyproject.toml`, `requirements.txt`, `tools/requirements-llm.txt`) **What It Does:** -- Detects major version bumps -- Adds `do-not-merge` label -- Comments with review request -- Requires manual review +- Re-runs the `uv pip compile` command recorded in the lock header +- Commits the refreshed `requirements.lock` back to the PR branch if it changed +- Workflows-local backstop to the fleet Renovate pip-compile manager -**Use When:** Automatic protection +**Use When:** Automatic, no action needed --- diff --git a/tests/workflows/test_maint82_sync_campaign_contract.py b/tests/workflows/test_maint82_sync_campaign_contract.py index 27556bf65..3b798d947 100644 --- a/tests/workflows/test_maint82_sync_campaign_contract.py +++ b/tests/workflows/test_maint82_sync_campaign_contract.py @@ -2,7 +2,7 @@ import yaml -WORKFLOW = Path(".github/workflows/maint-82-sync-dependabot-campaign.yml") +WORKFLOW = Path(".github/workflows/maint-82-sync-dependency-campaign.yml") def _refresh_script() -> str: @@ -26,3 +26,12 @@ def test_campaign_refresh_passes_current_sync_hash_to_runner(): assert "const currentSyncHash = '${{ steps.hash.outputs.hash }}';" in script assert "currentSyncHash," in script + + +def test_campaign_workflow_bot_agnostic_identity(): + data = yaml.safe_load(WORKFLOW.read_text(encoding="utf-8")) + + # Renamed bot-agnostic display name (was "Sync/Dependabot Campaign"). + assert data["name"] == "Sync/Dependency Campaign" + # Loads the renamed campaign script. + assert "sync_dependency_campaign.js" in _refresh_script() diff --git a/tests/workflows/test_workflow_agents_consolidation.py b/tests/workflows/test_workflow_agents_consolidation.py index 664e65567..f49ca6fba 100644 --- a/tests/workflows/test_workflow_agents_consolidation.py +++ b/tests/workflows/test_workflow_agents_consolidation.py @@ -273,11 +273,6 @@ def test_merge_sync_prs_uploads_machine_readable_report_and_hash_input(): ), "Maint 71 must upload the merge report as a GitHub-visible artifact" -def test_dependabot_weekly_sweep_deletes_merged_branches(): - text = (WORKFLOWS_DIR / "maint-dependabot-weekly-sweep.yml").read_text(encoding="utf-8") - assert "--delete-branch" in text, "Dependabot sweep must request branch deletion on merge" - - def test_consumer_sync_run_uploads_machine_readable_report(): text = (WORKFLOWS_DIR / "maint-68-sync-consumer-repos.yml").read_text(encoding="utf-8") assert ( diff --git a/tests/workflows/test_workflow_naming.py b/tests/workflows/test_workflow_naming.py index 4435fc028..7932d12a1 100644 --- a/tests/workflows/test_workflow_naming.py +++ b/tests/workflows/test_workflow_naming.py @@ -203,8 +203,7 @@ def test_workflow_display_names_are_unique(): "agents-dedup.yml": "Duplicate Detection", "agents-guard.yml": "Health 45 Agents Guard", "maint-auto-label-dep-prs.yml": "Auto-label dependency PRs", - "maint-dependabot-auto-lock.yml": "Dependabot Auto-Lock", - "maint-dependabot-weekly-sweep.yml": "Dependabot Weekly Sweep (Consumers)", + "maint-auto-lock-deps.yml": "Auto-lock dependency PRs", "agents-63-issue-intake.yml": "Agents 63 Issue Intake", "agents-64-verify-agent-assignment.yml": "Agents 64 Verify Agent Assignment", "agents-issue-optimizer.yml": "Agents Issue Optimizer", @@ -273,7 +272,7 @@ def test_workflow_display_names_are_unique(): "maint-74-ledger-base-sync.yml": "Ledger Base Sync", "maint-80-langsmith-metrics-dashboard.yml": "LangSmith Metrics Dashboard", "maint-81-langsmith-fleet-conformance.yml": "LangSmith Fleet Conformance", - "maint-82-sync-dependabot-campaign.yml": "Sync/Dependabot Campaign", + "maint-82-sync-dependency-campaign.yml": "Sync/Dependency Campaign", "maint-83-bootstrap-consumer.yml": "Maint 83 Bootstrap Consumer", "maint-72-fix-pr-body-conflicts.yml": "Maint 72 Fix PR Body Conflicts", "maint-coverage-guard.yml": "Maint Coverage Guard",