feat(spec)!: drop the @objectstack/spec/cloud subpath — control-plane contracts leave, the package format moves to @objectstack/spec/marketplace - #17372
Conversation
…ve, package format moves to `./marketplace` WIP for the step-3 card of the cloud-contracts chain: the cloud control plane's own contracts (environment, environment-package, tenant, developer-portal, marketplace-admin, app-store) leave `@objectstack/spec`; the package & marketplace format relocates to `src/marketplace/` and is published as `@objectstack/spec/marketplace`; `EnvironmentType` is re-declared in `api/discovery.zod.ts`; `packages/metadata` reads the environment-artifact envelope from `@objectstack/spec/system`. Claude-Session: https://claude.ai/code/session_f95e3874-e532-4748-a921-044aa2752a2b Co-authored-by: Claude <noreply@anthropic.com>
…d carry defaults through def renames - `gen:schema` / `check:generated --fix` output: `cloud.json` shards leave, `marketplace.json` shards arrive, `api.json` gains `EnvironmentType`, the reference docs move to `content/docs/references/marketplace/`. - `scripts/lib/authorable-defaults.ts`: `carryDefaultsThroughRenames` — the #4666 defaults ratchet carried the baseline's KEY set through RENAMED_DEFS but not its DEFAULTS, so a declared category move read as 22 `(none) -> value (added)` flips; both halves are carried now, with the discrimination pinned in `authorable-defaults.test.ts`. - `./marketplace` publishes without a `browser` condition: its bundles link no `pg-connection-string` (the poisoned members were the deleted `environment` / `tenant` modules); `browser-reachable-entries.json` lists it as unjudged. - Count pins that describe the corpus move with it: 811 -> 782 isomorphic aliases, 37 -> 31 level-1 openers, description floor 140 -> 130. - `unemitted-schemas.baseline.json` drops the `Cloud.EnvironmentArtifactSchema` duplicate of the surviving `System.` row. Claude-Session: https://claude.ai/code/session_f95e3874-e532-4748-a921-044aa2752a2b Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 4 package(s): 51 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a452cc671aa0fa9f6ff2557a6153189d93c69c89 && git checkout a452cc671aa0fa9f6ff2557a6153189d93c69c89
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin eb7406ca25715085dece907d413af9e76b72c0e4 ef1fda259072f6ca043d5d7c9ee4c1cea5057206 && git checkout -B drift-repro eb7406ca25715085dece907d413af9e76b72c0e4 && git merge --no-ff ef1fda259072f6ca043d5d7c9ee4c1cea5057206
node scripts/docs-audit/affected-docs.mjs --json eb7406ca25715085dece907d413af9e76b72c0e4
|
…the deleted `cloud`
Contract-review patch round 1 of the cloud-subpath retirement PR. No gate
reads a hand-written docs enumeration of `@objectstack/spec`'s subpaths,
so three published pages kept advertising `cloud` after the exports-map
entry left, and none listed `marketplace`:
- `content/docs/deployment/troubleshooting.mdx` "Available subpaths" — now
the `./*` entries of `packages/spec/package.json`'s `exports` map, in its
order (16 entries, `meta-spelling` included), and the sentence names that
source so the next editor derives it rather than retypes it.
- `content/docs/plugins/packages.mdx` — the 15-namespace list.
- `content/docs/getting-started/glossary.mdx` — the 15-namespace list, the
layers table (its namespaces column, and `tenants` -> `packages` in the
purpose column beside it), the `### Cloud Protocol` entry (now
`### Marketplace Protocol`: the package & marketplace format), and the
Tenant entry, which named `cloud/tenant.zod.ts` as the schema's home.
Controlled sweep of `content/docs/**` after the edit: zero import-shaped
`@objectstack/spec/cloud` (same-shape controls: 131 `spec/data`, 8
`spec/marketplace`), zero namespace lists naming Cloud (control: 4 naming
Marketplace), zero `cloud/*.zod.ts` source paths (control: 8
`marketplace/*`). The two remaining `spec/cloud` mentions are historical
("left with", "re-exported until"). `concepts/architecture.mdx`'s six-card
conceptual layering still carries a "Cloud Protocol" card; that is a
framing question for the maintainer and is reported, not edited.
Claude-Session: https://claude.ai/code/session_f95e3874-e532-4748-a921-044aa2752a2b
Co-authored-by: Claude <noreply@anthropic.com>
os package publish honours or refuses a declared manifest.id, never substitutes one
#17530
Fixes #16325
Step 3 of 3 of the cloud-contracts chain (steps 1 and 2 merged: objectstack-ai/cloud#2037, objectstack-ai/objectui#8360). Ruling: option B "cut by owner" (director batch #62, comment
5563914257, maintainer 「同意」; direction verbatim 「我一直觉得 cloud 的协议应该放在云端,没必要开源」), and option A on the four consumer-less files (#16526, 「其他同意」, re-confirmed 「同意 A」). No alias, no deprecation window (2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进。」).What changes
@objectstack/spec/cloudis deleted — the exports-map entry, the tsup entry,packages/spec/src/cloud/**whole (24 files).environment.zod,environment-package.zod,tenant.zod,developer-portal.zod,marketplace-admin.zod,app-store.zodand their tests. 62 JSON-Schema defs are declared retired inRETIRED_DEFS_BY_MAJOR[18](oneentries/retired-defs/18.cloud__*.tseach); one semantic entry,cloud-subpath-retired, carries the prescription.environment/tenantalready live in the cloud repo (step 1); the other four had zero consumers anywhere and are recoverable fromd5d8d50db.packages/spec/src/marketplace/and published as@objectstack/spec/marketplace:package.zod,package-version.zod,marketplace.zod,package-l10n,template-manifest.zod(+ their 4 tests). 30 defs move category throughRENAMED_DEFS(cloud/X → marketplace/X), so every authorable key and default is carried and nothing leaves the author-facing contract. Whymarketplaceand notpackage:src/package/would have produced a shard literally namedpackage.jsoninsideapi-surface/,export-origins/,declaration-map/,json-schema.manifest/,authorable-surface/andauthorable-defaults/— a footgun for every tool that treats that filename specially.EnvironmentType(Schema)is re-declared inspec/api(api/discovery.zod.ts, its only open-source reader — the NODE_ENV fold table is aRecordkeyed byEnvironmentType);discovery.test.tsand theDiscoveryEnvironment ⊂ EnvironmentTypepin import it from there.cloud/EnvironmentType → api/EnvironmentTypeandcloud/Sha256Digest → system/Sha256Digest(the re-export's second emission) go throughRENAMED_DEFSas 0-key carries.packages/metadata/src/plugin.tsreadsEnvironmentArtifactSchemafrom@objectstack/spec/system(the one declaration;cloud/environment-artifact.zod.tswas a pure re-export).spec/api/package-api.zod.tsimportsArtifactReferenceSchemafrom../marketplace/marketplace.zod;packages/cli(package/publish.ts+ 2 tests) imports from@objectstack/spec/marketplace.check:generated --fix):cloud.jsonshards leave,marketplace.jsonshards arrive,api.jsongainsEnvironmentType;content/docs/references/cloud/→content/docs/references/marketplace/. The retirement itself is registered inRETIRED_DEFS_BY_MAJOR[18](the 62entries/retired-defs/18.cloud__*.ts) and in thecloud-subpath-retiredsemantic entry, both insrc/migrations/registry.ts.docs/protocol-upgrade-guide.mdandspec-changes.jsondo not project protocol 18 at all — the guide's last section isProtocol 16 → 17, and pre-existing 18-bucket entries such asapi/CrudEndpointPatternare equally absent from both — so neither file carries this retirement and neither is stale (check:generated: 15/15 current). An earlier revision of this body said those two files "carry the retirement"; that was wrong — see Patch round 1.@objectstack/specminor +**BREAKING**banner withadr-0087: registered cloud-subpath-retired(see Deviations for why notmajor);@objectstack/cli/@objectstack/metadatapatch (import paths only).The counters — the class that FAILed step 2, re-swept for this repo
Everything in this repo that named
cloudas a category, subpath, module path or count. The PM's three are the first three; the rest is this sweep's:packages/runtime/src/spec-subpath-alias-coverage.pin.test.ts:126PUBLISHED_SUBPATHScontainscloudmarketplace; floor>= 15still clears at 16 (19 − 1 + 1 keys, minus.and the two.json)packages/runtime/vitest.config.ts,packages/metadata/vitest.config.ts^@objectstack\/spec\/([a-z-]+)$ → src/$1/index.ts(#9457), somarketplaceresolves andcloudonly survives in the incident prosepackages/spec/scripts/export-origins.test.ts:53['./cloud', import('../src/cloud/index')]./marketplacepackages/spec/src/kernel/package-dependency-dual-source.test.tsPackageDependencyowned by./cloud, originsrc/cloud/package-version.zod.ts./marketplace,src/marketplace/package-version.zod.tspackages/spec/src/system/environment-artifact.test.ts./cloudre-exports the envelope to the./systemdeclarationholdersOf(name)equals['./system']— no other entry may name itpackages/spec/src/cloud/tenant.test.ts(#4739 pin)./systemmust not re-grow the provisioning family;./cloudholdsTenantPlansrc/system/tenant-provisioning-family-retired.test.ts, plus the new factholdersOf('TenantPlan')is emptypackages/spec/src/type-alias-convention.pin.test.ts(ADR-0122 registry +check:spec-parsed-alias)marketplace/,EnvironmentTypeSchemapinned again underapi/discovery(Iso871); count 811 → 782 with the history notepackages/spec/scripts/file-description.test.tsPAGES.cloud, thecloud/packagelink fixture,openingOf('cloud/template-manifest…'), corpus floor> 140, exact37level-1 openersmarketplace/…; measured 138 (floor → 130) and 31 (six of the seven deletedcloud/*.zod.tsopened at level 1 —tenantopened at level 2)packages/spec/scripts/root-meta.test.ts:70cloud→marketplace, still 14packages/spec/scripts/build-docs.tsCATEGORY_SECTIONS/CATEGORY_BLURBS,lib/category-title.tsmarketplacerowspackages/spec/scripts/build-schemas.ts,check-yaml-examples.tsimport * as Cloud from '../src/cloud'namespace mapsMarketplacepackages/spec/scripts/lib/renamed-defs.tscloud/PackageDependency"deliberately absent"packages/spec/scripts/check-template-manifests.ts:44TemplateManifestSchemafrom../src/cloud/…../src/marketplace/…packages/spec/unemitted-schemas.baseline.jsonCloud.EnvironmentArtifactSchema(duplicate of theSystem.row, keyed by export)packages/spec/browser-reachable-entries.json./cloudunjudged./marketplaceunjudgedpackages/spec/tsup.config.tsbrowserConditionedEntriespackages/spec/llms.txt:96(check:llms-txtre-derives counts)cloud / 11marketplace / 4; heading 205 → 198content/docs/getting-started/quick-reference.mdx(check:quick-reference-counts)## Cloud Protocol (3 of 11 schemas)## Marketplace Protocol (2 of 4 schemas)scripts/check-system-context-census.mjsNON_READ_ANCHORS+content/docs/permissions/system-context.mdxEnvironment.isSystematpackages/spec/src/cloud/environment.zod.ts#isSystem— "four declarations"packages/client/src/return-type-precision.test.tsEnvironmentfrom@objectstack/spec/cloudas the near-miss binding@ts-expect-error'd import from@objectstack/spec/marketplace— it goes red the day a row type calledEnvironmentreappears there (environmentIsNotTheCloudWireRowhad no subject left)apps/docs/redirects.mjs(check:docs-redirects)references/cloud/{marketplace,package,package-version,template-manifest}→references/marketplace/…,environment-artifact→references/system/…, the rest → the references index (the file's own convention for pages with no successor)packages/client/src/index.ts(the #11925/#12036 docblock — ruling text untouched),client.environments-namespace.test.ts,spec/src/system/index.ts,contracts/index.ts,kernel/plugin-security.zod.ts,system/environment-artifact.zod.ts,cli/src/utils/protocol-version-gap.ts,create-objectstacktest message,content/docs/concepts/north-star.mdx,docs/design/marketplace-publishing.md; patch round 1 addscontent/docs/deployment/troubleshooting.mdx,content/docs/plugins/packages.mdx,content/docs/getting-started/glossary.mdx— enumerations no gate readsStill naming
cloud, deliberately:docs/adr/**(history, governed),docs/plans/m1-project-artifact-schema.md(a historical plan), CHANGELOGs, the #9457 incident prose in the two vitest configs and the runtime pin header, andAGENTS.md's Protocol Domains row — governed surface, filed as #17370 rather than converting this PR to human-merge-only.objectui's two exports-map pins (
vite-objectstack-spec-dist.test.ts:152/:608, both 19) are that repo's bump PR's, as the card records — not touched here.A gate fix, in place —
carryDefaultsThroughRenamesgen:schema's #4666 defaults ratchet carried the baseline's KEY set throughRENAMED_DEFSbut not its DEFAULTS map, so the category move read as 22(none) → value (added)flips — declaring them inDEFAULT_CHANGES_BY_MAJORwould have been 22 false claims (a rename riding the acknowledged-change table, exactly whatrenamed-defs.tsforbids). Fix:scripts/lib/authorable-defaults.ts#carryDefaultsThroughRenames, applied at both baseline sites inbuild-schemas.ts, with four discrimination tests inscripts/authorable-defaults.test.ts(carry → zero findings; negative control: the uncarried baseline reproduces theaddedflip; a default that really moves across a rename is still caught; undeclared defs pass through). In-place under the bounded exemption: same defect class as this card (rename-blind counters), the form is pinned by the key-carry two lines above, no other claim declarespackages/spec/scripts/**, same gate family. File surface added to the claim by this PR.Deviations
minor, notmajor. The dispatch said "MAJOR";scripts/check-changeset-no-major.mjsrefusesmajorunder the launch-window convention (recorded on.github/workflows/pr-automation.ymland in.changeset/engine-verb-result-declarations.md). Breaking-ness is carried by the**BREAKING**banner + the ADR-0087registereddisposition; the gate printed✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 dispositionand✓ This diff introduces no major bump../marketplacepublishes without abrowsercondition../cloudwas browser-conditioned because its module graph reachedpg-connection-string; the poisoned members were the deletedenvironment/tenantmodules. Measured on the rebuilt dist:dist/marketplace/index.mjslinks it 0 times (dist/kernel/index.mjs: 4, the control).check:browser-reachable-entriesis green with it unconditioned.cloud/EnvironmentType → api/EnvironmentType(re-declared) andcloud/Sha256Digest → system/Sha256Digest(a rename onto a def that already existed — the casebuild-schemas.ts's manifest rewrite explicitly handles). Retiring them would have claimed they left the published set; they did not.references/cloud/pages — the table exists for exactly this, and the deleted control-plane pages follow its own "section index" convention.UNLOCKED (declared)on every run: no usableflockon this host, so nothing was serialized (declared here as the wrapper asks).turbo ls --affectedlists 77 packages (spec is the root of the graph). Run locally in full:@objectstack/spec(467 files / 13163 tests),@objectstack/runtime(251 / 3532),@objectstack/client(41 / 492),@objectstack/metadata(53 / 788),create-objectstack(15 / 195);@objectstack/cli--project unit(190 passed / 2 failed files — all 5 failing cases are/var/foldersvs/private/var/folderstmpdir-realpath mismatches inpublished-subpath-console.pin.test.ts/published-subpath-hook-body.pin.test.ts, host-environmental, nothing this diff touches); cliintegrationtier declared to CI. Typecheck green: spec, metadata, runtime, cli, client, create-objectstack. The remaining affected packages andpnpm lintare CI's.main(cca1dc0bf):check:bash32-floor(mapfile: command not foundin the simulated-3.2 harness) andcheck:merge-driver— host shell, not this diff; NOT MEASURED locally.Console Pin Gate — measured, not assumed
AGENTS.md Post-Task #4 asks whether the pinned sibling imports what is removed. It does:
.objectui-sha=53ded82bfpredates step 2's mergecb847fd6d(merge-base --is-ancestorexit 1 on a full clone; control leg pin→origin/mainexit 0), and at the pinpackages/types/src/index.ts:1105still readsexport type * as Cloud from '@objectstack/spec/cloud'. Soscripts/build-console.shwas run against THIS tree: green — 8817 modules,✓ Bundle canary present,✓ Console bundle carries THIS tree's @objectstack/spec, and only it. Mechanism:@object-ui/typesis built through turbo from objectui's own vendored spec (17.3.0, which still exports./cloud) and the re-export is type-only, erased in the injected SPA build. ⇒maindoes not go red on merge. The pin bump (withpnpm sdui:manifestand objectui's 19→18 pins) remains the objectui-side follow-up the card already records — not a precondition.Reverse verification
On the committed head,
packages/metadata/src/plugin.ts's import was flipped back to@objectstack/spec/cloud(on-disk proof:grep -c0→1 forcloud, 1→0 forsystem),tsc --noEmitinpackages/metadatawent red with TS2307 (Cannot find module '@objectstack/spec/cloud'), the file was restored withgit checkout HEAD -- <path>andgit diff HEADis empty (blob hash equal before/after). Direction: red, as expected. The client-side guard is the second leg:check:test-typecheckfor@objectstack/clientreports0 file(s) / 0 error(s), i.e. the@ts-expect-erroron theEnvironmentimport is consumed — the name really is gone.Gates
Derived by
node scripts/pm/dispatch-gates.mjs --commandson the actual changed set: 147 commands, all run; 141 exit 0. The six non-zero:check:skill-examples(exit 1 until@objectstack/client-reactwas built, then✅ 258 prose examples type-check),check:dual-build-cjs-loadsandcheck:i18n-coverage(exit 3 PREREQUISITE NOT MET until the fullpnpm build, then0—check-i18n-coverage: OK (13 config(s), 621 baselined, none new)), and the two host self-tests above. Ratchet families re-run on the final head after the last commit — see the PR comment / report for the sha line.pnpm --filter @objectstack/spec check:generated: 15/15 artifacts current.Patch round 1 — contract review (head
ef1fda259)Two items from the contract-review PASS, both landed in
ef1fda259(docs only: 3 files, 7 lines).Published docs advertised the deleted subpath.
content/docs/deployment/troubleshooting.mdx's "Available subpaths" enumeratedcloudand did not listmarketplace. It now lists the./*entries ofpackages/spec/package.json'sexportsmap in the map's own order (16,meta-spellingincluded) and names that source so the next editor derives it. The sweep the review asked for — the same controlled shape as the code sweep, overcontent/docs/**— found two more enumerations of the same set that no gate reads:content/docs/plugins/packages.mdx's 15-namespace list, andcontent/docs/getting-started/glossary.mdx(its 15-namespace list, the layers table — namespaces column, plustenants→packagesin the purpose column beside it — the### Cloud Protocolsection, now### Marketplace Protocol, and the Tenant entry, which namedcloud/tenant.zod.tsas the schema's home). All corrected.Sweep readings on
ef1fda259, each zero beside a same-shape control that fires:content/docs/**@objectstack/spec/cloud(from/import/require(then a quote)spec/data131 ·spec/marketplace8Kernel, Cloud·Security, Cloud· the backtickedcloud-then-qalist fragment)cloud/NAME.ts/cloud/NAME.zod.tsmarketplace/NAME…: 8spec/cloudmentionpermissions/system-context.mdx:54("left this repo with the … subpath") and the generatedreferences/system/environment-artifact.mdx:12("re-exported this file until")spec/marketplace: 9 lines / 5 filesReported, not edited:
concepts/architecture.mdxstill carries a "Cloud Protocol" card in its six-card conceptual layering — a framing choice, not a namespace list — filed as content/docs/concepts/architecture.mdx still presents a "Cloud Protocol" card after the./cloudsubpath left@objectstack/spec#17387; and the fact that no gate holds these three enumerations equal to the exports map, filed as No gate holds the hand-written docs enumerations of@objectstack/specsubpaths and protocol namespaces equal to the exports map #17388.docs/qa/platform-checklist/FOLLOW-UPS.md:269records a historical path in a governance note and is correct as history — untouched.A claim in this body was wrong and is corrected in "What changes" above:
docs/protocol-upgrade-guide.mdandspec-changes.jsondo not carry the retirement; they do not project protocol 18 at all.Gates for this round:
node scripts/pm/dispatch-gates.mjs --commandsover the three files derives 39 families; all 39 ran onef1fda259and all exit 0 (four first returned 3 / 1 as PREREQUISITE NOT MET until@objectstack/lintand@objectstack/client-reactwere built, then 0:✅ 258 prose examples type-check across 3 surface(s),✓ check-docs-transcript-drift: 4 declared transcript value(s) across 401 page(s) … equal what the registry derives today,✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 438 files,✅ 27 ObjectSchema.create example(s) … carry an os validate-clean security posture).--ranreconciliation: 39 derived, 39 run, 0 unrun. The 108 code-derived families of the full changed set derive from files unchanged sinceedfbc7f22, where they ran (CI on that head: 35 success / 6 skipped, 0 failures) — a declared narrowing for a docs-only delta, not a re-run. The verify-lock wrapper again reportedUNLOCKED (declared)(no usableflockon this host).维护者速读(草稿)
改了什么 —— 开源包
@objectstack/spec里的cloud子路径整个删掉了。云端控制面自己的数据结构(环境、租户、开发者门户、市场后台、应用商店,62 个 schema)从开源包里离开:环境和租户已经在云端仓库里重新声明(第一步),另外四个文件在任何仓库里都没有使用者,直接删除(可从历史d5d8d50db找回)。「包与市场格式」这一半(包、版本、市场发布/安装、模板清单,30 个 schema)留在开源包里,从cloud/搬到marketplace/,以@objectstack/spec/marketplace发布,内容一字不改。为什么改 —— 你 9 月 6 日的方向:「我一直觉得 cloud 的协议应该放在云端,没必要开源」,决策批次 #62 定了 B「按归属切开」。控制面 schema 的生产者和消费者都在云端仓库,开源这边只用到一个类型(
EnvironmentType,已经搬到api/)。留在开源包里的坏处是:SDK 的client.environments.*故意返回any,而旁边就放着一个看起来正好能接上的Environment类型,接上去编译能过、运行时读到空值。删掉之后这个类型在开源包里根本不存在,接错在结构上不可能。风险与代价(含回滚) —— 这是删除已发布子路径的破坏性变更,不留别名、不留过渡期(按你 8 月 27 日的裁决)。仓库的发布约定不允许
major,所以 changeset 标minor并带BREAKING横幅和 ADR-0087 登记,迁移说明写在 changeset 和升级指南里。云端仓库还有 4 处PackageSchema从@objectstack/spec/cloud导入,要在这个版本发布后改到/marketplace(云端席位的活);objectui 那边两处「19 个导出项」的钉子会在它升级 spec 依赖时变 18(那张 PR 的活)。当前钉住的 objectui 版本还有一行只导出类型的Cloud,实测 Console Pin Gate 仍然绿(类型导出被擦除)。回滚:整个 PR 一次 revert 即可,生成物会跟着回来。席位意见 ——
你要做的 —— 确认两点:①
marketplace这个名字可以接受(比package好的理由:package会在六个生成物目录里各生出一个叫package.json的分片文件,工具链会把它当包清单);② 这个 PR 走人工合并还是队列——它不触及受管面(AGENTS.md 那一行另开了 #17370)。Generated by Claude Code