Repository navigation
ci(ui-merge): port the four objectui gates that guard published @object-ui packages into root CI - #26854
Conversation
…ct-ui packages into root CI - Build & E2E UI runs check:eager-closure (with the base-branch ceiling freshness step on pull requests) over the Console build it already makes, and test:dist for @object-ui/components over the registry build the SDUI manifest step already ran. - A full-battery job, UI Published Artifacts (push/schedule only), builds what the release builds and runs check:published-dist and the check:node-esm-load load leg over it with --no-build. - scripts/release-publish.sh runs check-published-dist-tooling --no-build before npm's record starts and refuses to publish on a non-zero exit. release-verify-npm battery 16 stubs npm and pins the order, the clean record and the refusal. Claude-Session: https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx Co-authored-by: Claude <noreply@anthropic.com>
…in the full-battery carrier The ruling on objectui#4846 forbade a per-PR full-repo build. The pin read it per workflow FILE, which a full-battery job in ci.yml (a file that subscribes pull_request) cannot satisfy although it costs a pull request nothing. It now reads each job of a per-PR or queue workflow: a job that runs the scan must deny both events in its own if:. The retired "runs nightly" pin comes back against the root job that schedules the scan. Claude-Session: https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx Co-authored-by: Claude <noreply@anthropic.com>
…stall graph sees it check-pre-install-import-graph finds a job's install by its `pnpm install` line and reads every step of a job without one as running before node_modules exists; both gates import typescript. The ui jobs spell their setup out for the same reason. Claude-Session: https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT ·
|
|
Generated by Claude Code |
|
Generated by Claude Code |
Contract reviewServed-tier: Director seat (summon #52, ledger objectstack#12708). This record supersedes 6450870402 on the same head; nothing in the diff changed. That record's one must-fix was wrong: it read ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS Generated by Claude Code |
…-s71sz8 Claude-Session: https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx Co-authored-by: Claude <noreply@anthropic.com>
Requested by Steve · project thread
Part of #26717 (the four "port" rows: 23, 21, 7
test:dist, 22. The five port-or-retire rows 19, 18, 7-docs, 17 and 26 are measured below and left for the next card.)Clause-②: no
Metadata-protocol: none
维护者速读
@object-ui/*包的四个 objectui 检查接回根 CI。Build & E2E UI多跑两步:Console 首屏体积预算(check:eager-closure)和 components 的构建产物测试(test:dist),都复用这个 job 已有的构建。新增一个只在 main 的 push/定时批次跑的 job「UI Published Artifacts」:先跑一次和发布相同的pnpm build,再跑两个检查,一个查发布包里有没有测试文件,一个查每个 ESM 入口能不能被 Node 直接加载。scripts/release-publish.sh在changeset publish之前先扫一遍,扫出问题就拒绝发布。@object-ui/*时也没有经过发布前的那道扫描。Build & E2E UI是必需检查,多出的两步会挡 PR。这两步在 main 上实测是绿的,首屏体积还有 481 KB 余量。新 job 不是必需检查,PR 和合并队列都不跑它。但发布审批前读的全量批次里会有它,它红了算 main-red。回滚就是 revert 本 PR。.github/workflows/ci.yml末尾新增的ui-published-artifactsjob;scripts/release-publish.sh新增的拒发段;ui/scripts/__tests__/check-published-dist-tooling.test.ts改写的接线测试(这一处要你拍板,见决策卡)。node scripts/release-verify-npm.mjs --self-test,预期 169 例全过,其中第 16 组有 12 例,包括「扫出测试文件就拒绝发布、changeset publish 不会运行」。What changes
Rows of the objectstack#25697 workflow roster, ported from the objectui workflows deleted by #26058. Invocations were read at that PR's parent
be789779(published-dist-gate.yml,node-esm-load-gate.yml,performance-budget.yml,ci.yml"Test (dist pins)").check:eager-closureBuild & E2E UI, over the Console build the job already makes. On a pull request, a step before it resolves the base branch's ceiling constants (objectui#6245 freshness half), ported fromperformance-budget.yml.check:eager-closure — resolve the base-branch ceiling constants,check:eager-closure — weigh the Console's eager closure against its budgettest:diston@object-ui/componentsBuild & E2E UI. The root turbo task'sdependsOn: ["build"]replays the registry build the SDUI manifest step already ran.test:dist — run the built-artifact pins of @object-ui/componentscheck:published-distUI Published Artifacts, afterpnpm build, with--no-build. Alsoscripts/release-publish.sh, before npm's record starts, refusing on a non-zero exit.check:published-dist — scan every published @object-ui tarball for tooling filescheck:node-esm-load(load leg)--no-build. It runs whatever the scan above said.check:node-esm-load — import every published ESM entry under plain Nodeui-published-artifacts(UI Published Artifacts):if:is Build & E2E UI's condition plusgithub.event_name != 'pull_request' && github.event_name != 'merge_group', the full-battery spelling. So a pull request and a queue entry skip it,main's push and 4-hourly schedule runs carry it, andscheduled-full-run.mjs --release-gatereads it before a publish approval. It is not a required context. Its setup is spelled out (Node, pnpm, a restore-only store cache,pnpm install --frozen-lockfile) rather than thesetup-workspacecomposite, becausecheck-pre-install-import-graph.mjsreads a job with no literalpnpm installas running entirely beforenode_modulesexists, and both gates importtypescript. It restores the build-core / ui-deps turbo seeds (restore-only).timeout-minutes: 60, objectui's bound for each of the two builds it replaces.pnpm build+--no-build. The gate's own build mode is not the release's build.check:published-distbuildsturbo run build --filter=!@object-ui/sitefromui/, which under the monorepo is 128 tasks including@objectstack/docs; the release buildspnpm build(no docs).check:node-esm-load's build mode runspnpm buildinui/, which has nobuildscript (measured:ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command "build" not found, exit 254). Building once with the release's command and grading with--no-buildis the order the publish path runs the scan in.--no-buildcannot pass vacuously: a package with nothing under its build output isno-build-output, both gates assert a floor, and the ESM gate's provenance leg still refuses an artifact whose turbo log names another tree.scripts/release-publish.shrunsnode ui/scripts/check-published-dist-tooling.mjs --no-buildafter thenpm_config_globalconfigunset and before--start-npm-record, so the scan'snpm pack --dry-runruns never land in the recordrelease-verify-npm.mjsreads back. A non-zero exit prints an::error::and exits 1 beforechangeset publish.ci.ymlnow names four full-battery jobs.Forced test edits
scripts/release-verify-npm.mjsbattery 16 (floor 8 → 12). Its run ofrelease-publish.shwould otherwise run the real scan over an unbuilt tree. It now stubsnpmonPATH(answeringnpm pack --dry-run --jsonwith one build-output file, plus adist/__tests__/*.test.jswhen asked; the stub writes a debug log intonpm_config_logs_dirlike real npm). New cases: the scan packs every published package beforechangeset; npm's record holds only the publish's log; a tarball with tooling material exits 1 withRefusing to publish; and thenchangesetandgitare never called. The existing call-order case filters out thenpm packlines.ui/scripts/__tests__/check-published-dist-tooling.test.ts(own commit, option B on the thread's decision card). The pinis NOT wired into any per-PR workflowread the ruling (objectui#4846 comment 5307574139: no per-PR full-repo build) per workflow FILE, so no job ofci.yml(which subscribespull_request) could carry the scan. It now reads per JOB: in any workflow that subscribespull_requestormerge_group, a job that runs the scan must deny both events in its ownif:, and the loop must find at least one carrier. The retiredruns nightlypin comes back asruns in ci.yml's full battery, after the release's build, with --no-build. If option A or C is chosen instead, this commit is dropped and the job moves or loses its first step.Why
The roster kept these four as "kept, follow-up port", and #26058 deleted their only runners. Since then nothing has run them: 18.2.0 published
@object-ui/*without the published-dist scan, and a Console first-load regression would land unmeasured. Card #26717 has the business case.Tests
Measured on head
f0665932(the final head8090d1fadiffers on this PR's paths only in the new job's setup steps, spelled out instead of the composite; the gate commands, scripts andrelease-publish.share unchanged) in this worktree, after a coldpnpm build(116 tasks, 13m50s underos-verify-lock, shared box):The four gates as the CI steps spell them (all exit 0)
pnpm --dir ui run check:published-dist --no-build: exit 0, 241 s.Inspected 36 published package(s): 5799 tarball file(s), 5654 of them build output, 0 tooling artifact(s) in build output.pnpm --dir ui run check:node-esm-load --no-build: exit 0, 81 s.Provenance leg: 35 of 35 gradable entries were built by this tree.Load leg: 33 of 36 published ESM entries imported and evaluated.(create-plugin is bin-only; plugin-dashboard and plugin-map are the ruled.cssboundary.)VITE_BASE_PATH=/console/ pnpm --dir ui/apps/console exec vite build, thenpnpm --dir ui run check:eager-closure: exit 0.Console eager closure is 1950.2 KB gzipped across 89 of 2392 chunks (budget: 2431.6 KB, headroom: 481.4 KB).Per-chunk, membership and sensitivity halves all ✅; freshness not applicable off a pull request.pnpm exec turbo run test:dist --filter=@object-ui/components: exit 0,Test Files 2 passed (2),Tests 8 passed (8).run:extracted from thisci.yml, run underbash -ewithPR_BASE_SHA=8b276385,BASE_REF=main: exit 0, then the checker withGITHUB_EVENT_NAME=pull_requestand those five variables: exit 0,Ceiling freshness: all 3 ceiling constants are unchanged on main since this checkout's base.node scripts/release-verify-npm.mjs --self-test:OK … 169 cases pass across 16 batteries.pnpm --dir ui exec vitest run scripts/__tests__/check-published-dist-tooling.test.ts: 33 passed.Counter-probes (each defect turned its step red, then was restored)
One locked script, every mutation restored by trap and checked by sha256 or
git diff HEAD:ui/packages/core/dist/probe-26717.test.d.tswritten into a built dist.check:published-dist --no-buildexit 1:@object-ui/core [tooling-in-published-output] packages/core/dist/probe-26717.test.d.ts. The release-path spellingnode ui/scripts/check-published-dist-tooling.mjs --no-buildexit 1 too. Restored (file absent). Battery 16's tooling case is the same refusal throughrelease-publish.sh.export * from './probe26717extless'appended to builtui/packages/react/dist/index.js.check:node-esm-load --no-buildexit 1:✗ @object-ui/components (via @object-ui/react): ERR_MODULE_NOT_FOUND …/react/dist/probe26717extless, 21 lines naming it. Restored, sha matches.eager-closure.jsongrown by 700,000 gzip bytes.check:eager-closureexit 1:❌ Console eager closure is 2633.8 KB gzipped across 89 chunks — 202.2 KB over the 2431.6 KB budget.Restored, sha matches; re-run exit 0.scripts/ablation-replace.mjs,namespace: 'page'→namespace: 'probe26717'onpage:header's registration inui/packages/components/src/renderers/layout/containers.tsx.turbo run test:dist --filter=@object-ui/componentsrebuilt the package and exited 1:× registers page:header from the BUILT bundle. Restored (git diff HEADempty); re-run exit 0.a tarball carrying tooling material fails the scriptandchangeset publish never runscases.CI run ids per gate go on the card once a
mainpush run and this PR's run exist: this PR's run covers the twoBuild & E2E UIsteps;UI Published Artifactsfirst runs on the push that lands this.Acceptance notes
origin/main8b276385: no root workflow runs any of them.lockfile-integrity(check:lockfile-integrity): rootvalidate-deps.ymlhas no duplicate-dependency step (its steps are changeset-fixed, override-consistency, vendor-export-contract-resolve, OSV and outdated), so no overlap to retire it on. One ui test reads the script.lockfile-dedupe(check:lockfile-dedupe): no root workflow runspnpm dedupe --check; tooling surface: 15 check:*/census:* scripts no workflow runs, five censuses only their own tests reach, a 759-line wrapper around pnpm dedupe --check, 23 copy-pasted vite configs (UMD forces inlineDynamicImports), 94 tsconfigs, dead turbo inputs #25920 measured 145 runs / 90 days, 0 failures. One ui test reads it.@object-ui/site: no root workflow builds it, and rootpnpm buildexcludes./ui*/apps/site.live-e2e(test:e2e:live:ci): no root carrier; one ui test readsplaywright.live.config.shadcn-check(shadcn:check): no root carrier; three ui tests readshadcn-sync.check-eager-locale-catalogues.mjs(aliascheck:eager-locale-catalogues),check:sdui-registration-pins, a package size report and a PR comment. None of them runs in root CI today. The card namescheck:eager-closureonly; the rest is next-card material.pnpm --dir ui run check:node-esm-loadwithout--no-buildexits 1 in the monorepo (ui/package.jsonhas nobuildscript). Andcheck:published-dist's own build mode builds@objectstack/docs, unlike the release it claims to match. Both are ui script defects; this PR routes around neither: CI and the release call the gates with--no-buildafter the release's own build. Fixing the scripts' build modes is a ui-lane card (andui/package.jsonis held by open chore(deps)(deps-dev): bump the development-dependencies group across 1 directory with 22 updates #26556).release.yml's comment over the publish step still describesrelease-publish.shaschangeset publishfollowed by one tag push; it now runs the scan first. Not edited here (outside the claimed surface).check-node-esm-load.mjsdocblock says--no-build"is wired into nothing"; this PR wires it. Not edited (ui script, outside the surface).Build & E2E UI(required) gains three steps, about 1 to 2 minutes on top of its measured 312 s run (schedule run 40537439467) against its 35-minute bound.Verification
Final head
8090d1fa(the branch withorigin/main0ea90c7 merged in; no overlapping files). After a freshpnpm build(13m22s), the sweep ran every commandnode scripts/pm/dispatch-gates.mjs --commandsderives for this change set (4 paths) underos-verify-lock(VERDICT command-exit 0, 44m35s).Gate sweep and reconciliation
exit 0. Among them:pnpm --dir ui exec vitest run scripts/(178 files, 5066 tests passed, 2 skipped),node scripts/release-verify-npm.mjs --self-test(169 cases across 16 batteries),pnpm check:published-files, andcheck:pm-dispatch-gates(fast tier, 1935 cases).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran <ledger>:✓ 71 derived famil(ies) accounted for — 71 run, 0 NOT-MEASURED, 0 UNRUN.pnpm lintand the full test suites, which are CI's run.Human Minimum
packages/specholds no runtime logic. (not touched)pnpm lintpasses. (no formatter ran;pnpm lintis CI's run)main, one pull request.skip-changeset)packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx