From 580be7a990a2a8f6fda21f2ba8487695e8ae91dc Mon Sep 17 00:00:00 2001 From: "marcin p. joachimiak" <4625870+realmarcin@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:15:32 -0700 Subject: [PATCH] Run vendored-sync on every PR instead of almost never MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check_vendored_sync.sh compares six vendored files against the canonical hub. Only two of them were reachable through the paths: filter of the workflow the job lived in — scripts/validate_id_label_correspondence.py directly, and src/communitymech/schema/mech_shared.yaml via the schema glob. Editing scripts/chem_formula.py or any of the three tests/test_id_label_*.py never fired the guard, and neither did editing check_vendored_sync.sh itself or scripts/.vendored_canon_ref. The cause is not four missing path entries. A bash+curl job was sharing a paths: filter with a gate that needs uv and a cached OAK ontology download. The filter is right for the expensive job and was inherited by the cheap one. Deriving the filter from the checker's own FILES array is not possible — GitHub evaluates paths: from static YAML before checkout, so it cannot read the repo. Any hand-maintained list has to be kept in sync with the checker by hand, which is the same drift this guard exists to catch. So move the job to its own workflow with no filter rather than re-syncing two lists. Adds a 3-attempt retry, since the job now makes 6 raw.githubusercontent fetches on every PR and a hub blip should not fail unrelated work. Exit 2 (missing or empty pinned ref) short-circuits — a local precondition failure that retrying cannot fix. The retry lives in the workflow, not in check_vendored_sync.sh: that script is vendored byte-identical across the spokes and has no canonical copy in the hub to diff against (#278), so editing it here would create cross-repo drift that nothing detects. Verified: both workflows parse, vendored-sync resolves to no paths filter, label-correspondence keeps its filter and its own job, and the real checker passes 6/6 against CultureBotAI/CultureMech@6be694f3. The workflow is byte-identical to the one landing in MediaIngredientMech apart from one comment naming the local gate. Part of the cross-Mech sweep for CultureBotAI/TraitMech#198. Reference implementation: CultureBotAI/TraitMech#196. Co-Authored-By: Claude Opus 5 --- .github/workflows/label-correspondence.yaml | 23 +++---- .github/workflows/vendored-sync.yaml | 74 +++++++++++++++++++++ 2 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/vendored-sync.yaml diff --git a/.github/workflows/label-correspondence.yaml b/.github/workflows/label-correspondence.yaml index 46c31c657..6f13f0540 100644 --- a/.github/workflows/label-correspondence.yaml +++ b/.github/workflows/label-correspondence.yaml @@ -32,22 +32,15 @@ on: permissions: contents: read -jobs: - # Cross-repo durability guard: the validator is vendored byte-identical across - # the Mech repos, so fail fast if this copy drifts from the canonical hub's copy. - # Fast and dependency-free (no uv / OAK), so it blocks even though the drift - # report below is non-blocking. - vendored-sync: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # Shared-reference drift check against the canonical hub (CultureMech) at - # the pinned commit in scripts/.vendored_canon_ref — the reference lives in - # another repo, so a local edit to a vendored copy fails CI. Fast and - # dependency-free (bash + curl), blocks before any uv/OAK setup. - - name: Verify vendored files match canonical hub - run: bash scripts/check_vendored_sync.sh +# The vendored-sync job used to live here and inherited the `paths:` filter +# above, which is scoped to this workflow's expensive OAK-backed check. That +# filter listed only 2 of the 6 files the drift checker compares, so the guard +# almost never fired (CultureBotAI/TraitMech#198). It now has its own workflow — +# .github/workflows/vendored-sync.yaml — with no paths filter, because it is +# bash + curl and there is nothing to save by filtering it. The filter above is +# correct for THIS job, which needs uv + a cached OAK ontology download. +jobs: label-correspondence: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/vendored-sync.yaml b/.github/workflows/vendored-sync.yaml new file mode 100644 index 000000000..5367e2eff --- /dev/null +++ b/.github/workflows/vendored-sync.yaml @@ -0,0 +1,74 @@ +name: vendored-sync + +# Cross-repo durability guard. Several files here are vendored byte-identical +# from the canonical hub (CultureBotAI/CultureMech) at the commit pinned in +# scripts/.vendored_canon_ref; scripts/check_vendored_sync.sh fetches each one +# and diffs it, so a one-copy edit fails CI. +# +# DELIBERATELY NO `paths:` FILTER. This job used to live in +# label-correspondence.yaml and inherited that workflow's filter, which listed +# only 2 of the 6 files the checker compares. Editing scripts/chem_formula.py or +# any tests/test_id_label_*.py never fired the guard, and neither did editing +# the checker itself or the pinned ref (CultureBotAI/TraitMech#198). +# +# A path list cannot be derived from the checker's own FILES array — GitHub +# evaluates `paths:` from static YAML before checkout, so it cannot read the +# repo. Any hand-maintained list therefore has to be kept in sync with the +# checker by hand, which is precisely the drift this guard exists to prevent. +# Running unconditionally removes the failure class instead of re-syncing two +# lists. The job is bash + curl with no uv/OAK, so there is nothing to save by +# filtering it; the expensive label-correspondence gate keeps its filter. +# +# Reference implementation: CultureBotAI/TraitMech#196. + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: vendored-sync-${{ github.ref }} + # PRs only: on main, cancelling would leave the earlier commit unverified. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + vendored-sync: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + + # Retried here rather than inside check_vendored_sync.sh: that script is + # vendored byte-identical across the spokes and has no canonical copy in + # the hub to diff against (CommunityMech#278), so editing it locally would + # create cross-repo drift that nothing currently detects. + - name: Verify vendored files match canonical hub + run: | + set -uo pipefail + for attempt in 1 2 3; do + # Capture the status explicitly. `if cmd; then ...; fi` is NOT usable + # here: an if-statement whose condition fails and which has no else + # branch returns 0 itself, so a following `status=$?` reads 0 rather + # than the command's exit code. + status=0 + bash scripts/check_vendored_sync.sh || status=$? + if [ "$status" -eq 0 ]; then + exit 0 + fi + # Exit 2 is a local precondition failure (missing or empty pinned + # ref), not a transient fetch problem — retrying cannot help. + if [ "$status" -eq 2 ]; then + echo "::error::scripts/.vendored_canon_ref is missing or empty" >&2 + exit 2 + fi + if [ "$attempt" -lt 3 ]; then + echo "::warning::vendored-sync attempt $attempt failed (exit $status); retrying in 5s" >&2 + sleep 5 + fi + done + echo "::error::vendored files differ from the canonical hub, or the hub could not be reached after 3 attempts" >&2 + exit 1