Skip to content

ci: bound history fetches for path checks and server test shards - #8955

Open
Synaeve wants to merge 1 commit into
adcontextprotocol:mainfrom
Synaeve:ci/8559-bounded-checkout
Open

Synaeve wants to merge 1 commit into
adcontextprotocol:mainfrom
Synaeve:ci/8559-bounded-checkout

Conversation

@Synaeve

@Synaeve Synaeve commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Closes #8559

Problem

Several verification lanes checked out with fetch-depth: 0, which fetches every branch (about 2,070) and every tag before any check runs. #8559 shows the apps/web check spending its whole 10-minute budget in checkout, and a server unit shard spending 14m49s of 20 there. Most of these lanes only diff two snapshots or diff against the merge base; the server suites read no history at all.

Changes

Audit

Workflow / job What the history is used for Change
apps-web-check.yml Tree diff of origin/<base> (or the push before commit) against HEAD Depth 1, then a depth-1 fetch of the compared commit
build-check.yml server unit shards Nothing (only git rev-parse HEAD) Depth 1
build-check.yml server integration shards Nothing Depth 1 (sparse set and blob:none kept)
changeset-check.yml Scope check base...HEAD, git show base:path, changeset status --since Merge-base fetch
check-testable-snippets.yml base...HEAD names and per-file diffs Merge-base fetch
check-image-alt-text.yml main...HEAD to pick changed images Merge-base fetch
check-migration-numbers.yml PR/merge group: origin/main tree and main...HEAD deletions Merge-base fetch on PR/merge group only
build-check.yml build worker Immutable-artifact check: base...HEAD plus refs/tags/v<version> (a missing tag reads as unreleased) Unchanged, comment added
build-check.yml canonical shards Docs nav test reads origin/<stable>.x; withdrawn-release marker test checks real history Unchanged, comment added
training-agent-storyboards.yml git archive origin/3.0.x Unchanged (protocol-scoped workflow)
release.yml, release-docs.yml, recover-protocol-cdn.yml, deploy.yml, forward-merge-3.*.yml Tags, release provenance, merges, release state Unchanged
ai-review.yml Trusted base checkout for the external Ladon action Unchanged

There are no composite actions under .github/actions/.

scripts/fetch-merge-base.sh

Fetches commits and trees (no blobs) for origin/<base> and HEAD with a doubling depth (50 up to 3,200) until origin/<base>...HEAD has an exact merge base. Then it falls back to the complete history of those two refs, and exits non-zero if there is still no merge base. Two details matter:

  • After the first fetch the base tip is pinned by SHA. Git skips an up-to-date ref when another commit is also wanted, so otherwise only HEAD deepens.
  • A merge base is accepted only when no shallow boundary lies between it and either tip. Without this, a shallow graph can return an older common ancestor, such as a release commit merged into both sides.

It prints the merge base, the depth that found it, the elapsed time and git count-objects -v. Each fetch is tried 3 times, 5 s apart, as actions/checkout does for its own fetches. If the last attempt fails, or git merge-base or git rev-list errors, it exits with ::error::.

.github/workflows/apps-web-check.yml

Depth-1 checkout, then a depth-1 fetch of the compared commit (3 attempts). The tree diff is unchanged. If that commit still cannot be fetched, or the diff errors, every web check runs with a warning. The stable required context is unchanged.

.github/workflows/build-check.yml

Server unit and integration shards check out depth 1. Every worker gets a "Report checkout footprint" step, so CI shows what each checkout fetched next to its duration. The build worker and canonical shards keep full history, with comments saying why.

Merge-base lanes

changeset-check.yml, check-testable-snippets.yml, check-image-alt-text.yml and check-migration-numbers.yml run the script before diffing.

  • A failed diff now stops the image check and the snippet check instead of reading as "no changes".
  • The image and snippet lanes now also trigger on scripts/fetch-merge-base.sh, and the image lane on its own workflow file, so this PR's CI exercises them.
  • The snippet lane fetches the changed docs' blobs in one batch before its per-file checker runs.
  • changeset-check.yml takes the base ref from a job-level BASE_REF instead of interpolating ${{ github.base_ref }} into run:.

Tests

Decisions for reviewers

  1. apps/web when the compare fails. If the compared commit cannot be fetched after 3 attempts, or the diff errors, every web check runs with a warning; the job does not fail. The issue asks to "fail closed". This choice never reports unexecuted checks as passing, but a maintainer should confirm it.
  2. Window and retry sizes. The helper starts at 50 commits and doubles up to 3,200 before falling back to full history. Fetches are tried 3 times, 5 s apart. In the dry run, five of six PRs resolved in the first window; chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates #7388 needed 800.
  3. Integration shard. It keeps blob:none plus the sparse set, so about 0.8 GiB of blobs still arrive through lazy fetches. Locally, that path dominated full-tree checkout time. Watch the new footprint step, and switch to a plain depth-1 fetch if it stays slow.
  4. Merge-base lanes still fetch the full tip with blobs (about 1.3 GiB, including dist/protocol/ tarballs none of them read). filter: blob:none plus a !/dist/protocol/ sparse set is a likely bigger win, left as a follow-up.
  5. Blast radius. The helper now runs in front of Check for changeset on every PR. To land with less risk, the apps/web and server-shard changes (the failures in the issue) could go in first, and the merge-base lanes after.

Out of scope / follow-ups

  • Build worker: a bounded checkout would need the merge base plus every release tag, because a missing tag disables the immutable-artifact check.
  • Canonical shards: need origin/<stable>.x and real history for two tests.
  • training-agent-storyboards.yml appears to need only the origin/3.0.x tip, but it is a protocol-scoped workflow.
  • The acceptance runs on fresh runners need CI.
  • Merge-base lanes: blob:none plus a sparse set without dist/protocol/ (decision 4).

🤖 Generated with Claude Code

workflows
- apps/web check fetches only the compared commit for its tree diff
- server unit and integration shards check out depth 1
- changeset, snippet, image and migration checks fetch only to the merge base
- retry the new fetches three times, as actions/checkout does
- record the checkout footprint in every Build Check worker

scripts
- add fetch-merge-base.sh: doubling depth, exact merge base, full-history fallback

tests
- drive the fetch logic and the web change detector against file:// clones
@mintlify

mintlify Bot commented Nov 12, 2026

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit a06b200.

Approve preview

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated paths require human/CODEOWNERS approval.

This PR modifies six .github/workflows/** files, which fall under the repo's hard, path-based approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so decision-table row 2 fires: the gate is not satisfied and the outcome must be escalate.

The reviewer findings are clean — CI-infrastructure-only changes that bound fetch-depth: 0 history fetches, with a new fail-closed scripts/fetch-merge-base.sh (MAX_DEPTH doubling with --unshallow fallback, shallow-boundary exactness guard, non-zero exit on failure) and strong regression test coverage (tests/ci-bounded-checkout.test.mjs). No protocol surface touched, so no changeset is required. No Critical/High/Medium findings.

The escalation is purely due to the unsatisfied gated-paths approval requirement. Once a human/CODEOWNERS approval lands (review_decision: APPROVED), this clean diff can fall through to approve.

Escalation reasons

  • Modifies .github/workflows/** (apps-web-check.yml, build-check.yml, changeset-check.yml, check-image-alt-text.yml, check-migration-numbers.yml, check-testable-snippets.yml) — files under the deterministic gated-paths approval gate; requires human/CODEOWNERS review (review_decision is REVIEW_REQUIRED, not APPROVED).

Why human review

  • Modifies gated paths under .github/workflows/** (6 workflow files) while review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required before merge.
  • This PR touches a path under a hard, non-overridable approval gate (.github/workflows/apps-web-check.yml (modified) matches .github/workflows/**; .github/workflows/build-check.yml (modified) matches .github/workflows/**; .github/workflows/changeset-check.yml (modified) matches .github/workflows/**; .github/workflows/check-image-alt-text.yml (modified) matches .github/workflows/**; .github/workflows/check-migration-numbers.yml (modified) matches .github/workflows/**; .github/workflows/check-testable-snippets.yml (modified) matches .github/workflows/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Nov 12, 2026
@aao-secretariat
aao-secretariat Bot requested a review from bokelley November 12, 2026 16:24

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI full-history checkout exhausts job budgets before verification runs

1 participant