From 63a8764ce545e05e6c62ff4f33dd8cedc55d580b Mon Sep 17 00:00:00 2001 From: Codex Automation Date: Sun, 23 Aug 2026 07:11:30 -0500 Subject: [PATCH 1/4] fix(sync): cover every shared workflow drift pair --- config/template-drift-allowlist.txt | 69 +++++++++++-------- scripts/check_template_drift.py | 2 +- .../test_check_template_drift_coverage.py | 22 ++++++ 3 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 tests/scripts/test_check_template_drift_coverage.py diff --git a/config/template-drift-allowlist.txt b/config/template-drift-allowlist.txt index 8991ac8f6..c8300ddac 100644 --- a/config/template-drift-allowlist.txt +++ b/config/template-drift-allowlist.txt @@ -4,19 +4,6 @@ # If either file changes, the fingerprint no longer matches and Health 74 fails # until the template is aligned or this baseline is deliberately re-reviewed. # -# 2026-08-23 re-baseline: refreshed the consumer template fingerprints for -# auto-label, capability-check, decompose and dedup after each guarded its -# editable install on a packaging file existing, with a tools/requirements-llm.txt -# fallback. A consumer need not be an installable package (the template ships no -# pyproject.toml), so `pip install -e ".[langchain]"` exited 1 on one -- the same -# defect that made the backplane-conformance stub a hard red gate instead of the -# opt-in skip its own header promises. Deliberately NOT applied to the root -# workflows: this repo has a pyproject.toml declaring a langchain extra, so the -# guard's first branch always fires there and the install already works. The -# consumer-only guard is enforced by -# tests/workflows/test_consumer_template_pip_install_guard.py, so a wholesale -# align cannot silently strip it. -# # 2026-07-24b re-baseline: fingerprints regenerated after check_template_drift.py # gained action-pin canonicalization (uses:@ collapses to @). # From here, Renovate action-pin bumps and the intentional pinned-vs-floating @@ -84,37 +71,37 @@ reason = Intentional divergence re-reviewed 2026-08-10: root conveyor labels Gat [pair.5] main = .github/workflows/agents-auto-label.yml template = templates/consumer-repo/.github/workflows/agents-auto-label.yml -main_sha256 = 84edb688a3f3629e206432bc63a891f7b7b6e8737d53044ddecce50f97f64231 -template_sha256 = 7ca1550aa40b09a0c3f42d2ce3450551008aec62ed8dc94b6dc59a98bff16c78 -reason = Fingerprints refreshed 2026-08-23: both surfaces now pass the named setup-api-client secret inputs instead of the whole-secrets-context blob, applied identically to root and consumer so it introduces no new divergence. That removal is what cleared GitHub's suspicious-workflow hold on agents-dedup (#3185), which then ran at run_attempt 1 with nothing approved after 22 days held. Underlying divergence unchanged: Fingerprints refreshed 2026-08-23: the consumer template now guards its editable install on a packaging file existing, falling back to tools/requirements-llm.txt, because a consumer need not be an installable package (the template ships no pyproject.toml) and `pip install -e ".[langchain]"` exited 1 on such a repo - see stranske/Orchestrator, 128 flat root modules with no pyproject.toml/setup.py/setup.cfg. Consumer-only by design, so NOT applied to root: this repo has a pyproject.toml declaring a langchain extra, where the guard's first branch always fires and the install already works. Enforced by tests/workflows/test_consumer_template_pip_install_guard.py, which fails if the guard is stripped from the template - including by a wholesale align. Underlying divergence unchanged: intentional divergence updated 2026-08-05: root and consumer workflows both isolate the eligibility sparse checkout under eligibility-source/ while retaining consumer SHA pins and auth plumbing. Do not align wholesale: that would strip the consumer security contract. +main_sha256 = 2b69ddf783e49273b966ab87f0caaf65c0a3c2b803bf46eba240d0637eeda00b +template_sha256 = 60a5f4bff00adcf4ab9fe967f6f0d4b53d8be8d1111c545aa0778c7424ff69b9 +reason = Intentional divergence updated 2026-08-05: root and consumer workflows both isolate the eligibility sparse checkout under eligibility-source/ while retaining consumer SHA pins and auth plumbing. Do not align wholesale: that would strip the consumer security contract. [pair.6] main = .github/workflows/agents-autofix-dispatcher.yml template = templates/consumer-repo/.github/workflows/agents-autofix-dispatcher.yml main_sha256 = d50546b43254e3374d53a8efb27b3f47efbddce20ae4511579a103bd6ca1e27c template_sha256 = 533b0f2241708e39e97de00bbae0795b7f9d95f32d9337e0ddcf51ed4fa4c76b -reason = Existing reviewed baseline drift; align the template or update this fingerprint deliberately. +reason = Intentional divergence re-reviewed 2026-08-23: the Workflows-local dispatcher resolves and invokes the in-repository reusable autofix surface, while the consumer wrapper uses the published Workflows workflow with pinned actions and consumer secret wiring. Do not align wholesale because that would remove the consumer deployment contract. [pair.7] main = .github/workflows/agents-capability-check.yml template = templates/consumer-repo/.github/workflows/agents-capability-check.yml main_sha256 = 6a31f201710befd79283a59228524d279345f3b3351aa1e5bdf373da770590a3 -template_sha256 = 92a2ab44ada9e1e5238f8117071df1b86f4c5b300a086b3c970bda623076b9cd -reason = Fingerprints refreshed 2026-08-23: the consumer template now guards its editable install on a packaging file existing, falling back to tools/requirements-llm.txt, because a consumer need not be an installable package (the template ships no pyproject.toml) and `pip install -e ".[langchain]"` exited 1 on such a repo - see stranske/Orchestrator, 128 flat root modules with no pyproject.toml/setup.py/setup.cfg. Consumer-only by design, so NOT applied to root: this repo has a pyproject.toml declaring a langchain extra, where the guard's first branch always fires and the install already works. Enforced by tests/workflows/test_consumer_template_pip_install_guard.py, which fails if the guard is stripped from the template - including by a wholesale align. Underlying divergence unchanged: intentional divergence re-baselined 2026-08-22 after both surfaces migrated from the retired check_capability alias to classify_capabilities. The consumer template retains SHA-pinned actions and LangSmith tracing while the root workflow retains in-repo concurrency and sparse-checkout plumbing; do not align wholesale because that would strip the consumer security contract. +template_sha256 = f295e850992e119a0105921eb0c56cc6573fd368a38bbe0f9624fe0ae904a70c +reason = Intentional divergence re-baselined 2026-08-22 after both surfaces migrated from the retired check_capability alias to classify_capabilities. The consumer template retains SHA-pinned actions and LangSmith tracing while the root workflow retains in-repo concurrency and sparse-checkout plumbing; do not align wholesale because that would strip the consumer security contract. [pair.8] main = .github/workflows/agents-decompose.yml template = templates/consumer-repo/.github/workflows/agents-decompose.yml main_sha256 = 03bf9ec09953e0b1298783b1d14f030fefd40f00a61caf404cf0b562297a1e82 -template_sha256 = 8964f9a4cdb6ffd724cba533befd09ba30336b4d9d2b3047351d045a39ed5949 -reason = Fingerprints refreshed 2026-08-23: the consumer template now guards its editable install on a packaging file existing, falling back to tools/requirements-llm.txt, because a consumer need not be an installable package (the template ships no pyproject.toml) and `pip install -e ".[langchain]"` exited 1 on such a repo - see stranske/Orchestrator, 128 flat root modules with no pyproject.toml/setup.py/setup.cfg. Consumer-only by design, so NOT applied to root: this repo has a pyproject.toml declaring a langchain extra, where the guard's first branch always fires and the install already works. Enforced by tests/workflows/test_consumer_template_pip_install_guard.py, which fails if the guard is stripped from the template - including by a wholesale align. Underlying divergence unchanged: intentional divergence re-baselined 2026-08-09: root-only warning-only REST triage marker added; do not align wholesale because consumer pins and retry plumbing are contractually distinct. +template_sha256 = ea4373306a028425bc147bdd7e003118aab99130f872fb313644cdcf8ba36b84 +reason = Intentional divergence re-baselined 2026-08-09: root-only warning-only REST triage marker added; do not align wholesale because consumer pins and retry plumbing are contractually distinct. [pair.9] main = .github/workflows/agents-dedup.yml template = templates/consumer-repo/.github/workflows/agents-dedup.yml main_sha256 = 95d9d1c0acd6fbc143b1eb56e0c0741be370b2ab8853ed660ac1249821ea41dd -template_sha256 = aa6c161b640eb303df8d47bc4d85535210f9cc7a49e4f165059432d6f0106bd2 -reason = Fingerprints refreshed 2026-08-23: the consumer template now guards its editable install on a packaging file existing, falling back to tools/requirements-llm.txt, because a consumer need not be an installable package (the template ships no pyproject.toml) and `pip install -e ".[langchain]"` exited 1 on such a repo - see stranske/Orchestrator, 128 flat root modules with no pyproject.toml/setup.py/setup.cfg. Consumer-only by design, so NOT applied to root: this repo has a pyproject.toml declaring a langchain extra, where the guard's first branch always fires and the install already works. Enforced by tests/workflows/test_consumer_template_pip_install_guard.py, which fails if the guard is stripped from the template - including by a wholesale align. Underlying divergence unchanged: intentional divergence (re-baselined 2026-08-23): both surfaces now pass the eight named setup-api-client secret inputs instead of `secrets: ${{ toJSON(secrets) }}`, applied identically to root and consumer so the whole-secrets-context handoff is gone from both. Fingerprints refreshed for that change. The remaining divergence is unchanged and still intentional: consumer template SHA-pins third-party actions per the fleet action-pin contract (docs/HISTORY.md, PR #1925) and sets LangSmith tracing env; root uses floating major tags with repo-internal concurrency + sparse-checkout (docs/fixes/sparse-checkout-audit-2026-02-03.csv). Do not align: would strip consumer action pins. +template_sha256 = 392b9354a9e14e85ea47fb6e921cacf5216227e5c615a2725eb5f22249860e41 +reason = Intentional divergence (re-baselined 2026-08-23): both surfaces now pass the eight named setup-api-client secret inputs instead of `secrets: ${{ toJSON(secrets) }}`, applied identically to root and consumer so the whole-secrets-context handoff is gone from both. Fingerprints refreshed for that change. The remaining divergence is unchanged and still intentional: consumer template SHA-pins third-party actions per the fleet action-pin contract (docs/HISTORY.md, PR #1925) and sets LangSmith tracing env; root uses floating major tags with repo-internal concurrency + sparse-checkout (docs/fixes/sparse-checkout-audit-2026-02-03.csv). Do not align: would strip consumer action pins. [pair.10] main = .github/workflows/agents-guard.yml @@ -126,9 +113,9 @@ reason = Intentional divergence re-baselined 2026-06-30: root and consumer guard [pair.11] main = .github/workflows/agents-issue-optimizer.yml template = templates/consumer-repo/.github/workflows/agents-issue-optimizer.yml -main_sha256 = 6f27295c98bd7d94f1768b7036718c7331bbea281fd67faf4c110fe32da52696 -template_sha256 = 1754c7ce1a227199af13bb746d80e506442d0e2572879f2fdfba284a88845238 -reason = Fingerprints refreshed 2026-08-23: both surfaces now pass the named setup-api-client secret inputs instead of the whole-secrets-context blob, applied identically to root and consumer so it introduces no new divergence. That removal is what cleared GitHub's suspicious-workflow hold on agents-dedup (#3185), which then ran at run_attempt 1 with nothing approved after 22 days held. Underlying divergence unchanged: Fingerprints refreshed 2026-08-23: both surfaces now read the issue number from the step-level EVENT_ISSUE_NUMBER env var instead of interpolating ${{ github.event.issue.number }} into the run: body, removing a template-injection surface of the same shape that caused agents-71-codex-belt-dispatcher to be blocked as possibly malicious in consumer repos (#3127, 2026-08-16). Applied identically to root and consumer, so it introduces no new divergence. Underlying divergence unchanged: Intentional divergence re-baselined 2026-08-11: root remains in-tree (scripts/langchain + .github/scripts/issue_format.py); consumer vendors those via Workflows sparse-checkout under workflows-scripts/. Shared behavioral contract includes live format eligibility checks, format-lease release when those checks skip work, checkout-aware path validation, explicit guard retry dispatch, and quoted lowercase identifier acceptance. Do not align wholesale — that would strip consumer action pins/token setup. +main_sha256 = 815372cd8a0981510741d0d30195f1c25d60e881c60c9d88bfc2ee730ffd73b0 +template_sha256 = 1b3567e764da3d7406ef3b0847953d734d94be685712b3f320601b9654d3fc57 +reason = Fingerprints refreshed 2026-08-23: both surfaces now read the issue number from the step-level EVENT_ISSUE_NUMBER env var instead of interpolating ${{ github.event.issue.number }} into the run: body, removing a template-injection surface of the same shape that caused agents-71-codex-belt-dispatcher to be blocked as possibly malicious in consumer repos (#3127, 2026-08-16). Applied identically to root and consumer, so it introduces no new divergence. Underlying divergence unchanged: Intentional divergence re-baselined 2026-08-11: root remains in-tree (scripts/langchain + .github/scripts/issue_format.py); consumer vendors those via Workflows sparse-checkout under workflows-scripts/. Shared behavioral contract includes live format eligibility checks, format-lease release when those checks skip work, checkout-aware path validation, explicit guard retry dispatch, and quoted lowercase identifier acceptance. Do not align wholesale — that would strip consumer action pins/token setup. [pair.12] main = .github/workflows/agents-keepalive-loop-reporter.yml @@ -149,7 +136,7 @@ main = .github/workflows/agents-verifier.yml template = templates/consumer-repo/.github/workflows/agents-verifier.yml main_sha256 = 5e1cb04f45d27ccd30395eb804203677db130c7f1bcabf8054d80cb22c189e97 template_sha256 = 9c803d40ce8b26f4450a60d6f821eb2cadbd59c42b47259d3394ad1d932fee0f -reason = Existing reviewed baseline drift; align the template or update this fingerprint deliberately. +reason = Intentional divergence re-reviewed 2026-08-23: the Workflows verifier retains its local corpus and provider plumbing, while the consumer verifier calls the distributed surface with consumer-specific action pins and token setup. Do not align wholesale because those execution environments are intentionally different. [pair.15] main = .github/workflows/agents-weekly-metrics.yml @@ -164,3 +151,31 @@ template = templates/consumer-repo/.github/workflows/agents-auto-pilot.yml main_sha256 = 68e132fb70a176cbfab05714eb828f3debcde9326d5fcf1cf5a667d0a2675e0d template_sha256 = 843461352cf105f1e6bc0f50c60403f08fe4100fc529b1b301579306343d80a9 reason = Fingerprints refreshed 2026-08-23: both surfaces now pass the named setup-api-client secret inputs instead of the whole-secrets-context blob, applied identically to root and consumer so it introduces no new divergence. That removal is what cleared GitHub's suspicious-workflow hold on agents-dedup (#3185), which then ran at run_attempt 1 with nothing approved after 22 days held. Underlying divergence unchanged: Intentional divergence reviewed 2026-08-21: the Workflows-local auto-pilot retains Workflows-only PR-meta and keepalive fallbacks, while the consumer template dispatches only the consolidated Agents 80 and Agents 81 entry points. The sync manifest delivers the consumer-specific template so retired Workflows-local targets cannot be restored in consumer repositories. + +[pair.17] +main = .github/workflows/autofix.yml +template = templates/consumer-repo/.github/workflows/autofix.yml +main_sha256 = 81f2268c29cc8b5da388a44eab46a8613df153d9e4e83449ab3a5070c03558a7 +template_sha256 = 88cf48708d972137ef8e9fb3efc2323ee93c8cab16eaba0fc22c8c11ea6695c4 +reason = Intentional divergence reviewed 2026-08-23: the Workflows source runs its local reusable autofix workflow and uses the repository's CI concurrency/context contract; the consumer template calls the published reusable workflow with SHA-pinned actions, SERVICE_BOT_PAT, and workflow-run PR context recovery. Do not align wholesale because the consumer is a deployable wrapper rather than the source implementation. + +[pair.18] +main = .github/workflows/maint-coverage-guard.yml +template = templates/consumer-repo/.github/workflows/maint-coverage-guard.yml +main_sha256 = 3b08ecd7c98a8c0f9281136be6013f3602faecf07adc90ee38687f990fc7ffa6 +template_sha256 = e7badb31c3a806d89cbf4a3d87368e0f98785683f901cf0130f64e1c000fdb1a +reason = Intentional divergence reviewed 2026-08-23: Workflows uses its local artifact-cache action and helper sparse checkout, while consumers mint an App token and download coverage artifacts through pinned public actions. The artifact retrieval routes are deliberately different because consumers cannot rely on Workflows-local actions. + +[pair.19] +main = .github/workflows/pr-00-gate.yml +template = templates/consumer-repo/.github/workflows/pr-00-gate.yml +main_sha256 = b10972cc49cce6879f6ccd70af19343ab40330572c5c0003a7140350c87c9a4e +template_sha256 = ae8266ef5429f5ef5f0982292dd8fd3572358352e285976fe006011d5926f2c5 +reason = Intentional divergence reviewed 2026-08-23: the source Gate runs Workflows-only package, ledger, and diff-quality jobs with local reusable workflows; the consumer Gate uses published reusable workflows, pinned actions, and skips unavailable Workflows-local deliberate-break helpers. The consumer template must remain a bootstrap-safe deployment surface. + +[pair.20] +main = .github/workflows/reusable-pr-context.yml +template = templates/consumer-repo/.github/workflows/reusable-pr-context.yml +main_sha256 = 34f19d0bc75d0b3dbf4d897d38a45ccc20a453a3467c930cb749b6881cfe724f +template_sha256 = d4ac61af664ce7330a019ce09b2db451e234ebc9b720827320f1c2db32e35eb5 +reason = Intentional divergence reviewed 2026-08-23: Workflows keeps local setup-api-client, cancellation, and full multi-agent label detection; the consumer uses pinned actions, App-token setup, and only the consumer-supported routing labels. These execution contexts are intentionally not byte-identical. diff --git a/scripts/check_template_drift.py b/scripts/check_template_drift.py index af290cd31..bb0bdb180 100644 --- a/scripts/check_template_drift.py +++ b/scripts/check_template_drift.py @@ -207,7 +207,7 @@ def discover_workflow_pairs(repo_root: Path) -> list[WorkflowPair]: pairs: dict[tuple[str, str], WorkflowPair] = {} - for main_path in sorted(main_dir.glob("agents-*.yml")): + for main_path in sorted(main_dir.glob("*.yml")): template_name = WORKFLOW_ALIAS_MAPPINGS.get(main_path.name, main_path.name) template_path = template_dir / template_name if ( diff --git a/tests/scripts/test_check_template_drift_coverage.py b/tests/scripts/test_check_template_drift_coverage.py new file mode 100644 index 000000000..2eadedc15 --- /dev/null +++ b/tests/scripts/test_check_template_drift_coverage.py @@ -0,0 +1,22 @@ +from pathlib import Path + +from scripts.check_template_drift import WORKFLOW_ALIAS_MAPPINGS, discover_workflow_pairs + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def test_pair_discovery_covers_every_shared_basename() -> None: + main_dir = REPO_ROOT / ".github" / "workflows" + template_dir = REPO_ROOT / "templates" / "consumer-repo" / ".github" / "workflows" + main_names = {p.name for p in main_dir.glob("*.yml")} + template_names = {p.name for p in template_dir.glob("*.yml")} + shared = { + name for name in main_names & template_names + if WORKFLOW_ALIAS_MAPPINGS.get(name, name) in template_names + } + covered = {pair.main_path.name for pair in discover_workflow_pairs(REPO_ROOT)} + missing = sorted(shared - covered) + assert not missing, ( + "these basenames exist in BOTH .github/workflows/ and " + "templates/consumer-repo/.github/workflows/ but have no drift pair: " + ", ".join(missing) + ) From de0a788747f2dfa0ec14c18d13c8d7f73afdc669 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 23 Aug 2026 12:13:22 +0000 Subject: [PATCH 2/4] chore(autofix): formatting/lint --- tests/scripts/test_check_template_drift_coverage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/scripts/test_check_template_drift_coverage.py b/tests/scripts/test_check_template_drift_coverage.py index 2eadedc15..cc01f3cf8 100644 --- a/tests/scripts/test_check_template_drift_coverage.py +++ b/tests/scripts/test_check_template_drift_coverage.py @@ -11,7 +11,8 @@ def test_pair_discovery_covers_every_shared_basename() -> None: main_names = {p.name for p in main_dir.glob("*.yml")} template_names = {p.name for p in template_dir.glob("*.yml")} shared = { - name for name in main_names & template_names + name + for name in main_names & template_names if WORKFLOW_ALIAS_MAPPINGS.get(name, name) in template_names } covered = {pair.main_path.name for pair in discover_workflow_pairs(REPO_ROOT)} From da60051b8448955853b0cb464f47dd7ff4cb97b5 Mon Sep 17 00:00:00 2001 From: Codex Automation Date: Sun, 23 Aug 2026 07:41:55 -0500 Subject: [PATCH 3/4] fix(sync): satisfy test-quality and broaden Health 74 triggers Use literal empty-list assertion in drift coverage test, widen Health 74 path filters to all root workflow basenames, and document the expanded scope. Co-authored-by: Cursor --- .github/workflows/health-74-template-drift.yml | 4 ++-- docs/ci/WORKFLOWS.md | 2 +- docs/ci/WORKFLOW_SYSTEM.md | 2 +- tests/scripts/test_check_template_drift_coverage.py | 2 +- tests/workflows/test_template_drift_workflow.py | 6 ++++++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/health-74-template-drift.yml b/.github/workflows/health-74-template-drift.yml index b6a9299c3..de7653ac1 100644 --- a/.github/workflows/health-74-template-drift.yml +++ b/.github/workflows/health-74-template-drift.yml @@ -3,13 +3,13 @@ name: Health 74 Template Drift on: push: paths: - - '.github/workflows/agents-*.yml' + - '.github/workflows/*.yml' - 'templates/consumer-repo/.github/workflows/*.yml' - 'scripts/check_template_drift.py' - 'config/template-drift-allowlist.txt' pull_request: paths: - - '.github/workflows/agents-*.yml' + - '.github/workflows/*.yml' - 'templates/consumer-repo/.github/workflows/*.yml' - 'scripts/check_template_drift.py' - 'config/template-drift-allowlist.txt' diff --git a/docs/ci/WORKFLOWS.md b/docs/ci/WORKFLOWS.md index 4ab5ffa7b..ff6fc5bdd 100644 --- a/docs/ci/WORKFLOWS.md +++ b/docs/ci/WORKFLOWS.md @@ -199,7 +199,7 @@ Scheduled health jobs keep the automation ecosystem aligned: * [`health-71-sync-health-check.yml`](../../.github/workflows/health-71-sync-health-check.yml) monitors sync workflow health daily - creates issues if all recent runs failed or sync is stale (daily schedule, manual dispatch). * [`health-72-template-sync.yml`](../../.github/workflows/health-72-template-sync.yml) validates that manifest-declared exact template-sync files are in sync with their consumer template copies (PR, push on exact-sync source/template changes). * [`health-73-template-completeness.yml`](../../.github/workflows/health-73-template-completeness.yml) validates that consumer-intended workflows exist in the template directory and sync manifest - prevents workflows from being added to .github/workflows/ without being synced to consumer repos (PR, push on workflow/template changes). -* [`health-74-template-drift.yml`](../../.github/workflows/health-74-template-drift.yml) checks normalized content drift between main agent workflows and their consumer repo templates. It fails on unallowlisted drift and permits only fingerprinted baseline differences recorded in `config/template-drift-allowlist.txt` (PR, push on workflow/template changes). +* [`health-74-template-drift.yml`](../../.github/workflows/health-74-template-drift.yml) checks normalized content drift between main workflows that have consumer template counterparts and their `templates/consumer-repo/` copies. It fails on unallowlisted drift and permits only fingerprinted baseline differences recorded in `config/template-drift-allowlist.txt` (PR, push on any `.github/workflows/*.yml`, template workflow, checker, or allowlist change). * [`health-75-api-rate-diagnostic.yml`](../../.github/workflows/health-75-api-rate-diagnostic.yml) monitors API rate limit utilization across PATs and GitHub Apps - alerts when usage exceeds 85% and provides load balancing analysis (scheduled every 4 hours, manual dispatch). * [`health-76-codex-cli-freshness.yml`](../../.github/workflows/health-76-codex-cli-freshness.yml) emits a weekly machine-readable freshness contract for the verifier `@openai/codex` CLI pin and uploads the deliberate update path as an artifact (scheduled weekly, manual dispatch). * [`health-78-backplane-contract.yml`](../../.github/workflows/health-78-backplane-contract.yml) Workflows-internal gate that runs on PRs touching the run-contract/v1 contract set (schemas, registry, validator, fixtures): asserts the three schemas load as valid draft 2020-12 JSON Schema, `config/backplane_participants.json` keeps the required shape, and the bundled valid/invalid fixtures behave (the validator self-smoke). diff --git a/docs/ci/WORKFLOW_SYSTEM.md b/docs/ci/WORKFLOW_SYSTEM.md index ac776bcf2..3eacdaa27 100644 --- a/docs/ci/WORKFLOW_SYSTEM.md +++ b/docs/ci/WORKFLOW_SYSTEM.md @@ -739,7 +739,7 @@ Keep this table handy when you are triaging automation: it confirms which workfl | **Health 71 Sync Health Check** (`health-71-sync-health-check.yml`, maintenance bucket) | `schedule` (daily), `workflow_dispatch` | Monitor sync workflow health and create issues when all recent runs failed or sync is stale. | ⚪ Scheduled/manual | [Sync health check runs](https://github.com/stranske/Workflows/actions/workflows/health-71-sync-health-check.yml) | | **Health 72 Template Sync** (`health-72-template-sync.yml`, maintenance bucket) | `pull_request`, `push` (exact-sync sources/templates) | Validate that manifest-declared exact template-sync files match their `templates/consumer-repo/` copies. | ⚪ Required on PRs | [Template sync validation runs](https://github.com/stranske/Workflows/actions/workflows/health-72-template-sync.yml) | | **Health 73 Template Completeness** (`health-73-template-completeness.yml`, maintenance bucket) | `pull_request`, `push` (`.github/workflows/`, `templates/`, manifest) | Validate that consumer-intended workflows exist in template and manifest. Prevents workflows added to .github/workflows/ without being synced to consumer repos. | ⚪ Required on PRs | [Template completeness runs](https://github.com/stranske/Workflows/actions/workflows/health-73-template-completeness.yml) | -| **Health 74 Template Drift** (`health-74-template-drift.yml`, maintenance bucket) | `pull_request`, `push` (agents workflows, templates, drift checker config) | Check normalized content drift between main agent workflows and consumer repo templates. Fails on unallowlisted drift; permits only fingerprinted baseline differences in `config/template-drift-allowlist.txt`. | ⚪ Required on matching PRs | [Template drift runs](https://github.com/stranske/Workflows/actions/workflows/health-74-template-drift.yml) | +| **Health 74 Template Drift** (`health-74-template-drift.yml`, maintenance bucket) | `pull_request`, `push` (any `.github/workflows/*.yml`, templates, drift checker config) | Check normalized content drift between main workflows with consumer template counterparts and their `templates/consumer-repo/` copies. Fails on unallowlisted drift; permits only fingerprinted baseline differences in `config/template-drift-allowlist.txt`. | ⚪ Required on matching PRs | [Template drift runs](https://github.com/stranske/Workflows/actions/workflows/health-74-template-drift.yml) | | **Health 75 API Rate Diagnostic** (`health-75-api-rate-diagnostic.yml`, maintenance bucket) | `schedule` (every 4 hours), `workflow_dispatch` | Monitor API rate limit utilization across GITHUB_TOKEN, PATs, and GitHub Apps. Alerts when usage exceeds 85%, tracks consumer repo workflow activity, and provides load balancing analysis. | ⚪ Scheduled/manual | [API rate diagnostic runs](https://github.com/stranske/Workflows/actions/workflows/health-75-api-rate-diagnostic.yml) | | **Health 76 Codex CLI Freshness** (`health-76-codex-cli-freshness.yml`, maintenance bucket) | `schedule` (weekly), `workflow_dispatch` | Compare the verifier `@openai/codex` install pin with the latest npm release and upload a machine-readable update-path contract for deliberate CLI upgrades. | ⚪ Scheduled/manual | [Codex CLI freshness runs](https://github.com/stranske/Workflows/actions/workflows/health-76-codex-cli-freshness.yml) | | **Backplane Contract Integrity** (`health-78-backplane-contract.yml`, maintenance bucket) | `pull_request`, `push` (contract set: schemas, registry, validator, fixtures) | Workflows-internal gate over the run-contract/v1 contract set: asserts the three schemas load as valid draft 2020-12 JSON Schema, `config/backplane_participants.json` keeps the required shape, and the bundled valid/invalid fixtures behave (validator self-smoke). | ⚪ Required on contract PRs | [Backplane contract integrity runs](https://github.com/stranske/Workflows/actions/workflows/health-78-backplane-contract.yml) | diff --git a/tests/scripts/test_check_template_drift_coverage.py b/tests/scripts/test_check_template_drift_coverage.py index cc01f3cf8..9aee3de5c 100644 --- a/tests/scripts/test_check_template_drift_coverage.py +++ b/tests/scripts/test_check_template_drift_coverage.py @@ -17,7 +17,7 @@ def test_pair_discovery_covers_every_shared_basename() -> None: } covered = {pair.main_path.name for pair in discover_workflow_pairs(REPO_ROOT)} missing = sorted(shared - covered) - assert not missing, ( + assert missing == [], ( "these basenames exist in BOTH .github/workflows/ and " "templates/consumer-repo/.github/workflows/ but have no drift pair: " + ", ".join(missing) ) diff --git a/tests/workflows/test_template_drift_workflow.py b/tests/workflows/test_template_drift_workflow.py index 52983125b..7ce532eed 100644 --- a/tests/workflows/test_template_drift_workflow.py +++ b/tests/workflows/test_template_drift_workflow.py @@ -9,6 +9,12 @@ WORKFLOW = REPO_ROOT / ".github" / "workflows" / "health-74-template-drift.yml" +def test_template_drift_workflow_triggers_on_all_root_workflows() -> None: + source = WORKFLOW.read_text(encoding="utf-8") + assert ".github/workflows/*.yml" in source + assert "agents-*.yml" not in source + + def test_template_drift_workflow_installs_pyyaml_before_checker() -> None: workflow = yaml.safe_load(WORKFLOW.read_text(encoding="utf-8")) steps = workflow["jobs"]["check-drift"]["steps"] From bb0433418454b222c728c8195ba230149acd4893 Mon Sep 17 00:00:00 2001 From: Codex Automation Date: Sun, 23 Aug 2026 10:08:33 -0500 Subject: [PATCH 4/4] fix(sync): refresh drift baselines after rebase --- config/template-drift-allowlist.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/config/template-drift-allowlist.txt b/config/template-drift-allowlist.txt index c8300ddac..296fa5c08 100644 --- a/config/template-drift-allowlist.txt +++ b/config/template-drift-allowlist.txt @@ -71,8 +71,8 @@ reason = Intentional divergence re-reviewed 2026-08-10: root conveyor labels Gat [pair.5] main = .github/workflows/agents-auto-label.yml template = templates/consumer-repo/.github/workflows/agents-auto-label.yml -main_sha256 = 2b69ddf783e49273b966ab87f0caaf65c0a3c2b803bf46eba240d0637eeda00b -template_sha256 = 60a5f4bff00adcf4ab9fe967f6f0d4b53d8be8d1111c545aa0778c7424ff69b9 +main_sha256 = 84edb688a3f3629e206432bc63a891f7b7b6e8737d53044ddecce50f97f64231 +template_sha256 = 7ca1550aa40b09a0c3f42d2ce3450551008aec62ed8dc94b6dc59a98bff16c78 reason = Intentional divergence updated 2026-08-05: root and consumer workflows both isolate the eligibility sparse checkout under eligibility-source/ while retaining consumer SHA pins and auth plumbing. Do not align wholesale: that would strip the consumer security contract. [pair.6] @@ -86,21 +86,21 @@ reason = Intentional divergence re-reviewed 2026-08-23: the Workflows-local disp main = .github/workflows/agents-capability-check.yml template = templates/consumer-repo/.github/workflows/agents-capability-check.yml main_sha256 = 6a31f201710befd79283a59228524d279345f3b3351aa1e5bdf373da770590a3 -template_sha256 = f295e850992e119a0105921eb0c56cc6573fd368a38bbe0f9624fe0ae904a70c +template_sha256 = 92a2ab44ada9e1e5238f8117071df1b86f4c5b300a086b3c970bda623076b9cd reason = Intentional divergence re-baselined 2026-08-22 after both surfaces migrated from the retired check_capability alias to classify_capabilities. The consumer template retains SHA-pinned actions and LangSmith tracing while the root workflow retains in-repo concurrency and sparse-checkout plumbing; do not align wholesale because that would strip the consumer security contract. [pair.8] main = .github/workflows/agents-decompose.yml template = templates/consumer-repo/.github/workflows/agents-decompose.yml main_sha256 = 03bf9ec09953e0b1298783b1d14f030fefd40f00a61caf404cf0b562297a1e82 -template_sha256 = ea4373306a028425bc147bdd7e003118aab99130f872fb313644cdcf8ba36b84 +template_sha256 = 8964f9a4cdb6ffd724cba533befd09ba30336b4d9d2b3047351d045a39ed5949 reason = Intentional divergence re-baselined 2026-08-09: root-only warning-only REST triage marker added; do not align wholesale because consumer pins and retry plumbing are contractually distinct. [pair.9] main = .github/workflows/agents-dedup.yml template = templates/consumer-repo/.github/workflows/agents-dedup.yml main_sha256 = 95d9d1c0acd6fbc143b1eb56e0c0741be370b2ab8853ed660ac1249821ea41dd -template_sha256 = 392b9354a9e14e85ea47fb6e921cacf5216227e5c615a2725eb5f22249860e41 +template_sha256 = aa6c161b640eb303df8d47bc4d85535210f9cc7a49e4f165059432d6f0106bd2 reason = Intentional divergence (re-baselined 2026-08-23): both surfaces now pass the eight named setup-api-client secret inputs instead of `secrets: ${{ toJSON(secrets) }}`, applied identically to root and consumer so the whole-secrets-context handoff is gone from both. Fingerprints refreshed for that change. The remaining divergence is unchanged and still intentional: consumer template SHA-pins third-party actions per the fleet action-pin contract (docs/HISTORY.md, PR #1925) and sets LangSmith tracing env; root uses floating major tags with repo-internal concurrency + sparse-checkout (docs/fixes/sparse-checkout-audit-2026-02-03.csv). Do not align: would strip consumer action pins. [pair.10] @@ -113,8 +113,8 @@ reason = Intentional divergence re-baselined 2026-06-30: root and consumer guard [pair.11] main = .github/workflows/agents-issue-optimizer.yml template = templates/consumer-repo/.github/workflows/agents-issue-optimizer.yml -main_sha256 = 815372cd8a0981510741d0d30195f1c25d60e881c60c9d88bfc2ee730ffd73b0 -template_sha256 = 1b3567e764da3d7406ef3b0847953d734d94be685712b3f320601b9654d3fc57 +main_sha256 = 6f27295c98bd7d94f1768b7036718c7331bbea281fd67faf4c110fe32da52696 +template_sha256 = 1754c7ce1a227199af13bb746d80e506442d0e2572879f2fdfba284a88845238 reason = Fingerprints refreshed 2026-08-23: both surfaces now read the issue number from the step-level EVENT_ISSUE_NUMBER env var instead of interpolating ${{ github.event.issue.number }} into the run: body, removing a template-injection surface of the same shape that caused agents-71-codex-belt-dispatcher to be blocked as possibly malicious in consumer repos (#3127, 2026-08-16). Applied identically to root and consumer, so it introduces no new divergence. Underlying divergence unchanged: Intentional divergence re-baselined 2026-08-11: root remains in-tree (scripts/langchain + .github/scripts/issue_format.py); consumer vendors those via Workflows sparse-checkout under workflows-scripts/. Shared behavioral contract includes live format eligibility checks, format-lease release when those checks skip work, checkout-aware path validation, explicit guard retry dispatch, and quoted lowercase identifier acceptance. Do not align wholesale — that would strip consumer action pins/token setup. [pair.12]