Run vendored-sync on every PR instead of almost never - #302
Conversation
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 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a CI blind spot in CommunityMech’s vendored-file drift guard by moving vendored-sync into its own dedicated workflow that runs on every PR (and on main pushes), instead of inheriting the paths: filter intended for the heavier OAK/uv-backed label correspondence check.
Changes:
- Add a standalone
vendored-syncworkflow with nopaths:filter, so all six files checked byscripts/check_vendored_sync.share always covered. - Add workflow-level retry logic (3 attempts, 5s backoff) for transient hub fetch failures, while short-circuiting on local precondition failures (exit 2).
- Remove the embedded
vendored-syncjob fromlabel-correspondence.yamland document why it was split.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| .github/workflows/vendored-sync.yaml | New always-on vendored drift guard workflow with retry logic and concurrency settings. |
| .github/workflows/label-correspondence.yaml | Removes the old vendored-sync job and documents the rationale for keeping paths: filtering only on the expensive label gate. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ReviewAdversarial pass over this diff, done across both sweep PRs together since they are the same change. The cost asymmetry is now measured, not assertedThe argument for removing the filter is that the job is too cheap to be worth filtering. Both PRs now have real numbers:
25x and 47x. The filter was correct for the expensive job and inherited by the cheap one; that is the whole bug. Fleet consistency verified, not assumed
Filed from this review — TraitMech#209
Not urgent: divergence here degrades gracefully rather than producing a false pass. Corrections this sweep made to TraitMech#198
StateAll checks green. Not merging — that is the maintainer's call. |
ReviewSame fix as MediaIngredientMech#166, in the repo with the same defect. It now has its own workflow with no This supersedes my issue #280, which proposed extending the filter. Extending it would have left two lists to keep in sync; removing it removes the failure class. Verified disjoint from #305, the only other open PR here, which touches a single Both checks pass, including Closing #280 as fixed, with the note that the fix taken was better than the one filed. |
Part of the cross-Mech sweep tracked in CultureBotAI/TraitMech#198. Reference implementation: CultureBotAI/TraitMech#196. Companion PR: CultureBotAI/MediaIngredientMech#166.
The problem
scripts/check_vendored_sync.shcompares six files against the canonical hub. Only two were reachable through thepaths:filter of the workflow the job lived in:trigger_paths?scripts/validate_id_label_correspondence.pysrc/communitymech/schema/mech_shared.yamlsrc/communitymech/schema/**scripts/chem_formula.pytests/test_id_label_empty_adapter.pytests/test_id_label_unknown_prefix.pytests/test_id_label_plausibility.pyscripts/check_vendored_sync.shandscripts/.vendored_canon_refwere not listed either, so editing the checker or silently reverting the pin did not fire the checker.Why "delete the filter" rather than "extend it"
The two jobs sharing that filter are not comparable in cost:
vendored-syncis bash + curl, whilelabel-correspondenceneedsuvand a cached OAK ontology download. The filter is correct for the expensive one and was inherited by the cheap one.Extending the list by hand recreates the bug — two lists that must agree, with nothing enforcing it. And deriving the filter from the checker's
FILESarray is not implementable: GitHub evaluatespaths:from static YAML before checkout, so it cannot read the repo.So the job moves to its own workflow with no filter. There is no longer a list that can drift.
Retry
Running unconditionally means 6 unauthenticated
raw.githubusercontentfetches per PR, so a hub blip would fail unrelated work. Three attempts, 5s apart. Exit 2 (missing/empty pinned ref) short-circuits, since retrying cannot fix a local precondition.The retry is 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 exactly the cross-repo drift that issue describes, with nothing to detect it.Verified
vendored-syncresolves to nopaths:filter andlabel-correspondenceretains its filter and its own jobCultureBotAI/CultureMech@6be694f3Note on the sweep
Checking the fleet corrected two things in TraitMech#198:
CultureBotAI/MediaIngredientMech. The issue recorded MIM as unverified becausegh api CultureBotAI/MIM404s — that is an abbreviation, not a repo name.check_vendored_sync.shand novendored-syncjob, so there is nothing to fix there. The sweep is two spokes, not three repos.🤖 Generated with Claude Code