Skip to content

ci: build the pinned objectui SHA on the PR that moves it (#4290) - #4310

Merged
os-zhuang merged 1 commit into
mainfrom
claude/objectui-pin-bump-pr-gate-7q2dwa
Jul 31, 2026
Merged

ci: build the pinned objectui SHA on the PR that moves it (#4290)#4310
os-zhuang merged 1 commit into
mainfrom
claude/objectui-pin-bump-pr-gate-7q2dwa

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Closes #4290.

The gap

.objectui-sha is the single source of truth for the vendored Console SPA: release.yml reads it, clones objectui at that commit, builds @object-ui/console and copies the dist into packages/console/. Editing that one line replaces the entire frontend the platform ships — and nothing on the PR side ever built it.

Confirmed the issue's report against main:

  • The pin appears in exactly two workflows, neither a gate: release.yml (post-merge, on the main push) and showcase-smoke.yml (manual + nightly, whose own header says "it never gates PRs").
  • It is a root dotfile, so it matches neither of ci.yml's core (packages/**, examples/**, apps/!(docs)/**, package.json, pnpm-lock.yaml, tsconfig.json, .github/workflows/ci.yml) nor docs filter. A pin-only diff therefore skipped Build Core, Test Core, Build Docs and the Dogfood gate as well — chore(console): refresh the vendored objectui pin to bebaebd39ace #4288 merged a 76-commit pin bump with six of fourteen checks skipped.

A SHA that cannot build — a typo, a commit that was force-pushed away, a genuinely broken objectui, a dead bundle canary — reached main green and would have surfaced at publish time. The only thing between the two was the author remembering to run scripts/build-console.sh locally: discipline, not a gate.

The change

ci.yml gains a console paths filter (.objectui-sha, scripts/build-console.sh, scripts/check-console-sha.mjs, .github/workflows/ci.yml) and a Console Pin Gate job that builds @object-ui/console at the pin against this tree's @objectstack/client, then runs pnpm check:console-sha.

Two details that keep it honest rather than merely green:

  • It reuses release.yml's cache key (hashFiles('.objectui-sha', 'scripts/build-console.sh')). Actions caches are repo-scoped, so a PR reads whatever main already built for this pin — only a pin the repo has never built misses, and a miss is exactly when the gate has work to do. Watching the workflow and the drift guard therefore costs ~a minute, not a full vite build.
  • The restore/save pair is split, where release.yml uses the combined action, because the combined post-step saves even when the job failed. build-console.sh writes the SHA stamp (line 175) before it asserts the bundle canary (line 178), so a canary failure leaves a stamped-but-broken dist that would be cached, restored by the next run, and waved through the stamp check. Saving only once every assertion is green is what lets a restored dist carry the same guarantees as a freshly built one.
  • A separate Assert a real Console dist is in place step runs before the drift check, because check:console-sha deliberately exits 0 when there is no dist at all and only warns when a dist carries no stamp. Both are correct for that script and both would be vacuous passes here, where producing a dist is the entire point.

Scope, stated plainly

This proves the pinned SHA builds. It does not cover a packages/client change breaking the injected-client bundle — that input lives under packages/**, and watching it here would rebuild the console on a large fraction of every PR. release.yml remains the only check for that direction; the job comment says so rather than overclaiming.

Cost

Only a moved pin pays the full clone + vite build, and #4288's 76 commits of staleness are the measure of how rare that is. The two self-trigger entries (this workflow, the drift guard) leave the pin untouched and hit the dist cache.

The console filter follows the "a change to the guard runs the guard" rule the repo applies to every other filtered gate — deliberately, given #4291's finding that hand-maintained filters drift and go dormant (#2584, #3855). Unlike that generated filter, this one's input set is a single pin file plus its builder, which is literally release.yml's existing cache key rather than a duplicated ledger; a cross-reference comment now points each at the other.

The job lives in ci.yml rather than a standalone workflow on purpose: a paths:-filtered workflow publishes no check context when it doesn't match, which is the #3622 merge-deadlock. Gated on the filter job instead, the job publishes a skipped context on every PR and can safely be made required.

Verification

Both of the gate's paths were exercised, not just the one this PR happens to take.

Restore path — this PR's own CI run (job). The pin is unchanged here, so the key matched what main's Release job had already cached, proving the cross-workflow key reuse works:

CACHE_HIT: true
✓ Console dist present (27856 KB, via the dist cache restore) from objectui@bebaebd39ace
✓ Console dist matches the objectui pin (objectui@bebaebd39ace).

48 seconds end to end (05:17:18 → 05:18:06), which is the "watching this workflow is nearly free" claim measured rather than asserted. All 16 checks on the PR are green.

Build path — run locally from a clean clone at the current pin, since CI could only take the cache branch:

✓ Bundle canary 'import/jobs' present — framework client is in the bundle.
✓ @objectstack/console dist ready (45932 KB) from objectui@bebaebd39ace
✓ Console dist present (45932 KB, via scripts/build-console.sh) from objectui@bebaebd39ace
✓ Console dist matches the objectui pin (objectui@bebaebd39ace).

(45932 KB matches the ~45900 KB reported in #4290. The restored dist's du figure differs because it is measured on a different filesystem; provenance is what the stamp check pins down, and that agrees.)

Also checked:

  • Both workflows parse; console-pin resolves to the 14 steps intended, with if: needs.filter.outputs.console == 'true' matching the build-core/test/dogfood pattern already in the file. The filter job correctly set console=true for this PR and dispatched the job.
  • pnpm exec turbo run build --filter=@objectstack/client... resolves to client + its 26-package dependency closure and produces packages/client/dist/index.mjs — the artifact build-console.sh probes. It is pre-built rather than left to the script's own fallback, because that fallback is a bare pnpm build in packages/client that tsup cannot finish without @objectstack/core/@objectstack/spec built; both existing callers pre-build for the same reason.
  • The assert step was exercised under bash -e across four states — empty restore, healthy dist, dist-without-stamp (the vacuous-pass case), and empty assets/ + zero-byte index.html — failing on three and passing on one, with no set -e short-circuit in the healthy path.

Empty changeset — CI and docs only, releases nothing.

Follow-up for a maintainer

Console Pin Gate still needs adding to branch protection's required checks. Until then it reports but does not block. The job comment flags that its name is the contract, the same trap the dogfood shards note.

`.objectui-sha` is the single source of truth for the vendored Console SPA:
release.yml reads it, clones objectui at that commit, builds
`@object-ui/console` and copies the dist into `packages/console/`. Editing that
one line replaces the entire frontend the platform ships — and nothing on the
PR side ever built it.

The pin appeared in exactly two workflows, neither a gate: release.yml
(post-merge, on the main push) and showcase-smoke.yml (manual + nightly, whose
own header says "it never gates PRs"). It is also a root dotfile, so it matched
neither of ci.yml's `core` and `docs` filters — #4288 moved the pin 76 commits
with six of fourteen checks skipped, including Build Core, Test Core, Build
Docs and the Dogfood gate. A SHA that cannot build reached `main` green and
would have surfaced at publish time; the only thing between the two was the
author remembering to run `scripts/build-console.sh` locally.

Adds ci.yml's `Console Pin Gate` — a `console` paths filter over
`.objectui-sha`, `scripts/build-console.sh` and the two files that gate them,
plus a job that builds `@object-ui/console` at the pin against this tree's
`@objectstack/client` and then runs `pnpm check:console-sha`.

Two details that keep it honest rather than merely green:

- It restores `packages/console/dist` under release.yml's existing cache key
  (`hashFiles('.objectui-sha', 'scripts/build-console.sh')`), so only a pin the
  repo has never built misses — and a miss is exactly when the gate has work to
  do. Watching the workflow and the drift guard therefore costs about a minute,
  not a full vite build.
- The restore/save pair is split, where release.yml uses the combined action,
  because the combined post-step saves even when the job failed.
  `build-console.sh` writes the SHA stamp before it asserts the bundle canary,
  so a canary failure leaves a stamped-but-broken dist that would be cached,
  restored, then waved through the stamp check. Saving only once every
  assertion is green is what lets a restored dist carry the same guarantees as
  a freshly built one. A separate step asserts a real dist is on disk first,
  since `check:console-sha` deliberately exits 0 when there is no dist at all.

Scope, stated plainly: this proves the pinned SHA builds. It does not cover a
`packages/client` change breaking the injected-client bundle — that input lives
under `packages/**`, and watching it here would rebuild the console on a large
fraction of every PR. release.yml remains the only check for that direction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0165VP1PnZDTvBrKwjC7jm3D
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 5:17am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd tooling size/m labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/console.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/north-star.mdx (via packages/console)
  • content/docs/deployment/single-project-mode.mdx (via packages/console)
  • content/docs/deployment/vercel.mdx (via @objectstack/console)
  • content/docs/releases/implementation-status.mdx (via packages/console)
  • content/docs/releases/index.mdx (via @objectstack/console)
  • content/docs/releases/v12.mdx (via @objectstack/console)
  • content/docs/releases/v16.mdx (via @objectstack/console)
  • content/docs/releases/v9.mdx (via @objectstack/console)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 05:32
@os-zhuang
os-zhuang merged commit 873ecd0 into main Jul 31, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/objectui-pin-bump-pr-gate-7q2dwa branch July 31, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An objectui pin bump reaches main without any PR gate ever building the pinned SHA

2 participants