Skip to content

tooling(ui-wiring): inert workspace, turbo and CI wiring for the ui/ tree (objectstack#25697 PR A) - #25710

Merged
os-zhuang merged 5 commits into
mainfrom
claude/project-thread-dwhq6r-25697
Oct 25, 2026
Merged

os-zhuang merged 5 commits into
mainfrom
claude/project-thread-dwhq6r-25697

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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: until ui/package.json exists, nothing in this PR changes what pnpm installs, what turbo builds, or what CI runs.

  1. Workspace. pnpm-workspace.yaml adds ui, ui/packages/*, ui/apps/*, ui/examples/*. objectui declares no onlyBuiltDependencies, so the union with ours is our list unchanged. objectui's overrides and its @radix-ui/react-focus-scope patch arrive with the import, not here.

  2. Root turbo. turbo.json adds 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 things are not carried over:

    • the //#lint:root and //#type-check:e2e dependsOn 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.
    • its globalDependencies (pin plumbing).
  3. CI jobs in ci.yml, ported from objectui's ci.yml and lint.yml:

    • Type Check UI
    • Test UI (n/16)
    • Lint UI
    • Build & E2E UI

    The filter job 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) and ui_tree (whether ui/package.json exists, set by a step with no if:). Every job's if: follows THE FILTER CONTRACT: !cancelled() && needs.filter.outputs.ui_tree != 'false' && PATH_OUTPUTS != 'false'. With ui/ absent, every job is skipped on every event. None of them is a required context or an aggregator member.

  4. A spec change compiles the Console. Type Check UI and Build & E2E UI also run on ui_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.

  5. 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 keys type-check, lint and test:dist have no holder until the import. GENERIC_TASKS_AWAITING_UI_IMPORT holds them only while ui/package.json is absent. A held key that turbo.json does not declare is itself a finding. 7 new self-test cases.
  • check-cross-package-test-inputs: ui is a $TURBO_ROOT$ segment that git does not track yet. TOP_LEVEL_DIRECTORIES_AWAITING_IMPORT excuses 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-imports also learned the bare DIR workspace entry; before, it made the whole sweep fatal. 2 new cases.
  • check-expected-skips: the four * UI names are declared as by-design skips.

Why objectui commands are spelled pnpm --dir ui run ALIAS

This repo's workflow gates (check-self-test-wired, the dispatch-gates derivation and others) key scripts/NAME and pnpm ALIAS against this repo's root, and none of them reads working-directory. Under working-directory: ui, objectui's scripts/check-entry-guard.mjs was 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.mjs chooses Test Core's N: max/mean ≤ 1.3, from ui/scripts/test-shard-timings.json (4979 files, 325.7 summed minutes).

  • By package, @object-ui/app-shell alone is 104.2 minutes, so the bound admits at most 4 legs.
  • By file, objectui's own LPT sequencer gives max/mean = 1.000 at any N, so the job timeout sets N. 16 legs carry 20.4 summed minutes each.

The suite must run with cwd = ui/, because the dataset keys are relative to the vitest root.

Verification (local, on issue-25697 at 597471c)

Check Command Result
Lockfile byte-identical pnpm install --frozen-lockfile --prefer-offline && git diff --exit-code pnpm-lock.yaml exit 0 / exit 0 (also with --lockfile-only)
Workspace unchanged pnpm -r ls --depth -1 with the base workspace file vs the new one identical (84 members + root)
Same build tasks turbo run build --dry=json, before vs after 84 = 84 taskIds. Hashes were identical with the turbo/workspace commit alone. At head, 78 hashes move only because packages/spec/scripts/check-llms-txt.ts is an input of @objectstack/spec#build.
New tasks on today's tree turbo run type-check / lint / test:dist / test:watch --dry=json before: exit 1 "Could not find task"; after: exit 0. No root script and no CI step invokes them while ui/ is absent.
Workflow syntax actionlint 1.7.7 -shellcheck= .github/workflows/ci.yml (shellcheck not installed) exit 0, before and after
Derived gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run in full, plus pnpm check:pm-dispatch-gates 121 commands, all exit 0. --ran reconciliation: 118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN (all with recorded exit codes).
Ledger ratchet pnpm check:type-check-debt (under scripts/pm/os-verify-lock.sh) VERDICT command-exit 0
Lint on changed scripts eslint --no-inline-config on the 7 changed scripts exit 0

Throwaway 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 -1 lists 45 @object-ui/* packages plus object-ui-monorepo.
  • turbo run type-check --filter='./ui/packages/types' --dry=json resolves @objectstack/spec#build → @object-ui/types#type-check.
  • The full-scope filters select 46 ui type-check tasks. They select no @objectstack/* type-check and not the ui root (which would recurse).
  • A probe edit in packages/spec/src selects 44 ui packages under the affected filter.
  • pnpm --dir ui run type-check:coverage, lint:coverage, check:phantom-deps and check:entry-guard --self-test all exit 0.

Not verified here, owed by the CI runs on #25697 (a DO NOT MERGE branch = this head + the subtree import):

  • every new job running green with ui/ present;
  • the spec counter-probe turning Type Check UI red on a ui/ file;
  • cold and turbo-warm minutes for Type Check UI and Build & E2E UI against today's ~13 min hourly Spec Shape Gate;
  • the live required-set sweep (check-required-contexts --verify-required-set), which cannot run from a dev seat. The new names are not in REQUIRED_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-graph and check-workspace-manifest-cycles self-tests: their DECLARED_WATCH_HINTS do not cover ui/**/package.json. They cannot declare it today, because both also red on a pattern that covers zero manifests.
  • check-published-files self-test: EXPORTS_CENSUS_FLOOR (50) must rise. The live publishable count goes 73 → 112.
  • check-undeclared-dep-imports: 9 findings. @object-ui/console imports @objectstack/spec and @objectstack/client but declares them only as devDependencies.
  • check-type-check-coverage: it cannot parse ui/tsconfig.json, which is JSONC.
  • check-cross-package-test-inputs self-test: the tracked ui/ segment must be recognised or pending, as designed.
  • Not measured, but likely: the root pnpm build (turbo run build) would include object-ui-monorepo#build, which is itself turbo run build, and the root pnpm lint (eslint .) would sweep ui/.

Roster audit (item 5)

objectui workflow Disposition
ci.yml: Type Check · Test (shard n/16) · Build & E2E ported in PR A → Type Check UI · Test UI (n/16) · Build & E2E UI
ci.yml: Test (dist pins) · coverage lane · Build Docs kept, follow-up port
ci.yml: Test (aggregate) dropped until the cutover required set
ci.yml: Changeset Fixed Group Check → sub-issue 4
lint.yml ported in PR A → Lint UI (closer-contract step → sub-issue 4)
live-e2e, lockfile-dedupe, lockfile-integrity, node-esm-load-gate, performance-budget, published-dist-gate, shadcn-check kept, follow-up port
spec-range-floors dropped (workspace:^ makes the floor the workspace version)
governed-surface-guard, half-state-patrol, hook-selftests, labeler, required-check-set-patrol covered by an objectstack workflow → sub-issue 4
board-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-refresh → sub-issue 4

The 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 from GOVERNED_APPROVERS is 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/** and packages/spec/scripts/** (not in spec's files[]), 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

…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
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 24, 2026
@github-actions github-actions Bot added size/l ci/cd tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Oct 24, 2026
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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): node scripts/docs-audit/affected-docs.mjs --json 04273b9b2cdcadcc90c3fb44861625bddc91816e → packageMentionDocs.

@os-zhuang
os-zhuang marked this pull request as ready for review October 24, 2026 23:06
@os-zhuang
os-zhuang enabled auto-merge October 24, 2026 23:06
…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
@github-actions github-actions Bot added size/xl and removed size/l labels Oct 24, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

CI on 597471c344: the Lint & Repo Gates lane was red on two gates, everything else green (the four * UI jobs skipped as designed, Test Core still running at the time).

gate why red fix in 7ca50a8a7b
Dispatcher error-code vocabulary guard (check:dispatcher-error-vocabulary) refuses any pnpm-workspace.yaml glob that expands to no member (#16649); the four ui globs match nothing until the import WORKSPACE_GLOBS_AWAITING_IMPORT excuses exactly those four, only while ui/package.json is absent (awaitingImportGlobs); opt-in at the call site, self-test pins the key set, holds every key against the live workspace, and drives both marker states on a scratch root
Duration-shaped spec keys carry their unit in the key name (packages/spec check:duration-unit-keys) mirrors the workspace roots as ROOT_DIR_WATCH_HINTS literals and holds them against the live file in both directions the four roots declared as ui/src/**, ui/packages/*/src/**, ui/apps/*/src/**, ui/examples/*/src/**

Neither gate is in the dispatch-gates --commands derivation for this PR's paths, which is why the local sweep did not catch them. Reproduced on the PR merge commit (refs/pull/25710/merge), both exit 1 before and 0 after; the 80 gate commands the derivation names for the two edited files: 76 exit 0, 4 exit 3 NOT MEASURED (check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content need a built dist/; they do not read either edited file and were green in CI on the previous head). eslint on both files exit 0.

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.

os-zhuang pushed a commit that referenced this pull request Oct 26, 2026
…e Pin Gate, prerelease-pin-watch (#25827)

Part of #25697 (item 6, PR B). PR A (#25710) carried items 1 to 5 and is
merged; the card stays open for the epic PM, who hands this PR to the
cutover.
Blocked-by: #25752 (cutover PR first)
Part of objectstack#25677

Clause-②: no
Metadata-protocol: none

## What this retires

Once `ui/` lives in this repository, the gates that proved the objectui
pin from the outside have nothing left to prove. This PR deletes them:

- `.github/workflows/spec-shape-gate.yml` (**Spec Shape Gate**), with
`scripts/ci/spec-shape-gate.mjs` and
`scripts/ci/spec-shape-retirements.json`.
- The `console-pin` job (**Console Pin Gate**) in `ci.yml`, and the
`console` list and output of its `filter` job.
- `.github/workflows/prerelease-pin-watch.yml` (**Stable release
watch**).
- The `Spec Shape Gate` row of `RELEASE_GATE_WORKFLOWS` in
`scripts/ci/scheduled-full-run.mjs`. The release gate now reads `CI` and
`Lint & Type Check`. `release.yml` and `cut-rc.yml` have no read of
their own (measured); both reach the roster only through
`scheduled-full-run.mjs --release-gate`.

What replaces them is already on `main` from PR A: `Type Check UI` and
`Build & E2E UI` compile and build the Console from `ui/` in the PR that
changes one of the seven packages objectui installs (`ui_consumers`
filter).

The console scripts (`build-console.sh`, `check-console-sha.mjs`,
`check-console-injection.mjs`, `console-spec-probes.mjs`,
`assert-console-spec-injection.mjs`) stay, as the order says. 4a #25745
deletes them with the pin.

## Hit-class table

Census: `git grep -n -E 'spec-shape-gate|Spec Shape
Gate|spec-shape-retirements|prerelease-pin-watch|Console Pin
Gate|console-pin'` at the base `52f3f518e1` gives 169 hits in 33 files.
Also posted on the card:
#25697 (comment)

| Class | Files (hits) | Treatment |
|---|---|---|
| The retired gates themselves | `spec-shape-gate.yml` (15),
`scripts/ci/spec-shape-gate.mjs` (21),
`scripts/ci/spec-shape-retirements.json` (1), `prerelease-pin-watch.yml`
(6) | deleted |
| `ci.yml` job and filter | `ci.yml` (7) | `console-pin` job, `console`
filter list and output deleted; the comments that pointed at them
re-worded |
| Release-gate roster | `scripts/ci/scheduled-full-run.mjs` (19) |
roster row dropped; the self-tests that encoded a three-row roster are
rewritten to the two-row roster with the same assertion shape (none
deleted or skipped); two dated diagnosis fixtures kept verbatim |
| Gates that encode the deleted job or script |
`check-ci-filter-parity.mjs` (5), `check-expected-skips.mjs` (5),
`dispatch-gates.self-test.mjs` (20), `dispatch-gates.mjs` (7) |
filter-parity `console` subject retired with its battery (floor 10 to
9); the `Console Pin Gate` expected-skip row removed and its recorded
runs re-spelled to the live roster; the live-tree job-filter cases
re-pointed at `Build Core`; synthetic specimens kept; tense fixes |
| Orphan the deletion forces | `scripts/check-prerelease-pin-watch.mjs`
(15), `package.json` (2) | deleted, with the `check:spec-shape-gate` and
`check:prerelease-pins` aliases (deviation 1) |
| Instruction prose naming a deleted gate as live | `AGENTS.md` (2),
`docs/releases-maintenance.md` (2) | prose fixes, listed below |
| Comments naming a retired gate as live | `cut-rc.yml:358`,
`docs-deploy.yml`, `lint.yml` (2), `release-coverage-patrol.yml`,
`release.yml:1510`, `check-release-section-coverage.mjs` (2),
`check-self-test-workflow-commands.mjs`,
`check-vendor-version-stamps.mjs` (2), `check-required-contexts.mjs` |
past tense, or a one-paragraph addendum to the existing tombstone |
| Left for 4a #25745, which deletes or rewrites them |
`release.yml:1874` and `:1917`, `cut-rc.yml:383`,
`packages/console/README.md:54`, `check-console-injection.mjs` (6) |
untouched here, so the two PRs do not conflict on them |
| Dated history and fixtures | `docs/audits/*` (5),
`scripts/docs-audit/README.md`,
`scripts/fixtures/merge-queue-triage/*.json` (2), the
`fleet-write/validate.mjs` fixture,
`packages/spec/liveness/dashboard.json:146`, `pnpm-workspace.yaml:184`,
the #9533 tombstone in `check-required-contexts.mjs` | kept as written |

## Tier H prose touched

- `AGENTS.md` line 533: drops `Console Pin Gate` from the list of
full-battery jobs.
- `AGENTS.md` lines 1103 to 1104: the sibling-removal rule named the
full battery's `Console Pin Gate` as what reds a breaking removal on
`main`. It now names `Type Check UI`, which compiles the Console under
`ui/` in the PR that makes the removal. Line count unchanged (1117, the
ratchet ceiling).
- `docs/releases-maintenance.md`: the "What still checks the pin"
paragraph is now "What used to check the pin", and the release-gate
roster reads `CI`, `Lint & Type Check`.

No ADR is touched.

## Required contexts that vanish

**None.** Read at the time of this PR: the `main` ruleset requires
`TypeScript Type Check`, `Test Core`, `Build Core`, `Lint & Repo Gates`,
`Governed Surface Queue Guard`, and classic branch protection requires
`TypeScript Type Check`, `Build Core`, `Test Core`, `Dogfood Regression
Gate`. Neither `Spec Shape Gate` nor `Console Pin Gate` is in either
list, so the sitting needs no ruleset change for this PR. `node
scripts/check-required-contexts.mjs --verify-required-set` exits 0: the
registry and the live set agree in both directions.

## Landing with 4a #25745

This PR and 4a are two halves of one change and land as a batch, 4a
first:

- **On `main` alone this PR is red in one place.** The order keeps
`check-console-injection.mjs` and `console-spec-probes.mjs` until 4a and
deletes `Console Pin Gate`, their only PR-time runner. Two live-tree
cases of the dispatch-gates fast tier then red: the silent load-break
pair `check:console-injection` from `console-spec-probes.mjs`, and the
`pull_request`-only narrowing of the scheduled-only zero. That step
belongs to `Lint & Repo Gates`. With 4a merged first, the same tier
passes 1917 of 1917 (verification record below). Neither case was
weakened.
- **Two files overlap.** `package.json` (both PRs delete the
`check:prerelease-pins` alias) and one comment in
`scripts/pm/dispatch-gates.self-test.mjs` near line 1006. Take the
`main` side (4a) in both; checked on the local merge below.

## Deviations from the order

1. `scripts/check-prerelease-pin-watch.mjs` and its
`check:prerelease-pins` alias are deleted here as well. Once the
workflow is gone the script has no CI caller and `check-self-test-wired`
reds the orphan. 4a deletes the same file, so the two deletions agree.
2. `prerelease-pin-watch.yml` does not watch the objectui pin. It
watched better-auth prerelease overrides in `pnpm-workspace.yaml`, and
its watch list was already empty. It is deleted as ordered.
3. Beyond the order's file list, these gates had to follow the deletion
so they stay true: `check-ci-filter-parity.mjs`,
`check-expected-skips.mjs`, `dispatch-gates.self-test.mjs`. Each keeps
its assertion shape; nothing is skipped or loosened.

## Verification record

Head `1776dbd798a4ebde5abc974e5b8d01c6db3fd3ef`, base `52f3f518e1`.

- **Derived gate sweep** (`node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`): 86 derived, 82 run. 81 exit 0.
`pnpm check:pm-dispatch-gates` exits 1 (the two cases above, 2 of 1917).
4 exit 3, prerequisite not met, because they need every workspace dist
built: `check:dts-closure`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure`, `check:sourcemap-no-sources-content`. `Build
Core` runs them on this PR. `dispatch-gates.mjs --ran`: 86 accounted, 0
unrun.
- **Named by the order:** `node scripts/ci/scheduled-full-run.mjs
--self-test` 0, `pnpm check:shard-attestation` 0, `pnpm --filter
@objectstack/spec check:liveness` 0, `check-required-contexts.mjs
--verify-required-set` 0.
- **Merged tree** (`main` `abebc4e14d` + 4a `4ac569dc00` + this PR,
local, conflicts resolved to the `main` side): `pnpm
check:pm-dispatch-gates` exit 0 (fast tier 1917 of 1917). The same
86-command sweep on the merged tree: 80 exit 0, 6 exit 3 (prerequisite
not met: the throwaway worktree has no package dists or package-level
installs, so `check:doc-formula-expressions` and `check:type-check-debt`
join the four above; both exit 0 on this branch), no finding.

```text
exit  command (this branch, head 1776dbd)
0  node scripts/check-aggregator-roster.mjs
0  node scripts/check-aggregator-roster.mjs --self-test
0  node scripts/check-ci-filter-parity.mjs
0  node scripts/check-ci-filter-parity.mjs --self-test
0  node scripts/check-closing-keyword-parity.mjs
0  node scripts/check-closing-keyword-parity.mjs --self-test
0  node scripts/check-comment-mask-corpus.mjs
0  node scripts/check-declaration-mirrors.mjs
0  node scripts/check-declaration-mirrors.mjs --self-test
0  node scripts/check-dts-emitted.mjs --self-test
0  node scripts/check-merged-branch-reaper-outcome.mjs
0  node scripts/check-merged-branch-reaper-outcome.mjs --self-test
0  node scripts/check-position-name-fold-loaders.mjs
0  node scripts/check-position-name-fold-loaders.mjs --self-test
0  node scripts/check-release-section-coverage.mjs
0  node scripts/check-release-section-coverage.mjs --self-test
0  node scripts/check-release-section-coverage.mjs --strict
0  node scripts/check-scripts-symbol-anchors.mjs
0  node scripts/check-scripts-symbol-anchors.mjs --self-test
0  node scripts/check-self-test-wired.mjs
0  node scripts/check-self-test-wired.mjs --self-test
0  node scripts/check-self-test-workflow-commands.mjs
0  node scripts/check-self-test-workflow-commands.mjs --self-test
0  node scripts/check-step-collectors.mjs
0  node scripts/check-step-collectors.mjs --self-test
0  node scripts/check-whole-set-label-write.mjs
0  node scripts/check-whole-set-label-write.mjs --self-test
0  node scripts/ci/assert-turbo-remote-cache.mjs --self-test
0  node scripts/ci/prune-turbo-cache.mjs --self-test
0  node scripts/ci/scheduled-full-run.mjs --self-test
0  node scripts/docs-audit/check-drift-comment.mjs
0  node scripts/pm/bare-root-worklist.mjs --self-test
0  node scripts/pm/ci-failure.mjs --self-test
0  node scripts/release-dist-tags.mjs --self-test
0  node scripts/release-verify-npm.mjs --self-test
0  node scripts/render-release-coverage-anchor.mjs --self-test
0  pnpm --filter @objectstack/lint run check:doc-formula-expressions
0  pnpm check:agent-test-spelling
0  pnpm check:bash32-floor
0  pnpm check:cli-command-ids
0  pnpm check:cross-package-test-inputs
0  pnpm check:declared-population-live
0  pnpm check:doc-authoring
0  pnpm check:docs-audit-scope
0  pnpm check:driver-memory-census
0  pnpm check:entry-guard
0  pnpm check:gitlink-declared
0  pnpm check:manifest-repository-directory
0  pnpm check:merge-driver
0  pnpm check:node-version
0  pnpm check:nul-bytes
0  pnpm check:parse-guard
0  pnpm check:pm-close-cards
0  pnpm check:pm-expected-skips
0  pnpm check:pm-fleet-write-dispatch
0  pnpm check:pm-fleet-write-execute
0  pnpm check:pm-fleet-write-validate
0  pnpm check:pm-governed-merges
0  pnpm check:pm-governed-prose
0  pnpm check:pm-issue-transfer
0  pnpm check:pm-post-stamped
0  pnpm check:pm-record-recognisers
0  pnpm check:pm-skill-id-lint
0  pnpm check:pm-skill-ratchet
0  pnpm check:pnpm-acquisition
0  pnpm check:pnpm-filter-targets
0  pnpm check:pnpm-flag-spellings
0  pnpm check:ratchet-remedy-authority
0  pnpm check:refd-timer-probe
0  pnpm check:required-contexts
0  pnpm check:select-gate-families
0  pnpm check:shard-attestation
0  pnpm check:stall-guard-budget
0  pnpm check:stall-guard-headroom
0  pnpm check:turbo-task-graph
0  pnpm check:type-check-coverage
0  pnpm check:type-check-debt
0  pnpm check:vendor-version-stamps
0  pnpm check:watch-hint-literal
0  pnpm check:workflow-status-functions
0  pnpm check:workflow-step-name-quoting
1  pnpm check:pm-dispatch-gates
3  pnpm check:dts-closure
3  pnpm check:dual-build-cjs-loads
3  pnpm check:lean-entry-closure
3  pnpm check:sourcemap-no-sources-content
```

## 维护者速读(草稿)

**改了什么**:删掉三个只为「跨仓库钉版本」存在的门禁:Spec Shape Gate(每小时拉 objectui main
编译一遍)、Console Pin Gate(按 `.objectui-sha` 克隆 objectui
构建控制台)、prerelease-pin-watch(盯 better-auth
预发布版本,清单早已为空)。发布前的全量电池名单从三个工作流减为 `CI` 与 `Lint & Type Check`。AGENTS.md
两处、发布维护文档一处把它们改为过去时。

**为什么改**:objectui 合入 `ui/` 之后,控制台在同一个 PR 里由 `Type Check UI`、`Build & E2E
UI` 编译构建,规格一改就当场验证,不再需要隔一小时去克隆另一个仓库。留着旧门禁只会对着已不存在的钉版本报错。

**风险与代价(含回滚)**:不影响必需检查(这三个都不在必需集合里)。必须在 4a #25745 之后合入:单独合入时 `Lint &
Repo Gates` 会因控制台脚本失去 PR 期运行者而红两条,4a 删除这些脚本后即消失。回滚就是还原此 PR,三个工作流与脚本原样恢复。

**席位意见**:

**你要做的**:切换当天在 4a 之后合入本 PR(Tier H,需你批准)。规则集无需改动。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01H835wAyqCCeF7jiYq7XSxi)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants