Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
076bb90
release(ui-merge): one fixed group, an in-workspace Console build, no…
claude Oct 25, 2026
b35ec35
release(ui-merge): workflows, root scripts and the CLI remedy follow …
claude Oct 25, 2026
5c49f1b
release(ui-merge): gate self-tests stop reading the deleted pin scrip…
claude Oct 25, 2026
bb52434
release(ui-merge): gates, release notes and docs stop naming the dele…
claude Oct 25, 2026
f3340ff
release(ui-merge): the objectui-side half as a patch for the cutover …
claude Oct 25, 2026
fca4f55
release(ui-merge): the lockstep count reads 112 and spec's repo-test …
claude Oct 25, 2026
ea18e50
docs(console): the README names ui/apps/console as a code span, not a…
claude Oct 25, 2026
a5f4c93
Merge origin/main into claude/project-thread-a0vh3h (objectstack#2571…
claude Oct 25, 2026
60fd853
release(ui-merge): the VS Code extension stays in the one fixed group…
claude Oct 25, 2026
4ac569d
Merge origin/main into claude/project-thread-a0vh3h (objectstack#2571…
claude Oct 25, 2026
5e616fc
Merge origin/main into claude/project-thread-a0vh3h (objectstack#2571…
claude Oct 25, 2026
9a776c6
fix(release): keep the now-private @object-ui/runner out of the fixed…
claude Oct 25, 2026
d6ee25e
docs(ui-import): steps 1-2 run on the cutover tree as it now stands (…
claude Oct 25, 2026
50bd417
docs(ui-import): leave objectui's pending changesets to #25831 (objec…
claude Oct 25, 2026
e2db258
Merge origin/main into claude/project-thread-a0vh3h (objectstack#25711)
claude Oct 25, 2026
fb110c2
feat(release): apply 4a's ui/ half on the merged tree (objectstack#25…
claude Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/25711-console-built-in-workspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/console': patch
'@objectstack/cli': patch
---

build(console): `@objectstack/console` is built in this workspace from `ui/apps/console`, and the Console no longer arrives through a pinned objectui commit

Clause-②: no
Metadata-protocol: none

- **The package.** `@objectstack/console` ships the same `dist/` it shipped at 18.1.0: the built Console (`index.html`, `assets/`, the manifests it served). Two files leave the tarball: `dist/.objectui-sha`, which named the objectui commit the Console was built from, and `dist/.objectstack-injection.json`, which recorded the spec and client dists injected into that build. Both described a pin that no longer exists: the Console is built from the `ui/` tree of the same commit as the framework, so its version is the framework's version.
- **The CLI.** When a framework checkout has no built Console, `objectstack serve` / `objectstack dev` and the 503 at `/_console/` now name `pnpm console:build`, which builds `ui/apps/console` and copies its dist into `packages/console`. The remedy used to be `pnpm objectui:build`, which cloned objectui at the pinned commit.
- **One version.** The `@object-ui/*` packages and the private VS Code extension join the `@objectstack/*` fixed group, so every public package of both scopes publishes at one version and the extension ships at it.

What to do: nothing as a consumer; install `@objectstack/console` as before. In a framework checkout, run `pnpm console:build` where you ran `pnpm objectui:build`.
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@
"@object-ui/react",
"@object-ui/react-runtime",
"@object-ui/sdui-parser",
"@object-ui/types"
"@object-ui/types",
"object-ui"
]
],
"linked": [],
Expand Down
200 changes: 33 additions & 167 deletions .github/workflows/cut-rc.yml

Large diffs are not rendered by default.

25 changes: 0 additions & 25 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3064,31 +3064,6 @@ jobs:
continue-on-error: true
run: pnpm check:startup-registry-verdict

# objectui pin-changeset guard (#4731). A frontend-only refresh reaches the
# platform release record ONLY through the changeset `bump-objectui.sh`
# writes, and that list used to be guessed off objectui's commit subjects
# (`feat|fix` + a silent `head -40`). On one real range the guess dropped
# all 13 releasing non-feat/fix commits — every breaking `refactor(...)!`
# among them — and pulled in 5 that release nothing. The list and the bump
# level now come from what objectui DECLARED (`.changeset/*.md` added over
# the range; empty frontmatter = release-nothing). This self-test builds
# throwaway git repos carrying those exact shapes, runs the real scripts
# over them, and asserts the emitted changeset — including that a cap which
# fires announces itself and that an unwalkable range degrades loudly.
# #4843 folded `scripts/objectui-range.mjs` — the release page's Console
# section — onto that same `classifyRange()` criterion, so both self-tests
# run here: one criterion, two consumers, no room to drift apart.
- name: objectui pin-changeset digest guard
id: objectui-pin-changeset-digest-guard
continue-on-error: true
run: pnpm check:objectui-changeset

# Rationale: `scripts/bump-objectui.selftest.sh` header (#10797).
- name: objectui pin write-ordering guard
id: objectui-pin-write-ordering-guard
continue-on-error: true
run: pnpm check:objectui-bump

# Changeset-family gate self-tests (#6509). The SELF-TEST halves only —
# the real scans stay in pr-automation.yml's `changeset-check`, and the
# split is the whole point of this step.
Expand Down
118 changes: 18 additions & 100 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1835,109 +1835,26 @@ jobs:
run: node scripts/check-changeset-fixed.mjs

# ──────────────────────────────────────────────────────────────────────
# ⛔ TOMBSTONE — the #3340 pin-currency gate that used to sit here is gone
# (2026-08-20 ruling, #10134). Do not re-add one.
# ⛔ TOMBSTONE — no objectui pin is read, built or checked here, and none
# is re-added (objectstack#25711, D1 ruling 「同一个版本号组」).
#
# It compared `.objectui-sha` against objectui `main` at publish time and
# refused a release whose pin lagged. The ruling removed the question, not
# just the job: WHICH objectui revision this repo pins is a decision taken
# in an objectstack issue, never derived from another repo's HEAD, so
# "the pin is behind main" is not a defect a release lane may diagnose.
#
# R4's lesson (#6170 — "a gate required on a PR the publishing lane can
# skip is not a gate") is untouched and does not resurrect this one:
# #3340's actual invariant is "everything published is covered by the
# changeset record", and that is carried by `scripts/bump-objectui.sh` +
# `scripts/objectui-changeset-digest.mjs` at BUMP time — on the manual pin
# PR, which is now the only way the pin ever moves. Publishing an old
# console is therefore a published decision, not an unnoticed gap.
# The Console is a workspace package now: `ui/apps/console`
# (@object-ui/console) builds against this tree's @objectstack/* through
# `workspace:^`, and `@objectstack/console#build:dist` (root turbo.json)
# copies that dist with the tracked sdui.manifest.json; `pnpm run release`
# runs it (`pnpm run console:build`) after the build below. The
# pin-currency gate, the vendored-dist cache keyed on `.objectui-sha`,
# its SHA stamp check and
# its spec-injection check are gone with the pin: each answered "was the
# Console built from the objectui commit, and against the spec, this
# release names", and both are true by construction when the Console
# source and the spec are one tree at one commit. The single-zod canary
# survives inside packages/console/build-dist.sh, on the bytes it ships.
# ──────────────────────────────────────────────────────────────────────

- name: Build
run: pnpm run build

- name: Read the spec's declared zod range (Console dist key input)
id: spec-zod
# Fails rather than keying on an empty or `undefined` range: a key that
# silently stopped carrying the range would restore across a zod move.
run: |
node -e '
const range = require("./packages/spec/package.json").dependencies?.zod;
if (typeof range !== "string" || range.trim() === "") {
console.error("packages/spec/package.json declares no dependencies.zod, so the Console dist key cannot carry it.");
process.exit(1);
}
console.log("range=" + range.trim());
' >> "$GITHUB_OUTPUT"

# This key hashes the pin and the build script, and carries the spec's
# DECLARED zod range (the step above). ci.yml's Console Pin Gate (#4290)
# hashes a SUPERSET of the files: those two, the probe scripts and the
# spec's entry layout (#20765). So the two workflows no longer share dist
# entries, and each one builds its own on a miss. Keeping this key narrower
# is deliberate.
# Following ci.yml would rebuild the console on every Version Packages
# merge, and publishing a cached console whose spec content lags is the
# existing cache design.
#
# The zod range is the one spec input the cached dist is NOT allowed to
# lag (#21108). The console bundles a single zod instance, the console's
# own copy, and its build refuses a console zod outside the injected spec's
# declared `zod` range (objectui#11353). A dist restored after that range
# moved would publish a console the build never judged against it. The
# range is read out of packages/spec/package.json, not hashed with it: the
# file's `version` moves on every Version Packages merge, and hashing the
# whole file would turn every publish into a cold console rebuild.
#
# Split restore/save, matching ci.yml, and not the combined actions/cache
# action: the combined form's post-step saves even when the job FAILED,
# and build-console.sh writes the SHA stamp BEFORE it asserts the bundle
# canary — so a canary failure here would seed this shared, repo-scoped
# key with a stamped-but-broken dist that later runs restore and sail
# through. See ci.yml's split-restore note for the full rationale.
- name: Restore vendored Console dist (keyed on the objectui pin)
id: console-dist-cache
uses: actions/cache/restore@v6
with:
path: packages/console/dist
key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}

- name: Build vendored @objectstack/console SPA
# Clones objectstack-ai/objectui at the SHA pinned in .objectui-sha,
# builds @object-ui/console, and copies dist/ into
# packages/console/dist/. Must run before publish so the prepublishOnly
# guard in @objectstack/console passes.
if: steps.console-dist-cache.outputs.cache-hit != 'true'
run: bash scripts/build-console.sh

- name: Verify Console dist stamp matches pin
run: pnpm check:console-sha

# The spec-injection half of the same question, spelled exactly as
# ci.yml's Console Pin Gate spells it — both consumers of one cache key
# apply one check. check:console-sha answers only "which objectui SHA",
# and scripts/assert-console-spec-injection.mjs runs INSIDE
# build-console.sh, which the `if: cache-hit != 'true'` above skips — so
# on a cache hit nothing had asked whether the dist this job is about to
# PUBLISH bundles this tree's @objectstack/spec rather than the published
# tarball. --require-stamp because the vacuity check:console-sha tolerates
# (no dist, or a dist no build ever stamped) is wrong on a publish lane.
- name: Verify the restored Console dist bundles this tree's spec
env:
CONSOLE_DIST_CACHE_KEY: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}
run: pnpm check:console-injection --require-stamp

# Reached only with every console assertion above green: Actions steps
# default to `success()`, and nothing between the restore and here carries
# `if: always()` or `continue-on-error`. Saving only on a miss keeps an
# immutable entry from being rewritten on every release run.
- name: Save vendored Console dist
if: steps.console-dist-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: packages/console/dist
key: ${{ runner.os }}-console-dist-${{ hashFiles('.objectui-sha', 'scripts/build-console.sh') }}-zod-${{ steps.spec-zod.outputs.range }}

- name: Downstream backward-compat smoke (live hotcrm)
# Pre-publish gate (#2035): the about-to-publish @objectstack/spec must
# not break a real third-party consumer pinned to a published release.
Expand Down Expand Up @@ -2031,9 +1948,10 @@ jobs:
run: bash scripts/release-spec-changes.sh --verify

# ──────────────────────────────────────────────────────────────────────
# The publish itself. `pnpm run release` = build + build-console +
# scripts/release-publish.sh, which is `changeset publish` followed by ONE
# atomic `git push origin --tags` (#2191: the action's concurrent per-tag
# The publish itself. `pnpm run release` = build (both scopes; the turbo
# cache makes it the build above) + console:build (the Console dist into
# packages/console) + scripts/release-publish.sh, which is `changeset
# publish` followed by ONE atomic `git push origin --tags` (#2191: the action's concurrent per-tag
# pushes raced GitHub's ref backend and lost ~half the tags).
#
# changesets/action is NOT used here, and that is deliberate: handed a
Expand Down
18 changes: 7 additions & 11 deletions .github/workflows/showcase-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,13 @@ jobs:
# built `dist/`. Build the whole dependency closure (excluding the
# showcase's own app build, which serve --dev doesn't need).
- run: pnpm turbo run build --filter=@objectstack/example-showcase^...
# The console SPA served at /_console is vendored by build-console.sh
# (shallow-clones objectui@.objectui-sha, builds @object-ui/console, copies
# dist → packages/console/dist). Without it serve --dev warns "Console dist
# not found" and the app shell never renders.
- name: Vendor the pinned Console SPA
run: bash scripts/build-console.sh
# Self-test the drift guard: a fresh build-console.sh must stamp the dist
# with the pinned SHA so `pnpm check:console-sha` reports in-sync. Catches
# a regression where the stamp step is dropped or the pin/stamp diverge.
- name: Verify Console SHA stamp matches pin
run: pnpm check:console-sha
# The console SPA served at /_console is @objectstack/console's dist: a
# copy of the workspace's own ui/apps/console build, which `build` does not
# produce (packages/console/build-dist.sh, run by the `build:dist` task).
# Without it serve --dev warns "Console dist not found" and the app shell
# never renders.
- name: Build the Console SPA from ui/apps/console
run: pnpm console:build
- name: Install Playwright Chromium
working-directory: examples/app-showcase
run: pnpm exec playwright install --with-deps chromium
Expand Down
1 change: 0 additions & 1 deletion .objectui-sha

This file was deleted.

20 changes: 6 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"description": "ObjectStack Protocol & Specification - Monorepo for TypeScript Interfaces, JSON Schemas, and Convention Configurations",
"scripts": {
"build": "turbo run build --filter=!@objectstack/docs --filter=!./ui*/apps/site",
"dev": "node scripts/check-dev-prereqs.mjs && pnpm check:console-sha && pnpm --filter @objectstack/example-showcase dev",
"dev:showcase": "node scripts/check-dev-prereqs.mjs && pnpm check:console-sha && pnpm --filter @objectstack/example-showcase dev",
"dev:crm": "node scripts/check-dev-prereqs.mjs && pnpm check:console-sha && pnpm --filter @objectstack/example-crm dev",
"dev:todo": "node scripts/check-dev-prereqs.mjs && pnpm check:console-sha && pnpm --filter @objectstack/example-todo dev",
"dev": "node scripts/check-dev-prereqs.mjs && pnpm --filter @objectstack/example-showcase dev",
"dev:showcase": "node scripts/check-dev-prereqs.mjs && pnpm --filter @objectstack/example-showcase dev",
"dev:crm": "node scripts/check-dev-prereqs.mjs && pnpm --filter @objectstack/example-crm dev",
"dev:todo": "node scripts/check-dev-prereqs.mjs && pnpm --filter @objectstack/example-todo dev",
"spec:rebuild": "turbo run build --filter=...@objectstack/spec",
"test": "VITEST_MAX_WORKERS=$(node scripts/vitest-worker-cap.mjs) turbo run test test:repo --concurrency=50%",
"test:smoke": "turbo run test:smoke",
Expand All @@ -18,16 +18,13 @@
"prepare": "node scripts/setup-git-hooks.mjs",
"check:merge-driver": "node scripts/git-env.mjs --self-test && node scripts/git-merge-regen.mjs --self-test && node scripts/check-regen-pending.mjs --self-test",
"version": "node scripts/release-pending-publish.mjs annotate-shipped; changeset version && node scripts/sync-protocol-version.mjs && node scripts/sync-template-versions.mjs && node scripts/sync-docs-image-tags.mjs && node scripts/sync-release-index-currency.mjs && node scripts/check-skill-compatibility-version.mjs --fix",
"release": "pnpm run build && bash scripts/build-console.sh && bash scripts/release-publish.sh",
"release": "pnpm run build && pnpm run console:build && bash scripts/release-publish.sh",
"docs:dev": "pnpm --filter @objectstack/docs dev",
"docs:build": "pnpm --filter @objectstack/docs build",
"docs:start": "pnpm --filter @objectstack/docs start",
"os": "node packages/cli/bin/run.js",
"objectui:build": "bash scripts/build-console.sh",
"console:build": "turbo run build:dist --filter=@objectstack/console",
"gen:sdui-lockstep": "node scripts/check-sdui-lockstep.mjs --update",
"objectui:bump": "bash scripts/bump-objectui.sh",
"objectui:refresh": "bash scripts/bump-objectui.sh && bash scripts/build-console.sh",
"objectui:clean": "rm -rf packages/console/dist .cache/objectui-*",
"lint": "node --stack-size=4000 node_modules/eslint/bin/eslint.js . --no-inline-config",
"i18n:extract": "tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --source-hashes --out=packages/platform-objects/src/apps/translations",
"check:i18n": "node scripts/check-i18n-bundles.mjs --self-test && node scripts/check-i18n-bundles.mjs",
Expand Down Expand Up @@ -147,12 +144,7 @@
"check:optional-error-sink": "node scripts/check-optional-error-sink-contract.mjs --self-test && node scripts/check-optional-error-sink-contract.mjs",
"check:logger-receiver-detach": "node scripts/check-logger-receiver-detach.mjs --self-test && node scripts/check-logger-receiver-detach.mjs",
"check:startup-registry-verdict": "node scripts/check-startup-registry-verdict.mjs --self-test && node scripts/check-startup-registry-verdict.mjs",
"check:console-sha": "node scripts/check-console-sha.mjs",
"check:console-injection": "node scripts/check-console-injection.mjs --self-test && node scripts/check-console-injection.mjs",
"check:dev-prereqs": "node scripts/check-dev-prereqs.mjs --self-test && node scripts/check-dev-prereqs.mjs",
"check:objectui-changeset": "node scripts/objectui-changeset-digest.mjs --self-test && node scripts/objectui-range.mjs --self-test",
"check:objectui-bump": "bash scripts/bump-objectui.selftest.sh",
"check:prerelease-pins": "node scripts/check-prerelease-pin-watch.mjs --self-test && node scripts/check-prerelease-pin-watch.mjs",
"check:empty-changeset": "node scripts/check-empty-changeset.mjs --self-test && node scripts/check-empty-changeset.mjs",
"check:adr-0087-registration": "node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-adr-0087-registration.mjs",
"check:changeset-gate-self-tests": "node scripts/check-empty-changeset.mjs --self-test && node scripts/check-adr-0087-registration.mjs --self-test && node scripts/check-changeset-no-major.mjs --self-test",
Expand Down
Loading
Loading