Repository navigation
tooling(ui-wiring): inert workspace, turbo and CI wiring for the ui/ tree (objectstack#25697 PR A) - #25710
Conversation
…tree (objectstack#25697) pnpm-workspace.yaml gains ui, ui/packages/*, ui/apps/* and ui/examples/*. They match no directory until objectui is imported under ui/, so `pnpm install --frozen-lockfile` and `pnpm install --lockfile-only` leave pnpm-lock.yaml byte-identical and `pnpm -r ls --depth -1` is unchanged. objectui declares no onlyBuiltDependencies, so the union is this list. turbo.json gains the four task names objectui's packages use and the root lacks: type-check, lint, test:dist, test:watch, with objectui's inputs, outputs, dependsOn and cache settings. Its repo-root inputs are re-rooted under $TURBO_ROOT$/ui/. Two dependsOn edges are not carried: //#lint:root and //#type-check:e2e bind objectui's ROOT manifest, which is the `ui` member here, and a package-named dependsOn on a package that does not exist yet makes turbo refuse the whole run. The ui CI jobs run those two programs as their own steps instead. Its globalDependencies (pin plumbing) are not ported. `turbo run build --dry=json` lists the same 84 tasks with the same 84 hashes before and after. Two gates read "turbo.json describes something absent" as a defect, which is right except for a tree that is about to arrive: - check-turbo-task-graph: type-check, lint and test:dist are held by no member until the import. GENERIC_TASKS_AWAITING_UI_IMPORT holds them only while ui/package.json is absent; the hold is void once it exists, and a held key turbo.json does not declare is itself a finding. - check-cross-package-test-inputs: `ui` is a top-level segment git does not track yet. TOP_LEVEL_DIRECTORIES_AWAITING_IMPORT excuses it only while untracked; once tracked it owes a recognised or pending entry. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
… (objectstack#25697) ci.yml's filter job gains three outputs: `ui` (ui/** and the root files the ui jobs build from), `ui_consumers` (the seven packages objectui installs from this tree: spec, client, core, formula, lint, sdui-parser, types) and `ui_tree` (ui/package.json exists, from an unconditional step). Four jobs, ported from objectui's ci.yml and lint.yml: - Type Check UI: objectui's coverage guard, its check:* source gates and root tsc programs, then `turbo run type-check` over the ui packages, affected-only on pull_request / merge_group. Runs on `ui` OR `ui_consumers`, so a spec change compiles the Console in its own PR. - Test UI (n/16): objectui's vitest root, sharded by its own LPT sequencer. N stays 16: by package the 1.3 max/mean bound admits at most 4 legs (app-shell is 104.2 of 325.7 summed minutes); by file the bound never binds and 16 legs carry 20.4 summed minutes each. - Lint UI: objectui's lint job, with //#lint:root as its own step. - Build & E2E UI: the Console build and Playwright chromium, on `ui` OR `ui_consumers`. Every job's `if:` is `!cancelled() && ui_tree != 'false' && <paths>`, so with ui/ absent each one is skipped on every event, never failed and never started. None is a required context or an aggregator member. The .objectstack-pin plumbing is replaced by a turbo build of the seven @objectstack packages; objectui root scripts run with working-directory: ui, turbo runs from the root with directory filters. check-expected-skips declares the four names as by-design skips. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
…o not misread them (objectstack#25697) This repo's workflow gates key `scripts/<x>` and `pnpm <alias>` against the repository root and do not read `working-directory`. Under `working-directory: ui`, objectui's `node scripts/check-entry-guard.mjs` was credited to objectstack's file of the same name, and `scripts/check-lint-coverage.mjs`, which only objectui has, read as a missing script (check-self-test-wired --self-test went red on it). Every objectui command in the ui jobs is now `pnpm --dir ui run <its alias>` or `pnpm --dir ui exec ...`. Test UI drops objectui's per-file timings reporter and its upload: both feed the dataset refresh workflow, which is in the governance-workflow audit, and the sequencer reads the committed dataset meanwhile. check-llms-txt opens every manifest under the workspace globs, so the ui roots are declared as `ui/**/package.json` beside the others. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
…s (objectstack#25697) Three gates hold a hand-written copy of pnpm-workspace.yaml's `packages:` block and red when the live file names a root they do not declare. Each now declares the ui roots, which match nothing until the import: - check-published-files: ROOT_DIR_WATCH_HINTS gains ui/*, ui/packages/*, ui/apps/*, ui/examples/* (ui/* stands for the bare `ui` member, spelled with a separator as the declaration requires). - check-test-source-alias: WORKSPACE_PARENT_GLOBS gains the three `/*` roots; the bare `ui` member is declared as WORKSPACE_BARE_MEMBER_MANIFESTS (`ui/package.json`), walked as the one package at that directory and reconciled against the live file like the globs. - check-undeclared-dep-imports: its parser refused any entry that is not `<dir>/*`, so the bare `ui` entry made the sweep fatal. A bare `<dir>` is now the one package at that directory, absent contributes nothing; two new refusal-battery cases drive both directions, and `ui/**` joins ROOT_DIR_WATCH_HINTS. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
…roots (objectstack#25697) PR A's first CI run turned two gates red that the derived gate list for its paths did not name, both because pnpm-workspace.yaml now declares four globs that match nothing until the import: - check-dispatcher-error-vocabulary refuses any workspace glob that expands to no member (#16649), in production and in its self-test. WORKSPACE_GLOBS_AWAITING_IMPORT excuses the four ui globs ONLY while ui/package.json is absent (awaitingImportGlobs); the self-test holds the map against the live workspace, pins the exact key set, and drives both marker states on a scratch root so the excuse voids itself at the import. The excuse is opt-in at the call site, never a default. - check-duration-unit-keys (packages/spec) mirrors the workspace roots as ROOT_DIR_WATCH_HINTS literals and holds them against the live file in both directions; the four ui roots are declared as `ui/src/**`, `ui/packages/*/src/**`, `ui/apps/*/src/**`, `ui/examples/*/src/**`. Both gates and their self-tests exit 0 on the PR merge commit; every gate the dispatch derivation names for these two files exits 0. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU
|
CI on
Neither gate is in the The approval already on record is unaffected by the push (unpinned since 2026-09-04); auto-merge stays armed. Changed lines are now 1,019 (+1,019 / −33 → 1,052 total), still under the human-merge line. |
Part of #25697 (PR A of two; PR B, which retires the two-repo pin gates, is drafted later and merges in the cutover window). Part of #25677.
What
The objectstack side of importing objectui under
ui/, landed inert: untilui/package.jsonexists, nothing in this PR changes what pnpm installs, what turbo builds, or what CI runs.Workspace.
pnpm-workspace.yamladdsui,ui/packages/*,ui/apps/*,ui/examples/*. objectui declares noonlyBuiltDependencies, so the union with ours is our list unchanged. objectui's overrides and its@radix-ui/react-focus-scopepatch arrive with the import, not here.Root turbo.
turbo.jsonaddstype-check,lint,test:dist,test:watchwith objectui's inputs, outputs, dependsOn and cache settings. Its repo-root inputs are re-rooted under$TURBO_ROOT$/ui/. Two things are not carried over://#lint:rootand//#type-check:e2edependsOn edges. Turbo refuses a whole run over a package-named dependsOn whose package does not exist yet, so those two programs are steps in the ui jobs instead.globalDependencies(pin plumbing).CI jobs in
ci.yml, ported from objectui'sci.ymlandlint.yml:Type Check UITest UI (n/16)Lint UIBuild & E2E UIThe
filterjob gains three outputs:ui(ui/**plus the root files the jobs build from),ui_consumers(the seven packages objectui installs: spec, client, core, formula, lint, sdui-parser, types) andui_tree(whetherui/package.jsonexists, set by a step with noif:). Every job'sif:follows THE FILTER CONTRACT:!cancelled() && needs.filter.outputs.ui_tree != 'false' && PATH_OUTPUTS != 'false'. Withui/absent, every job is skipped on every event. None of them is a required context or an aggregator member.A spec change compiles the Console.
Type Check UIandBuild & E2E UIalso run onui_consumers. On pull_request and merge_group, Type Check UI uses turbo--filter=...[base], so a spec change selects every ui package that depends on spec.Roster audit of objectui's 28 workflows: see the table below.
Gates that had to learn about a tree that has not arrived yet
Several repo gates treat "config describes something absent" as a defect. That reading is correct except for a tree that is about to be imported. Each gate below now declares the ui roots, and each hold is narrow:
check-turbo-task-graph: the generic keystype-check,lintandtest:disthave no holder until the import.GENERIC_TASKS_AWAITING_UI_IMPORTholds them only whileui/package.jsonis absent. A held key that turbo.json does not declare is itself a finding. 7 new self-test cases.check-cross-package-test-inputs:uiis a$TURBO_ROOT$segment that git does not track yet.TOP_LEVEL_DIRECTORIES_AWAITING_IMPORTexcuses it only while it is untracked. Once tracked, it owes a recognised or pending entry.check-published-files,check-test-source-alias,check-undeclared-dep-imports,check-llms-txt: each holds a hand copy of the workspace globs, and each now declares the four ui roots.check-undeclared-dep-importsalso learned the bareDIRworkspace entry; before, it made the whole sweep fatal. 2 new cases.check-expected-skips: the four* UInames are declared as by-design skips.Why objectui commands are spelled
pnpm --dir ui run ALIASThis repo's workflow gates (
check-self-test-wired, the dispatch-gates derivation and others) keyscripts/NAMEandpnpm ALIASagainst this repo's root, and none of them readsworking-directory. Underworking-directory: ui, objectui'sscripts/check-entry-guard.mjswas credited to objectstack's file of the same name.scripts/check-lint-coverage.mjs, which only objectui has, read as a missing script.Test UI shard count
N = 16, chosen the way
scripts/partition-test-shards.mjschooses Test Core's N: max/mean ≤ 1.3, from ui/scripts/test-shard-timings.json (4979 files, 325.7 summed minutes).@object-ui/app-shellalone is 104.2 minutes, so the bound admits at most 4 legs.The suite must run with cwd =
ui/, because the dataset keys are relative to the vitest root.Verification (local, on
issue-25697at 597471c)pnpm install --frozen-lockfile --prefer-offline && git diff --exit-code pnpm-lock.yaml--lockfile-only)pnpm -r ls --depth -1with the base workspace file vs the new oneturbo run build --dry=json, before vs afterpackages/spec/scripts/check-llms-txt.tsis an input of@objectstack/spec#build.turbo run type-check / lint / test:dist / test:watch --dry=jsonui/is absent.actionlint 1.7.7 -shellcheck= .github/workflows/ci.yml(shellcheck not installed)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run in full, pluspnpm check:pm-dispatch-gates--ranreconciliation: 118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN (all with recorded exit codes).pnpm check:type-check-debt(underscripts/pm/os-verify-lock.sh)eslint --no-inline-configon the 7 changed scriptsThrowaway import (local, never pushed, deleted afterwards). Built from this branch plus the #25678 spike's adapted
ui/tree, overrides and patch, then a lockfile recompute. Results:pnpm install: 48 s.pnpm -r ls --depth -1lists 45@object-ui/*packages plusobject-ui-monorepo.turbo run type-check --filter='./ui/packages/types' --dry=jsonresolves@objectstack/spec#build → @object-ui/types#type-check.@objectstack/*type-check and not theuiroot (which would recurse).packages/spec/srcselects 44 ui packages under the affected filter.pnpm --dir ui run type-check:coverage,lint:coverage,check:phantom-depsandcheck:entry-guard --self-testall exit 0.Not verified here, owed by the CI runs on #25697 (a
DO NOT MERGEbranch = this head + the subtree import):ui/present;Type Check UIred on aui/file;check-required-contexts --verify-required-set), which cannot run from a dev seat. The new names are not inREQUIRED_CONTEXTS.Acceptance notes: what the import commit will turn red (measured on the throwaway tree)
None of these is reachable while
ui/is absent. Each needs a decision in the cutover PR (sub-issue 5) or in sub-issue 2:check-turbo-task-graphandcheck-workspace-manifest-cyclesself-tests: theirDECLARED_WATCH_HINTSdo not coverui/**/package.json. They cannot declare it today, because both also red on a pattern that covers zero manifests.check-published-filesself-test:EXPORTS_CENSUS_FLOOR(50) must rise. The live publishable count goes 73 → 112.check-undeclared-dep-imports: 9 findings.@object-ui/consoleimports@objectstack/specand@objectstack/clientbut declares them only as devDependencies.check-type-check-coverage: it cannot parseui/tsconfig.json, which is JSONC.check-cross-package-test-inputsself-test: the trackedui/segment must be recognised or pending, as designed.pnpm build(turbo run build) would includeobject-ui-monorepo#build, which is itselfturbo run build, and the rootpnpm lint(eslint .) would sweepui/.Roster audit (item 5)
ci.yml: Type Check · Test (shard n/16) · Build & E2Eci.yml: Test (dist pins) · coverage lane · Build Docsci.yml: Test (aggregate)ci.yml: Changeset Fixed Group Checklint.ymllive-e2e,lockfile-dedupe,lockfile-integrity,node-esm-load-gate,performance-budget,published-dist-gate,shadcn-checkspec-range-floorsworkspace:^makes the floor the workspace version)governed-surface-guard,half-state-patrol,hook-selftests,labeler,required-check-set-patrolboard-snapshot,changelog,changeset-guard,changeset-presence,changeset-release,check-links,cross-repo-issue-closer,dependabot-auto-merge,docs-deploy,line-citation-gate,merge-queue-head-patrol,repo-gates,test-shard-timings-refreshThe full table, one line per workflow with notes, goes on #25697 as a comment (the seat posts it).
维护者速读
改了什么 —— objectstack 这一侧为导入 objectui(
ui/子目录)预先铺线,全部惰性:在ui/package.json出现之前,pnpm 装什么、turbo 建什么、CI 跑什么,一个字节都不变。具体是 workspace 四条ui通配、根 turbo 四个任务名(type-check/lint/test:dist/test:watch)、ci.yml四个新 job(Type Check UI / Test UI 16 片 / Lint UI / Build & E2E UI,都挂在"ui/树存在"这一个开关上,不存在就 skipped,且都不是必查项),以及 7 个仓内门禁学会这四个还不存在的根目录。附 objectui 28 个 workflow 的去向表。为什么改 —— 让后面那个 2.59 M 行的导入提交只做一件事:
git subtree add。接线先行、并且先在今天的树上过一轮 CI,导入那天就不用边导入边改 CI。spec 一改就编译 Console(Type Check UI 对ui_consumers也触发)是把"objectui 被 spec 改坏"从发版后发现提前到 PR 上发现。风险与代价(含回滚) —— 对现有流水线零影响的证据:锁文件逐字节不变(
--frozen-lockfile过)、turbo run build --dry=json前后同为 84 个任务、121 条派生门禁全部 exit 0、actionlint 过。代价是ci.yml多了 624 行当前全部 skipped 的 job;导入后每个碰ui/的 PR 多跑 16 个测试 runner(与 objectui 今天一样)。回滚 = revert 一个 squash 提交,无发布、无数据副作用。未在本地量到的只有导入后 Type Check UI / Build & E2E UI 的冷热分钟数,靠下一步的 DO NOT MERGE 验证分支量。席位意见 —— 建议批准。这是子单 3 的 PR A;PR B(退役两仓 pin 门禁)单独起草、只在切换窗口合。
你要做的(一个动作) —— 在本 PR 上留一条 APPROVED review;之后由派发席翻 ready、挂 auto-merge 走合并队列。
Landing
Tier H:
.github/workflows/**. Draft until an APPROVED review fromGOVERNED_APPROVERSis on record; then the dispatch seat lands it through the merge queue (maintainer ruling C). No seat flips it ready before that.No changeset: the diff touches workflows, root config,
scripts/**andpackages/spec/scripts/**(not in spec'sfiles[]), so nothing published changes.Changed lines: 964 (931 additions, 33 deletions), under the human-merge threshold.
Clause-②: no
Metadata-protocol: none
🤖 Generated with Claude Code
https://claude.ai/code/session_01RSDL3SFtJaKzcymvCD8RsU