Refs #416. Fixes a real defect in the merged #422 implementation, caught by #416's own "verify on a real PR" Done-when.
The bug
The docs-only skip merged in #422 was safe but completely ineffective — every docs-only PR still ran the full matrix. Verification PR #426 (a single-.md change) ran test/migrations/functional-e2e for ~18 min instead of skipping.
Root cause (confirmed deterministically)
The test/migrations jobs check out fetch-depth: 1 (shallow). In a shallow clone:
origin/main has no remote-tracking ref, and there's no merge-base, so the detect script's git diff origin/main...HEAD (three-dot) errors → || true → empty diff → the fail-safe returns docs_only=false → full matrix.
Reproduced in a real shallow file:// clone: origin/main did not resolve and three-dot errored, while git diff FETCH_HEAD HEAD (two-dot) correctly returned the docs file. The api-docs/format jobs were unaffected only because they use fetch-depth: 0.
Fix
Diff against FETCH_HEAD (always written by git fetch, resolves in a shallow clone) instead of origin/main.
Use a two-dot tree diff (git diff FETCH_HEAD HEAD) — no merge-base needed.
Push case: fetch depth 2 for the merge commit's first-parent tree; fall through to full matrix if unresolved.
Local: shellcheck clean; in the confirmed-broken shallow synthetic the fix now returns docs_only=true for docs and false for a .cs change.
Post-merge (required before closing #416): open a real docs-only PR and confirm test/migrations/functional-e2e complete in seconds (measured job durations, not just green). I won't tick #416's boxes until that's shown.
Docs
docs/ci-cd.md and docs/decisions.md will be updated with the shallow-checkout note in a follow-up to this same PR (or here) — flagging now so it's not missed.
Refs #416. Fixes a real defect in the merged #422 implementation, caught by #416's own "verify on a real PR" Done-when.
## The bug
The docs-only skip merged in #422 was **safe but completely ineffective** — every docs-only PR still ran the full matrix. Verification PR #426 (a single-`.md` change) ran `test`/`migrations`/`functional-e2e` for ~18 min instead of skipping.
## Root cause (confirmed deterministically)
The `test`/`migrations` jobs check out **`fetch-depth: 1`** (shallow). In a shallow clone:
- **`origin/main` has no remote-tracking ref**, and there's **no merge-base**, so the detect script's `git diff origin/main...HEAD` (three-dot) **errors** → `|| true` → empty diff → the fail-safe returns `docs_only=false` → **full matrix**.
Reproduced in a real shallow `file://` clone: `origin/main` did not resolve and three-dot errored, while `git diff FETCH_HEAD HEAD` (two-dot) correctly returned the docs file. The `api-docs`/`format` jobs were unaffected only because they use `fetch-depth: 0`.
## Fix
- Diff against **`FETCH_HEAD`** (always written by `git fetch`, resolves in a shallow clone) instead of `origin/main`.
- Use a **two-dot** tree diff (`git diff FETCH_HEAD HEAD`) — no merge-base needed.
- Push case: fetch depth 2 for the merge commit's first-parent tree; fall through to full matrix if unresolved.
- Fail-safe + `--no-renames` (rename-misclassification guard) unchanged.
## Verification
- Local: shellcheck clean; in the confirmed-broken shallow synthetic the fix now returns `docs_only=true` for docs and `false` for a `.cs` change.
- **Post-merge (required before closing #416):** open a real docs-only PR and confirm `test`/`migrations`/`functional-e2e` complete in **seconds** (measured job durations, not just green). I won't tick #416's boxes until that's shown.
## Docs
`docs/ci-cd.md` and `docs/decisions.md` will be updated with the shallow-checkout note in a follow-up to this same PR (or here) — flagging now so it's not missed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The test/migrations jobs check out fetch-depth:1. A shallow clone has NO
origin/<base> tracking ref and no merge-base, so the three-dot
origin/main...HEAD errored -> empty diff -> docs_only=false -> EVERY docs-only PR
silently ran the full matrix (safe but the skip never fired). git fetch always
writes FETCH_HEAD, which resolves in a shallow clone; diff against it with a
two-dot tree diff (no merge-base). Confirmed in a real shallow file:// clone:
origin/main did NOT resolve and three-dot errored, while FETCH_HEAD two-dot
correctly returned the docs file. api-docs/format were unaffected only because
they use fetch-depth:0.
Refs #416
Cold-context review-only agent, every claim verified by execution in real git clone --depth=1 clones (reproducing the exact fetch-depth:1 shape), not reasoned:
Fix works: in a shallow clone origin/main does not resolve (the old three-dot break); after the fetch, FETCH_HEAD resolves and git diff --no-renames FETCH_HEAD HEAD (two-dot, no merge-base) returns the correct set → docs-only PR now yields docs_only=true.
False-positive direction is provably safe: built the adversarial case (docs-only PR behind main; main advanced with a .cs the PR lacks) → the .cs surfaces in the two-dot diff → docs_only=false → full matrix. It cannot wrongly skip a code change; behind-main biases toward running more.
Fail-open confirmed, --no-renames retained, push merge-commit case works (depth-2 deepen for HEAD^1), no regression for up-to-date PRs (two-dot == old three-dot there).
NIT (accepted): push-path nfields=$(git rev-list … | wc -w …) lacks || true under pipefail+errexit. Unreachable in a valid checkout (HEAD always resolves) and fail-safe if ever hit (fails the required step → merge blocked, never a silent skip). Accepting rather than spending another full-matrix CI run on a cosmetic, unreachable line.
Cold-context review-only agent, **every claim verified by execution in real `git clone --depth=1` clones** (reproducing the exact `fetch-depth:1` shape), not reasoned:
- **Fix works:** in a shallow clone `origin/main` does not resolve (the old three-dot break); after the fetch, `FETCH_HEAD` resolves and `git diff --no-renames FETCH_HEAD HEAD` (two-dot, no merge-base) returns the correct set → docs-only PR now yields `docs_only=true`.
- **False-positive direction is provably safe:** built the adversarial case (docs-only PR *behind* main; main advanced with a `.cs` the PR lacks) → the `.cs` surfaces in the two-dot diff → `docs_only=false` → full matrix. It **cannot** wrongly skip a code change; behind-main biases toward running more.
- **Fail-open confirmed**, `--no-renames` retained, push merge-commit case works (depth-2 deepen for `HEAD^1`), no regression for up-to-date PRs (two-dot == old three-dot there).
- **NIT (accepted):** push-path `nfields=$(git rev-list … | wc -w …)` lacks `|| true` under `pipefail`+`errexit`. Unreachable in a valid checkout (HEAD always resolves) and fail-*safe* if ever hit (fails the required step → merge blocked, never a silent skip). Accepting rather than spending another full-matrix CI run on a cosmetic, unreachable line.
Review-verdict: MERGEABLE @ 082124f7
timothy
merged commit 4818b07668 into main2026-07-17 22:39:05 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #416. Fixes a real defect in the merged #422 implementation, caught by #416's own "verify on a real PR" Done-when.
The bug
The docs-only skip merged in #422 was safe but completely ineffective — every docs-only PR still ran the full matrix. Verification PR #426 (a single-
.mdchange) rantest/migrations/functional-e2efor ~18 min instead of skipping.Root cause (confirmed deterministically)
The
test/migrationsjobs check outfetch-depth: 1(shallow). In a shallow clone:origin/mainhas no remote-tracking ref, and there's no merge-base, so the detect script'sgit diff origin/main...HEAD(three-dot) errors →|| true→ empty diff → the fail-safe returnsdocs_only=false→ full matrix.Reproduced in a real shallow
file://clone:origin/maindid not resolve and three-dot errored, whilegit diff FETCH_HEAD HEAD(two-dot) correctly returned the docs file. Theapi-docs/formatjobs were unaffected only because they usefetch-depth: 0.Fix
FETCH_HEAD(always written bygit fetch, resolves in a shallow clone) instead oforigin/main.git diff FETCH_HEAD HEAD) — no merge-base needed.--no-renames(rename-misclassification guard) unchanged.Verification
docs_only=truefor docs andfalsefor a.cschange.test/migrations/functional-e2ecomplete in seconds (measured job durations, not just green). I won't tick #416's boxes until that's shown.Docs
docs/ci-cd.mdanddocs/decisions.mdwill be updated with the shallow-checkout note in a follow-up to this same PR (or here) — flagging now so it's not missed.🤖 Generated with Claude Code
Cold-context review-only agent, every claim verified by execution in real
git clone --depth=1clones (reproducing the exactfetch-depth:1shape), not reasoned:origin/maindoes not resolve (the old three-dot break); after the fetch,FETCH_HEADresolves andgit diff --no-renames FETCH_HEAD HEAD(two-dot, no merge-base) returns the correct set → docs-only PR now yieldsdocs_only=true..csthe PR lacks) → the.cssurfaces in the two-dot diff →docs_only=false→ full matrix. It cannot wrongly skip a code change; behind-main biases toward running more.--no-renamesretained, push merge-commit case works (depth-2 deepen forHEAD^1), no regression for up-to-date PRs (two-dot == old three-dot there).nfields=$(git rev-list … | wc -w …)lacks|| trueunderpipefail+errexit. Unreachable in a valid checkout (HEAD always resolves) and fail-safe if ever hit (fails the required step → merge blocked, never a silent skip). Accepting rather than spending another full-matrix CI run on a cosmetic, unreachable line.Review-verdict: MERGEABLE @
082124f7