The Validation step "Assert the base branch is fully merged into the PR
head" ran main_merged.sh, which fetches the live tip of main when the job
starts. PR runs wait in the queue for hours while image-update PRs land
on main several times an hour, so nearly every queued run failed
Validation on start and had to be re-pushed after another main merge
(PR #1265: behind_by=0 at push, behind_by=9 while its run still queued).
main_merged.sh takes a new INFINITO_MAIN_COMMIT override: when set, it
fetches that full commit SHA from the remote and requires HEAD to
contain it instead of the live tip; a set but malformed or empty value
exits 2. The Validation step passes github.event.pull_request.base.sha,
the main tip at event time, which the step's pull_request-only guard
always provides. The run tests the merge against that base, so the gate
still guarantees the tested tree contains it.
The local pre-push hook (make test-main-merged) leaves the override
unset and keeps checking the live tip: a push happens right now, so
lagging the current main is the real defect there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
git writes MERGE_HEAD only after the pre-merge-commit hook returns, so the
gate never found it and aborted every non-fast-forward merge with "No merge
in progress"; git then wrote the merge state via abort_commit(), leaving a
half-finished merge that git status reported as "still merging". The gate
had therefore never verified a single signature.
git also skips pre-merge-commit entirely once a merge conflicts, so the
conflict-resolution commit was unguarded as well. prepare-commit-msg fires
with MERGE_HEAD present on both paths and no-ops on ordinary commits.
Verified in a scratch repo with a real pre-commit install: ordinary commit
passes silently, clean automerge and conflict-resolution commit both block
an unsigned incoming commit, and the extra stash/restore cycle round-trips
unstaged changes byte-identically.
pre_commit install never removes hook types dropped from the config, so the
install script now also uninstalls the stale pre-merge-commit hook.
Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations.
Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>