Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

ci(repo-gates): restore the build's turbo cache, save and prune on push only - #13360

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-13348-repo-gates-turbo-cache
Oct 24, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-13348-repo-gates-turbo-cache

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #13348
Clause-②: no

Governed surface

.github/workflows/** is on objectui's governed surface (node scripts/check-governed-queue-guard.mjs --test .github/workflows/repo-gates.yml → GOVERNED). This PR stays draft until an approval from a governed approver (os-zhuang / hotlong) is on record.

What changed (3 files)

  • .github/workflows/repo-gates.yml: the build step now has a Turbo cache, set up the same way as performance-budget.yml's bundle-analysis cache (objectui#12276):
    • Restore Turbo Cache runs before "Build every package the gates below read". It uses actions/cache/restore@v6 on .turbo/cache, with the key turbo-${{ runner.os }}-repo-gates-${{ github.sha }} and one restore key, turbo-${{ runner.os }}-repo-gates-. It runs on the same condition as the build: !cancelled() && steps.install.outcome == 'success'.
    • The build step sets TURBO_RUN_SUMMARY: 'true'. The build command and its filter set are unchanged, and it is still never an unfiltered turbo run build (header, repo-gates.yml:55).
    • Three bookkeeping steps run after the last gate, all bounded and continue-on-error:
      • Report what the turbo cache replayed runs on every run (!cancelled()).
      • Prune the turbo cache to what this run used runs on push only.
      • Save Turbo Cache runs on push only, and only when cache-hit != 'true', with a 5 min bound. The existing save of the pinned @objectstack build stays the last step.
    • Pull-request and merge-group runs restore the cache and never save it.
  • scripts/__tests__/check-action-ref-convention.test.ts: this test pins the exact list of uses: refs in repo-gates.yml. The new restore and save add two refs, so the list now has them, in file order.
  • content/docs/guide/ci-cd-pipeline.md: the Repo Gates section now names the cache and scripts/report-turbo-cache.mjs. ci-cd-pipeline-doc.test.ts requires the page to name every first-party command a workflow runs, and it has no exception list.

$SKILL_FILTER / $SNIPPET_FILTER

On this PR's base, the two derived filters add no tasks to the build. Checked with turbo run build --dry=json: --filter='./packages/*' alone gives 40 tasks, and adding both derived filters gives the same 40. That is also the task set bundle-analysis builds. The filters are derived on every run, so they could change it later. If one ever names a package outside packages/, that run gets fewer cache hits. It does not break the cache.

How acceptance will be measured

  • Baseline: the latest main push run, 39921589107: the build step took 247 s and the job took 419 s.
  • This PR's own run will miss the cache. Pull requests restore from main's entry, and no repo-gates- entry exists yet. The ≤90 s build step can only show up after a push to main has saved an entry. To read it:
    • check the ::notice that "Report what the turbo cache replayed" writes on the first PR run after that push;
    • check that the push run's prune and save steps ran.
  • Caveat on push runs: push runs share the ref's concurrency group with cancel-in-progress: true, so a merge can cancel the previous merge's run before it saves. Of the 20 newest main push runs, 18 succeeded and 2 were cancelled, so the save should land on most merges.

Judgment calls

  1. The restore is gated on the install outcome, the same condition as the build it serves, so a run that will not build does not spend a restore. bundle-analysis's restore has no if:, and the turbo cache policy test does not constrain this.
  2. The save's 5 min bound is not yet derived from this step's own runs, because no run of this job has saved this cache. The workflow comment says so. The bound rests on the nearest measured save of the same 40-task entry, bundle-analysis's 1 s save (objectui#7048's arithmetic), and the comment says to re-derive it from this step's own runs. Including the pinned build's 2 min save, this job's save bounds total 7 of its 30 minutes.

Checks run

  • All 90 scripts/__tests__/*.test.ts files that read workflows, repo-gates or the CI/CD page pass. This includes report-turbo-cache.test.ts (each job has its own prefix and only one restore key, only push saves, the prune runs before the save, the report reads the summary) and workflow-cache-save-bound.test.ts (saves use the split form, run after the last command and stay within the job ceiling). Before the 2 pin updates, those 2 tests failed.
  • check-governed-queue-guard --test → GOVERNED.
  • repo-gates.yml parses with the yaml package.
  • These checks pass: check-action-ref-convention, check-pre-install-import-graph, check-control-bytes, check-doc-links, check-new-cross-file-line-citations (0 new) and check-changeset-presence (no changeset owed: no released package source changed).
  • actionlint is not installed and the repo has no wrapper for it, so it was not run.

Checklist

  • Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj

…sh only (objectui#13348)

Repo Gates cold-built `./packages/*` on every pull request, push and queue
build: the one build-carrying job with no `.turbo/cache` restore. Add the
`performance-budget.yml` shape (objectui#12276): one restore key under this
job's own `turbo-<os>-repo-gates-` prefix, TURBO_RUN_SUMMARY on the build
step, then the replay report, the push-only prune and the push-only bounded
save as bookkeeping after the last gate. The build's filter set is unchanged.

The two in-tree pins that read this workflow move with it: the action-ref
convention test's list of repo-gates.yml refs, and the CI/CD pipeline page's
Repo Gates section, which must name `scripts/report-turbo-cache.mjs`.

Refs objectui#13348

Claude-Session: https://claude.ai/code/session_01WJozybDmRH8z2c5SD7vJkj
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation configuration ci/cd tests labels Oct 23, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 00fb95e38809f8adfa4957eed3f705786848f753
Local-runs: none
Scope: .github/workflows/repo-gates.yml + .github/workflows/performance-budget.yml (the bundle-analysis cache the hunks claim to mirror), scripts/report-turbo-cache.mjs (the report/prune the new steps run), scripts/tests/report-turbo-cache.test.ts and scripts/tests/workflow-cache-save-bound.test.ts (the policy every .turbo/cache in .github/workflows/ is held to), turbo.json (what the replayed build hash covers), package.json (check:readme-exports), scripts/tests/check-action-ref-convention.test.ts and content/docs/guide/ci-cd-pipeline.md (the two pins the PR moves)

Inputs: card #13348 (body and all three comments, the surface revision 6364367391 included), PR #13360 body and file list, the net diff origin/main...pr13360 (merge-base 2c4e5f5af, three files, +101/−2), the cited files read at the head's tree, and the check-runs on the head. The dispatch order and the dispatching seat's conclusions were not read.

① Derived judgments

Behaviour changes the diff implies, each judged:

  1. The build's task set is unchanged; the governing text holds. The Build every package the gates below read step keeps pnpm exec turbo run build --filter='./packages/*' $SKILL_FILTER $SNIPPET_FILTER byte for byte and the same if:; the only addition is TURBO_RUN_SUMMARY: 'true', which turbo neither hashes (a TURBO_* variable, not in turbo.json's env/globalEnv) nor acts on beyond writing .turbo/runs/. The header's "never an unfiltered turbo run build" is untouched. Right.
  2. PR and merge-group runs restore and never save. Save Turbo Cache runs only when github.event_name == 'push' (and cache-hit != 'true'); Prune has the same event. pull_request, merge_group and workflow_dispatch runs therefore write nothing, so no PR can plant an entry that a later run restores. The population test (saves only on runs that are not a pull request or a queue build, after pruning on the same runs) reads this job from .github/workflows/ and holds it to exactly that. Right.
  3. Push saves after prune, within bounds. Order on the head: last gate (README Export Check) → report → prune (run:) → Save Turbo Cache → pinned save. Both saves sit after the job's last run: step, each carries timeout-minutes and continue-on-error: true, and the bounds total 5 + 2 = 7 of a 30-minute ceiling (the save-bound test's total * 2 > ceiling rule: 14 ≤ 30). The save's if: has no !cancelled(), so the implicit success() applies: a main push with any red step saves nothing. Same shape as bundle-analysis; conservative, not a gap. Right.
  4. A stale or poisoned entry cannot make a gate pass that would fail cold. (a) Only main/develop pushes write, so the only producer is this job's own build at a landed commit. (b) Turbo replays a task only on an exact input-hash match, and turbo.json's build hash covers $TURBO_DEFAULT$, the lockfile-resolved dependencies, dependsOn: ["^build"], the root tsconfigs and build scripts, and the globalDependencies .objectstack-sha and node_modules/.cache/objectstack/.stamp.json — so a PR that moves source, a dependency, or the @objectstack pin misses and builds cold. (c) Every gate after the build runs node scripts/*.mjs directly (check:readme-exports is node scripts/check-readme-exports.mjs in package.json), never through turbo, so no gate verdict is ever a cache replay; they read dist/**, which is inside the task's declared outputs. (d) Bundle Analysis already replays this identical 40-task set from main on every PR, so the outputs coverage of the entry is exercised in the tree today. Right.
  5. The restore condition skips no needed build. The restore's if: is the build's own (!cancelled() && steps.install.outcome == 'success'); the build is not conditioned on the restore, so a restore that fails on transport still leaves the build running cold. The two --build-filter derive steps precede the restore and do not read the cache. Right.
  6. Its own prefix, one restore key. turbo-${{ runner.os }}-repo-gates-${{ github.sha }} with the single restore key turbo-${{ runner.os }}-repo-gates-; repo-gates is a prefix of no other job's tag (perf, type-check, …) and none is a prefix of it. On a PR ref the exact key finds nothing and falls through to main's newest entry, as the comment says. Right.
  7. The report step reads what the policy requires. if: ${{ !cancelled() }}, RESTORED_CACHE_KEY: ${{ steps.turbo-cache.outputs.cache-matched-key }}, placed before the save, with TURBO_RUN_SUMMARY set between restore and report; report-turbo-cache.mjs without --prune only prints and exits 0/1 under continue-on-error. Right.
  8. The two pins are honest. check-action-ref-convention.test.ts gains exactly actions/cache/restore@v6 and actions/cache/save@v6, in the head's file order (pinned restore → turbo restore → turbo save → pinned save); nothing is loosened to a count or a prefix. ci-cd-pipeline.md adds one sentence naming scripts/report-turbo-cache.mjs, which ci-cd-pipeline-doc.test.ts's first-party-command rule (both directions, no exception list) requires for the new run: steps, and every claim in the sentence (restore on PR and queue runs, prune-then-save on push, report on every run) matches the YAML. Right.
  9. What the diff does not do. It changes no gate step, no if: of a gate, no concurrency, no trigger, no path filter. The three gates' inputs are the same dist whether built or replayed.

Check-runs on the head (the gate verdicts), all 33 completed: success on Repo Gates, Lint, Type Check, Test and all 16 shards, Test (dist pins), Build & E2E, Build Docs, Changeset Declaration, Changeset Fixed Group Check, Changeset Claim Re-read, Governed Surface Queue Guard, Line Citation Gate, Live E2E (informational), label; skipped only on the coverage lane, its matrix placeholder and dependabot, none a verdict. The head's own Repo Gates run (job 120402878710) carries the new instrument's annotation: build 0/39 replayed · cache 39 entries, 11.1 MB, 0 not used by this run · restored nothing (cold) — a PR run that restored nothing and, by its if:, saved nothing, with the gates green on a cold build, exactly as judgment 2 and flag 5 predict. (The dev's --dry=json count of 40 tasks against the summary's 39 cacheable ones is a difference of one task declared uncached or a root task, not a filter change; the filter line is unchanged.)

② Semver level

No release level. The diff touches a workflow, a test under scripts/__tests__/ and a page under content/docs/; no src/ of a fixed-group package, no files-listed artefact and none of the eight publish-contract fields in any package.json moved, so per objectui AGENTS.md §9 (authoritative text: scripts/check-changeset-presence.mjs) no changeset is owed. The PR body's Clause-②: no line matches the card and the surface revision; Changeset Declaration and Changeset Fixed Group Check are green on the head.

③ Boundary flags

  1. Restore gated on steps.install.outcome == 'success' (dev's judgment call 1) — answered: correct. The restore serves nothing but the build, whose if: is identical; a run whose install failed has no build to replay and would only spend a restore. bundle-analysis has no if: because that job has no conditional steps at all; report-turbo-cache.test.ts asks nothing of the restore's condition. No change requested.
  2. timeout-minutes: 5 borrowed from the perf job (dev's judgment call 2) — answered: accepted. Same 40-task entry (the dev's --dry=json on the base, which this review did not re-run, says the two derived filters add nothing; bundle-analysis builds the same filter), whose measured save is 1 s; the bound is on a continue-on-error bookkeeping step, so its worst case is a longer main push run, never a changed verdict; the job's save bounds stay at 7/30. The comment says to re-derive from this step's own runs — do so once a handful of main pushes have saved.
  3. Push-run cancel-in-progress (the PR body's caveat) — answered: self-healing. A cancelled main run saves nothing (implicit success()), the next main run prefix-restores the previous saved entry, replays what matches and saves its own; the cost is one PR window of partial hits, never a wrong verdict. Not a reason to split the push concurrency group in this PR.
  4. Raised here, not by the dev: a second build-sized entry per main push. bundle-analysis already saves the same 40 hashes under turbo-<os>-perf-<sha>; this PR adds the same bytes again under repo-gates-. Sharing one prefix is ruled out by objectui#12276's one-job-one-prefix policy (the test above enforces it), and the prune keeps each entry to the current outputs, so this is a known cost of the policy rather than a defect — noting it so the pool's eviction is read with both in mind.
  5. Acceptance is unobservable on this head by design. No repo-gates- entry exists until a main push saves one, so this PR's own Repo Gates run restores nothing (its ::notice reads restored nothing (cold)). The card's ≤90 s / ≤4.5 min numbers are to be read off the first PR run after the merge, from the Turbo cache annotation on the check-run — the instrument the diff installs. The lane seat should close the card against that reading, not against this PR's run.

Implemented-by: claude/issue-13348-repo-gates-turbo-cache
Reviewed-by: session_01WJozybDmRH8z2c5SD7vJkj

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

  • 改了什么: 给 Repo Gates 任务加 Turbo 构建缓存:构建前恢复缓存;只有 push 到 main 时才清理并保存。PR 和合并队列的跑只读不写。构建命令和过滤条件一字未改。另外改了两处钉测:一处测试里固定列出的 action 引用清单(多了缓存的恢复和保存两项),一处 CI 文档(写明缓存脚本)。
  • 为什么改: Repo Gates 是唯一一个带构建、却没有缓存的任务,每个 PR、每次 push、每次合并队列都从零构建约 250 秒,整个任务约 7 分钟。快速通道(fix(ci): the affected-tests fast path runs: the shard step compares the output value, not the selector's key=value line (objectui#13350) #13358)生效后,它会成为大多数 PR 最慢的一项。
  • 风险与代价: 门禁步骤本身不经过缓存,直接读构建产物;缓存键覆盖源码、依赖和 pin,只有 main 能写入,所以旧缓存或坏缓存不会让本该失败的门禁通过。缓存步骤都限时,失败也不让任务变红。代价是每次 push 到 main 多存一份约 11 MB 的缓存。回滚就是 revert 这一个提交。
  • 席位意见: 同意合并。fable 档隔离复核 PASS(comment 6364625915),33 项检查全绿。提速要等合并后 main 第一次保存缓存才看得到,本 PR 自己的那次跑是冷启动。
  • 你要做的: 在 PR ci(repo-gates): restore the build's turbo cache, save and prune on push only #13360 上点 Approve。批准后由席位转 ready、进合并队列。

席位核对

Seat: domain:ui#1 · Clause-②: no · head 00fb95e38809f8adfa4957eed3f705786848f753 · 2026-10-23T12:15Z

项 读数
形态 3 个文件:.github/workflows/repo-gates.yml(受治理面,Tier H)、scripts/__tests__/check-action-ref-convention.test.ts、content/docs/guide/ci-cd-pipeline.md。后两个由卡上的文件面修订(comment 6364367391)补入。不欠 changeset。
前提实测 基线 run 39921589107:构建步骤 247 s,整个任务 419 s。两个派生过滤变量在当前 base 上不增加任务,加不加都是 40 个,与 performance-budget 任务构建的集合相同。
门禁 check-runs 33 项全部完成,全部 success(覆盖率、矩阵占位、dependabot 按设计跳过)。本 head 的 Repo Gates 注记为 build 0/39 replayed … restored nothing (cold):说明报告步骤工作正常,PR 的跑既没恢复也没保存。
契约复核 CONTRACT_REVIEW_TIER 隔离复核 PASS,comment 6364625915,Local-runs: none。
接受的偏差 恢复步骤以安装成功为前提,与构建步骤条件相同。保存的 5 分钟上限借用 perf 任务的实测值(同一批 40 个任务,保存耗时 1 s),等跑过几次 main push 再按本步骤自己的数据重定。main 上的 push 在同一 ref 下会相互取消(cancel-in-progress),被取消的那次不保存,下一次会接上,代价只是一段时间命中率偏低。
范围外去向 卡面验收(构建步骤 ≤ 90 s、任务 ≤ 4.5 min)在本 head 上按设计观测不到。合并后,席位用第一个 PR 跑的 Turbo cache 注记来核验,不拿本 PR 的跑判。
落地条款 本 PR 同笔挂 needs-user-decision,保持草稿,等授权批准人(os-zhuang / hotlong,已请审)批准。获批后席位 pr_ready + automerge_enable。⛔ 席位不批准。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 24, 2026 02:08
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 24, 2026 02:08
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit 3688e5c Oct 24, 2026
34 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-13348-repo-gates-turbo-cache branch October 24, 2026 02:21
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

ci/cd configuration documentation Improvements or additions to documentation tests

Projects

None yet

3 participants