Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
91 changes: 87 additions & 4 deletions .github/workflows/health-68-consumer-sync-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
workflow_run:
workflows: [Merge Sync PRs]
types: [completed]
schedule:
# Daily self-healing cadence; workflow_run remains debounced below.
- cron: '15 6 * * *'
workflow_dispatch:
inputs:
repos:
Expand All @@ -24,10 +27,10 @@

permissions:
contents: read
issues: write
actions: read

concurrency:
group: consumer-sync-drift-${{ github.repository }}-${{ github.ref }}
group: health-68-consumer-sync-drift
cancel-in-progress: true

jobs:
Expand All @@ -37,6 +40,8 @@
github.event_name != 'workflow_run' ||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main')
runs-on: ubuntu-latest
outputs:
drift_failed: ${{ steps.compare.outcome == 'failure' }}
steps:
- name: Checkout Workflows repo
uses: actions/checkout@v7
Expand All @@ -47,18 +52,73 @@
secrets: ${{ toJSON(secrets) }}
github_token: ${{ github.token }}

- name: Debounce workflow_run fan-out
id: debounce
if: github.event_name == 'workflow_run'
env:
GH_TOKEN: ${{ github.token }}
DEBOUNCE_MINUTES: '30'
run: |
set -euo pipefail
latest_created="$(
node -e '
const { Octokit } = require("@octokit/rest");
const { createTokenAwareRetry } = require("./.github/scripts/github-api-with-retry.js");
const core = { info: () => {}, warning: console.warn, debug: () => {} };
const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");

(async () => {
const github = new Octokit({ auth: process.env.GH_TOKEN || process.env.GITHUB_TOKEN });
const { withRetry } = await createTokenAwareRetry({
github,
core,
env: process.env,
task: "health-68-consumer-sync-drift-debounce",
capabilities: ["actions:read"],
});
const response = await withRetry(() => github.rest.actions.listWorkflowRuns({
owner,
repo,
workflow_id: "health-68-consumer-sync-drift.yml",
per_page: 20,
}));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
process.stdout.write(JSON.stringify(response.data));
})().catch((error) => {
console.error(error);
process.exit(1);
});
' | jq -r --argjson current "$GITHUB_RUN_ID" \
'[.workflow_runs[] | select(.id != $current) | select(.conclusion == "success" or .conclusion == "failure" or .conclusion == "timed_out")][0].created_at // empty'
)"
if [ -z "$latest_created" ]; then
echo "No prior executable Health 68 run; continuing."
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
latest_epoch="$(date -d "$latest_created" +%s)"
now_epoch="$(date -u +%s)"
age_minutes=$(( (now_epoch - latest_epoch) / 60 ))
echo "Last executable Health 68 run was ${age_minutes} minutes ago at ${latest_created}."
if [ "$age_minutes" -lt "$DEBOUNCE_MINUTES" ]; then
echo "Skipping debounced workflow_run fan-out (< ${DEBOUNCE_MINUTES} minutes)."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"

- name: Setup Python
if: steps.debounce.outputs.skip != 'true'
uses: actions/setup-python@v7
with:
python-version: '3.14'

- name: Install dependencies
if: steps.debounce.outputs.skip != 'true'
run: pip install pyyaml requests

- name: Compare consumer repos to templates
id: compare
if: steps.debounce.outputs.skip != 'true'
env:
CONSUMER_SYNC_DRIFT_REPORT_JSON: artifacts/consumer-sync-drift-report.json
INPUT_REPOS: ${{ inputs.repos }}
Expand All @@ -74,16 +134,39 @@
--report-json "$CONSUMER_SYNC_DRIFT_REPORT_JSON"

- name: Upload drift report
if: ${{ always() }}
if: ${{ always() && steps.debounce.outputs.skip != 'true' }}
uses: actions/upload-artifact@v7
with:
name: consumer-sync-drift-report
path: artifacts/consumer-sync-drift-report.json
if-no-files-found: warn
retention-days: 14

publish-drift:
name: Publish actionable drift tracker update
needs: check-drift
if: ${{ needs.check-drift.result == 'failure' }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Checkout Workflows repo
uses: actions/checkout@v7
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

- name: Setup API client
uses: ./.github/actions/setup-api-client
with:
secrets: ${{ toJSON(secrets) }}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github_token: ${{ github.token }}

- name: Download drift report
uses: actions/download-artifact@v6
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
with:
name: consumer-sync-drift-report
path: artifacts

- name: Create drift issue
if: ${{ failure() }}
uses: actions/github-script@v9
with:
script: |
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/health-71-sync-health-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,28 @@ jobs:
echo "**Hours since last success:** ${{ steps.check.outputs.hours_since_success }}"
fi
} >> "$GITHUB_STEP_SUMMARY"

- name: Assert durable tracker workflow liveness
id: tracker_liveness
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
pip install pyyaml
if python3 scripts/check_durable_tracker_liveness.py --comment-on-failure; then
echo "status=healthy" >> "$GITHUB_OUTPUT"
else
echo "status=unhealthy" >> "$GITHUB_OUTPUT"
exit 1
fi

- name: Tracker liveness summary
if: always() && steps.tracker_liveness.outcome == 'failure'
run: |
{
echo "## Durable tracker liveness"
echo ""
echo "At least one durable tracker source workflow has no executable run inside its configured cadence."
echo "FYI comments were posted on the affected tracker issue(s); no gate was opened or closed."
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
} >> "$GITHUB_STEP_SUMMARY"
21 changes: 21 additions & 0 deletions config/durable_tracker_liveness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Workflows monitored by Health 71 durable-tracker liveness assertions.
# Each entry must match a row in docs/ops/DURABLE_TRACKING_ISSUES.md.
trackers:
- workflow: agents-weekly-metrics.yml
issue: 2211
max_age_hours: 192
- workflow: maint-82-sync-dependency-campaign.yml
issue: 1836
max_age_hours: 1
- workflow: health-83-dependency-sync-efficiency.yml
issue: 2897
max_age_hours: 192
- workflow: health-68-consumer-sync-drift.yml
issue: 2210
max_age_hours: 48
- workflow: maint-69-sync-integration-repo.yml
issue: 2470
event_driven: true
- workflow: maint-80-langsmith-metrics-dashboard.yml
issue: 2415
max_age_hours: 192
2 changes: 1 addition & 1 deletion docs/WORKFLOW_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ _Inline Gate helper_
- **`health-44-gate-branch-protection.yml`** — Enforces branch-protection policy via `tools/enforce_gate_branch_protection.py` when the PAT is configured (now triggered on PRs or by the consolidated sweep) and skips minting extra GitHub App tokens because enforcement already runs with the configured `BRANCH_PROTECTION_TOKEN`.
- **`health-50-security-scan.yml`** — Security scanning workflow triggered on push, PR, and schedule. Runs CodeQL vulnerability scans using the configured PAT priority list without minting additional GitHub App tokens.
- **`health-67-integration-sync-check.yml`** — Daily + event-driven comparison between `templates/integration-repo/` and the Workflows-Integration-Tests repo. Manual dispatchers can now toggle the CI/versions/input checks individually to avoid cloning/running sections they don't need while still filing drift issues when enabled checks detect problems.
- **`health-68-consumer-sync-drift.yml`** — Daily + change-triggered drift detector for consumer repos. Uses the shared `scripts/list_registered_consumer_repos.py` helper to build the repo list (also accepts a manual override) before running `scripts/check_consumer_sync_drift.py`, and files/updates the `consumer-sync` issue when inconsistencies surface.
- **`health-68-consumer-sync-drift.yml`** — Daily self-healing drift detector for consumer repos, also triggered by qualifying template/manifest/script pushes, manual dispatch, and debounced successful main-branch `Merge Sync PRs` completions. Uses the shared `scripts/list_registered_consumer_repos.py` helper to build the repo list (also accepts a manual override) before running `scripts/check_consumer_sync_drift.py`, and files/updates the `consumer-sync` issue when inconsistencies surface.
- **`health-69-consumer-sync-shadow-evidence.yml`** — Weekly, change-triggered, and manual read-only producer for the typed consumer-sync plan plus a non-authorizing Orchestrator handoff artifact. It has `contents: read` only and cannot mutate consumers or promote the shadow capability.
- **`health-71-sync-health-check.yml`** — Daily monitor that inspects the recent `maint-68-sync-consumer-repos` runs. Manual dispatches can tweak `lookback_runs`/`max_age_hours`, and the workflow now reuses `scripts/list_registered_consumer_repos.py` (instead of ad-hoc parsing) without minting an extra GitHub App token.
- **`health-72-template-sync.yml`** — Keeps manifest-declared exact template-sync files in sync between Workflows and the consumer template. For PRs from this repo it auto-runs `scripts/sync_templates.sh` + pushes deltas, then validates via `scripts/validate_template_sync.py`, relying only on the installation token.
Expand Down
2 changes: 1 addition & 1 deletion docs/ops/DURABLE_TRACKING_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ failure-notification path.
| [#2211](https://github.com/stranske/Workflows/issues/2211) | 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-dependency-campaign.yml`](../../.github/workflows/maint-82-sync-dependency-campaign.yml) + [`.github/scripts/sync_dependency_campaign.js`](../../.github/scripts/sync_dependency_campaign.js) | Every 10 minutes + Mondays 10:30 UTC | Body rewritten in place; due transient delivery lanes are serialized, while actionable delivery blockers do not suppress corrective candidate work |
| [#2897](https://github.com/stranske/Workflows/issues/2897) | Dependency/sync maintenance-efficiency advisory report | [`health-83-dependency-sync-efficiency.yml`](../../.github/workflows/health-83-dependency-sync-efficiency.yml) | Weekly Mondays 11:15 UTC + `workflow_dispatch` | Comment appended only on material-evidence fingerprint change |
| [#2210](https://github.com/stranske/Workflows/issues/2210) | 🔄 Consumer repo drift detected | [`health-68-consumer-sync-drift.yml`](../../.github/workflows/health-68-consumer-sync-drift.yml) | After each successful main-branch `Merge Sync PRs` run, qualifying manifest/template/script/tool pushes, or manual dispatch | Body rewritten in place, but only for actionable states |
| [#2210](https://github.com/stranske/Workflows/issues/2210) | 🔄 Consumer repo drift detected | [`health-68-consumer-sync-drift.yml`](../../.github/workflows/health-68-consumer-sync-drift.yml) | Daily 06:15 UTC schedule, debounced successful main-branch `Merge Sync PRs` runs, qualifying manifest/template/script/tool pushes, or manual dispatch | Body rewritten in place, but only for actionable states |
| [#2470](https://github.com/stranske/Workflows/issues/2470) | 🚨 Integration-Tests Sync Failed - Action Required | [`maint-69-sync-integration-repo.yml`](../../.github/workflows/maint-69-sync-integration-repo.yml) | On qualifying template/config pushes or manual dispatch | Stuck-window marker in body + recovery comment |
| [#2415](https://github.com/stranske/Workflows/issues/2415) | 📊 LangSmith Trace Coverage Dashboard | [`maint-80-langsmith-metrics-dashboard.yml`](../../.github/workflows/maint-80-langsmith-metrics-dashboard.yml) | Mondays 09:00 UTC + manual dispatch | Body rewritten in place |

Expand Down
Loading
Loading