Skip to content

feat(spec)!: drop the @objectstack/spec/cloud subpath — control-plane contracts leave, the package format moves to @objectstack/spec/marketplace - #17372

Merged
hotlong merged 3 commits into
mainfrom
claude/issue-16325-spec-drops-cloud-subpath
Sep 10, 2026
Merged

hotlong merged 3 commits into
mainfrom
claude/issue-16325-spec-drops-cloud-subpath

Conversation

@hotlong

@hotlong hotlong commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

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/cloud is deleted — the exports-map entry, the tsup entry, packages/spec/src/cloud/** whole (24 files).
  • The cloud control plane's contracts leave the open-source spec: environment.zod, environment-package.zod, tenant.zod, developer-portal.zod, marketplace-admin.zod, app-store.zod and their tests. 62 JSON-Schema defs are declared retired in RETIRED_DEFS_BY_MAJOR[18] (one entries/retired-defs/18.cloud__*.ts each); one semantic entry, cloud-subpath-retired, carries the prescription. environment/tenant already live in the cloud repo (step 1); the other four had zero consumers anywhere and are recoverable from d5d8d50db.
  • The package & marketplace FORMAT stays, relocated to 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 through RENAMED_DEFS (cloud/X → marketplace/X), so every authorable key and default is carried and nothing leaves the author-facing contract. Why marketplace and not package: src/package/ would have produced a shard literally named package.json inside api-surface/, export-origins/, declaration-map/, json-schema.manifest/, authorable-surface/ and authorable-defaults/ — a footgun for every tool that treats that filename specially.
  • EnvironmentType(Schema) is re-declared in spec/api (api/discovery.zod.ts, its only open-source reader — the NODE_ENV fold table is a Record keyed by EnvironmentType); discovery.test.ts and the DiscoveryEnvironment ⊂ EnvironmentType pin import it from there. cloud/EnvironmentType → api/EnvironmentType and cloud/Sha256Digest → system/Sha256Digest (the re-export's second emission) go through RENAMED_DEFS as 0-key carries.
  • packages/metadata/src/plugin.ts reads EnvironmentArtifactSchema from @objectstack/spec/system (the one declaration; cloud/environment-artifact.zod.ts was a pure re-export).
  • spec/api/package-api.zod.ts imports ArtifactReferenceSchema from ../marketplace/marketplace.zod; packages/cli (package/publish.ts + 2 tests) imports from @objectstack/spec/marketplace.
  • Generated artifacts regenerated (check:generated --fix): cloud.json shards leave, marketplace.json shards arrive, api.json gains EnvironmentType; content/docs/references/cloud/ → content/docs/references/marketplace/. The retirement itself is registered in RETIRED_DEFS_BY_MAJOR[18] (the 62 entries/retired-defs/18.cloud__*.ts) and in the cloud-subpath-retired semantic entry, both in src/migrations/registry.ts. docs/protocol-upgrade-guide.md and spec-changes.json do not project protocol 18 at all — the guide's last section is Protocol 16 → 17, and pre-existing 18-bucket entries such as api/CrudEndpointPattern are 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.
  • Changeset: @objectstack/spec minor + **BREAKING** banner with adr-0087: registered cloud-subpath-retired (see Deviations for why not major); @objectstack/cli / @objectstack/metadata patch (import paths only).

The counters — the class that FAILed step 2, re-swept for this repo

Everything in this repo that named cloud as a category, subpath, module path or count. The PM's three are the first three; the rest is this sweep's:

counter what it pinned how it moved
packages/runtime/src/spec-subpath-alias-coverage.pin.test.ts:126 PUBLISHED_SUBPATHS contains cloud names marketplace; floor >= 15 still clears at 16 (19 − 1 + 1 keys, minus . and the two .json)
packages/runtime/vitest.config.ts, packages/metadata/vitest.config.ts alias tables no edit needed — both use the anchored regex rule ^@objectstack\/spec\/([a-z-]+)$ → src/$1/index.ts (#9457), so marketplace resolves and cloud only survives in the incident prose
packages/spec/scripts/export-origins.test.ts:53 ['./cloud', import('../src/cloud/index')] ./marketplace
packages/spec/src/kernel/package-dependency-dual-source.test.ts PackageDependency owned by ./cloud, origin src/cloud/package-version.zod.ts ./marketplace, src/marketplace/package-version.zod.ts
packages/spec/src/system/environment-artifact.test.ts ./cloud re-exports the envelope to the ./system declaration replaced by the stronger form: holdersOf(name) equals ['./system'] — no other entry may name it
packages/spec/src/cloud/tenant.test.ts (#4739 pin) ./system must not re-grow the provisioning family; ./cloud holds TenantPlan the system/contracts half survives as src/system/tenant-provisioning-family-retired.test.ts, plus the new fact holdersOf('TenantPlan') is empty
packages/spec/src/type-alias-convention.pin.test.ts (ADR-0122 registry + check:spec-parsed-alias) M45–M54 cloud modules, 30 pins, count 811 six modules' 30 pins leave, M50–M53 re-point to marketplace/, EnvironmentTypeSchema pinned again under api/discovery (Iso871); count 811 → 782 with the history note
packages/spec/scripts/file-description.test.ts PAGES.cloud, the cloud/package link fixture, openingOf('cloud/template-manifest…'), corpus floor > 140, exact 37 level-1 openers marketplace/…; measured 138 (floor → 130) and 31 (six of the seven deleted cloud/*.zod.ts opened at level 1 — tenant opened at level 2)
packages/spec/scripts/root-meta.test.ts:70 14-category list cloud → marketplace, still 14
packages/spec/scripts/build-docs.ts CATEGORY_SECTIONS / CATEGORY_BLURBS, lib/category-title.ts per-category tables the docs generator holds equal to the tree marketplace rows
packages/spec/scripts/build-schemas.ts, check-yaml-examples.ts import * as Cloud from '../src/cloud' namespace maps Marketplace
packages/spec/scripts/lib/renamed-defs.ts cloud/PackageDependency "deliberately absent" now a source (category move); 32 entries added
packages/spec/scripts/check-template-manifests.ts:44 imports TemplateManifestSchema from ../src/cloud/… ../src/marketplace/…
packages/spec/unemitted-schemas.baseline.json Cloud.EnvironmentArtifactSchema (duplicate of the System. row, keyed by export) row deleted
packages/spec/browser-reachable-entries.json ./cloud unjudged ./marketplace unjudged
packages/spec/tsup.config.ts entry + browserConditionedEntries entry renamed; not browser-conditioned (see Deviations)
packages/spec/llms.txt:96 (check:llms-txt re-derives counts) cloud / 11 marketplace / 4; heading 205 → 198
content/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.mjs NON_READ_ANCHORS + content/docs/permissions/system-context.mdx Environment.isSystem at packages/spec/src/cloud/environment.zod.ts#isSystem — "four declarations" row retired with a note; page says three
packages/client/src/return-type-precision.test.ts Environment from @objectstack/spec/cloud as the near-miss binding the guard moves from a member read to a @ts-expect-error'd import from @objectstack/spec/marketplace — it goes red the day a row type called Environment reappears there (⚠️ not silently deleted; the old environmentIsNotTheCloudWireRow had 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)
prose only, updated: 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-objectstack test message, content/docs/concepts/north-star.mdx, docs/design/marketplace-publishing.md; patch round 1 adds content/docs/deployment/troubleshooting.mdx, content/docs/plugins/packages.mdx, content/docs/getting-started/glossary.mdx — enumerations no gate reads

Still 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, and AGENTS.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 — carryDefaultsThroughRenames

gen:schema's #4666 defaults ratchet carried the baseline's KEY set through RENAMED_DEFS but not its DEFAULTS map, so the category move read as 22 (none) → value (added) flips — declaring them in DEFAULT_CHANGES_BY_MAJOR would have been 22 false claims (a rename riding the acknowledged-change table, exactly what renamed-defs.ts forbids). Fix: scripts/lib/authorable-defaults.ts#carryDefaultsThroughRenames, applied at both baseline sites in build-schemas.ts, with four discrimination tests in scripts/authorable-defaults.test.ts (carry → zero findings; negative control: the uncarried baseline reproduces the added flip; 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 declares packages/spec/scripts/**, same gate family. File surface added to the claim by this PR.

Deviations

  1. Changeset level minor, not major. The dispatch said "MAJOR"; scripts/check-changeset-no-major.mjs refuses major under the launch-window convention (recorded on .github/workflows/pr-automation.yml and in .changeset/engine-verb-result-declarations.md). Breaking-ness is carried by the **BREAKING** banner + the ADR-0087 registered disposition; the gate printed ✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition and ✓ This diff introduces no major bump.
  2. ./marketplace publishes without a browser condition. ./cloud was browser-conditioned because its module graph reached pg-connection-string; the poisoned members were the deleted environment/tenant modules. Measured on the rebuilt dist: dist/marketplace/index.mjs links it 0 times (dist/kernel/index.mjs: 4, the control). check:browser-reachable-entries is green with it unconditioned.
  3. Two 0-key defs carried by rename rather than retired: cloud/EnvironmentType → api/EnvironmentType (re-declared) and cloud/Sha256Digest → system/Sha256Digest (a rename onto a def that already existed — the case build-schemas.ts's manifest rewrite explicitly handles). Retiring them would have claimed they left the published set; they did not.
  4. Docs redirects added for the retired references/cloud/ pages — the table exists for exactly this, and the deleted control-plane pages follow its own "section index" convention.
  5. Verify-lock wrapper reported UNLOCKED (declared) on every run: no usable flock on this host, so nothing was serialized (declared here as the wrapper asks).
  6. Local verification narrowed, declared: turbo ls --affected lists 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/folders vs /private/var/folders tmpdir-realpath mismatches in published-subpath-console.pin.test.ts / published-subpath-hook-body.pin.test.ts, host-environmental, nothing this diff touches); cli integration tier declared to CI. Typecheck green: spec, metadata, runtime, cli, client, create-objectstack. The remaining affected packages and pnpm lint are CI's.
  7. Two gate self-tests fail identically on clean main (cca1dc0bf): check:bash32-floor (mapfile: command not found in the simulated-3.2 harness) and check: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 = 53ded82bf predates step 2's merge cb847fd6d (merge-base --is-ancestor exit 1 on a full clone; control leg pin→origin/main exit 0), and at the pin packages/types/src/index.ts:1105 still reads export type * as Cloud from '@objectstack/spec/cloud'. So scripts/build-console.sh was run against THIS tree: green — 8817 modules, ✓ Bundle canary present, ✓ Console bundle carries THIS tree's @objectstack/spec, and only it. Mechanism: @object-ui/types is 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. ⇒ main does not go red on merge. The pin bump (with pnpm sdui:manifest and 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 -c 0→1 for cloud, 1→0 for system), tsc --noEmit in packages/metadata went red with TS2307 (Cannot find module '@objectstack/spec/cloud'), the file was restored with git checkout HEAD -- <path> and git diff HEAD is empty (blob hash equal before/after). Direction: red, as expected. The client-side guard is the second leg: check:test-typecheck for @objectstack/client reports 0 file(s) / 0 error(s), i.e. the @ts-expect-error on the Environment import is consumed — the name really is gone.

Gates

Derived by node scripts/pm/dispatch-gates.mjs --commands on the actual changed set: 147 commands, all run; 141 exit 0. The six non-zero: check:skill-examples (exit 1 until @objectstack/client-react was built, then ✅ 258 prose examples type-check), check:dual-build-cjs-loads and check:i18n-coverage (exit 3 PREREQUISITE NOT MET until the full pnpm build, then 0 — 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).

  1. Published docs advertised the deleted subpath. content/docs/deployment/troubleshooting.mdx's "Available subpaths" enumerated cloud and did not list marketplace. It now lists the ./* entries of packages/spec/package.json's exports map in the map's own order (16, meta-spelling included) and names that source so the next editor derives it. The sweep the review asked for — the same controlled shape as the code sweep, over content/docs/** — found two more enumerations of the same set that no gate reads: content/docs/plugins/packages.mdx's 15-namespace list, and content/docs/getting-started/glossary.mdx (its 15-namespace list, the layers table — namespaces column, plus tenants → packages in the purpose column beside it — the ### Cloud Protocol section, now ### Marketplace Protocol, and the Tenant entry, which named cloud/tenant.zod.ts as the schema's home). All corrected.

    Sweep readings on ef1fda259, each zero beside a same-shape control that fires:

    pattern over content/docs/** hits control (same shape)
    import-shaped @objectstack/spec/cloud (from / import / require( then a quote) 0 spec/data 131 · spec/marketplace 8
    namespace lists naming Cloud (Kernel, Cloud · Security, Cloud · the backticked cloud-then-qa list fragment) 0 the same shapes naming Marketplace: 4
    spec source paths cloud/NAME.ts / cloud/NAME.zod.ts 0 marketplace/NAME…: 8
    any spec/cloud mention 2, both historical — permissions/system-context.mdx:54 ("left this repo with the … subpath") and the generated references/system/environment-artifact.mdx:12 ("re-exported this file until") spec/marketplace: 9 lines / 5 files

    Reported, not edited: concepts/architecture.mdx still 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 ./cloud subpath 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/spec subpaths and protocol namespaces equal to the exports map #17388. docs/qa/platform-checklist/FOLLOW-UPS.md:269 records a historical path in a governance note and is correct as history — untouched.

  2. A claim in this body was wrong and is corrected in "What changes" above: docs/protocol-upgrade-guide.md and spec-changes.json do not carry the retirement; they do not project protocol 18 at all.

Gates for this round: node scripts/pm/dispatch-gates.mjs --commands over the three files derives 39 families; all 39 ran on ef1fda259 and all exit 0 (four first returned 3 / 1 as PREREQUISITE NOT MET until @objectstack/lint and @objectstack/client-react were 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). --ran reconciliation: 39 derived, 39 run, 0 unrun. The 108 code-derived families of the full changed set derive from files unchanged since edfbc7f22, 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 reported UNLOCKED (declared) (no usable flock on 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

hotlong and others added 2 commits September 10, 2026 16:54
…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>
@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/client, @objectstack/metadata, @objectstack/spec, touching 265 documentable anchor(s). ⚠️ 91 changed file(s) yielded no anchor (packages/cli/src/utils/protocol-version-gap.ts, packages/spec/api-surface/api.json, packages/spec/api-surface/cloud.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

51 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json eb7406ca25715085dece907d413af9e76b72c0e4.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 91 changed file(s) yielded no anchor (packages/cli/src/utils/protocol-version-gap.ts, packages/spec/api-surface/api.json, packages/spec/api-surface/cloud.json, …) — pages documenting those are invisible to this run
  • 9 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 143 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 eb7406ca25715085dece907d413af9e76b72c0e4 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from a452cc671aa0fa9f6ff2557a6153189d93c69c89 — the merge of head ef1fda259072f6ca043d5d7c9ee4c1cea5057206 into base eb7406ca25715085dece907d413af9e76b72c0e4, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs eb7406ca25715085dece907d413af9e76b72c0e4 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…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>
@hotlong
hotlong marked this pull request as ready for review September 10, 2026 11:16
@hotlong
hotlong enabled auto-merge September 10, 2026 11:16
@hotlong
hotlong added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit 776d64c Sep 10, 2026
51 checks passed
@hotlong
hotlong deleted the claude/issue-16325-spec-drops-cloud-subpath branch September 10, 2026 11:44
os-bill pushed a commit that referenced this pull request Sep 17, 2026
…in the discovery-environment subset pin

The pin test's docblock told a reader that `EnvironmentTypeSchema` is declared
by `cloud/environment.zod.ts`, while the file's own import 28 lines below reads
`./discovery.zod`. `packages/spec/src/cloud/` was removed when the `./cloud`
subpath left `@objectstack/spec` (#16325, landed by #17372 / 776d64c) and
the enum was re-declared in `api/discovery.zod.ts:434`, where it lives today.

Two residues of that move are corrected, both prose:

- the declaration table row, now naming `api/discovery.zod.ts`, with one added
  sentence recording why both rows name one module — so the next reader does
  not read the repetition as a copy-paste slip;
- the reverse cross-reference note, which named the two carrier FILES; it now
  names the two DECLARATIONS, which is what the sentence is actually about and
  what survives the next move.

Re-derived rather than copied forward: both member lists are unchanged and
correct (`production sandbox development` / those three plus `test staging
preview trial`, `discovery.zod.ts:473` and `:434`), and the two folds the
docblock cites (`staging` -> `sandbox`, `test` -> `development`) match
`NODE_ENV_TO_DISCOVERY_ENVIRONMENT`.

No assertion, message or schema is touched.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…in the discovery-environment subset pin (objectstack-ai#18586)

Fixes objectstack-ai#17730

Clause-②: no

The pin test's docblock told a reader that `EnvironmentTypeSchema` is
declared by `cloud/environment.zod.ts`, while the file's own import 28
lines below reads `./discovery.zod`. Class (b): a declared statement
contradicted by measurable behaviour in the same file. Only prose moves
here — no schema, no assertion, no assertion message.

## Premise, re-verified first-hand on `origin/main` (not taken from the
card)

```
git ls-tree -d origin/main packages/spec/src/cloud   ->  (no output: ABSENT)
git ls-tree -d origin/main packages/spec/src/api     ->  040000 tree 61d3b3d…   (lit control: the instrument prints a tree when one exists)
git grep -n 'EnvironmentTypeSchema *=' origin/main -- 'packages/spec/src/**'
     ->  packages/spec/src/api/discovery.zod.ts:434
```

Deletion traced to a commit, with a firing control:

```
git log --diff-filter=D -- packages/spec/src/cloud/environment.zod.ts
  ->  776d64c  2026-09-10  feat(spec)!: drop the `@objectstack/spec/cloud` subpath … (objectstack-ai#17372)
      commit body: "`EnvironmentType` is re-declared in `api/discovery.zod.ts`"
control (a file that still exists, same command):
git log --diff-filter=D -- packages/spec/src/api/discovery.zod.ts   ->  (empty, as it must be)
```

So `objectstack-ai#16325` is the ruling and `objectstack-ai#17372` / `776d64cd3d` is the landing.
The in-tree prose at `api/discovery.zod.ts:412` already attributes the
move to `objectstack-ai#16325` ("Until objectstack-ai#16325 this enum was declared by
`cloud/environment.zod.ts`"), and this docblock now matches that
convention.

Line numbers were taken off the tree, not off the card: the card says
the import is at `:39` and the second residue at `:27-28`; on
`origin/main` @ `5ed7ad9df8` they are at `:40` and `:26-27`.

## What changed — two residues, both in the docblock

1. **The declaration table row** (`:12`) now names
`api/discovery.zod.ts`. One sentence is added below the table recording
why both rows name one module, so the repetition does not read as a
copy-paste slip and does not get "repaired" back.
2. **The reverse cross-reference note** (`:26-27`) named the two carrier
FILES, one of which no longer exists. It now names the two DECLARATIONS
— which is what that sentence is actually about, and what survives the
next move.

Both cross-reference claims were re-derived rather than copied forward:

| claim | verified |
|:---|:---|
| `DiscoveryEnvironmentSchema`'s `.describe()` since objectstack-ai#4828 | `git log
-S'a richer 7-member taxonomy'` -> `77022a99ce` = objectstack-ai#4828 (PR objectstack-ai#5682) |
| `EnvironmentTypeSchema`'s JSDoc since this pin | `git log
--diff-filter=A` on the pin file -> `0261016601` (PR objectstack-ai#6288), the same
commit that introduced the reverse citation; carried into
`api/discovery.zod.ts` by `776d64cd3d` |

## The member lists were re-derived, not copied forward

The table's factual claim about each enum's vocabulary is **correct**
and is unchanged by this PR:

- `DiscoveryEnvironmentSchema` (`discovery.zod.ts:473`) = `production`
`sandbox` `development` — 3.
- `EnvironmentTypeSchema` (`discovery.zod.ts:434`) = those three plus
`test` `staging` `preview` `trial` — 7.

The two folds the docblock cites also hold against
`NODE_ENV_TO_DISCOVERY_ENVIRONMENT` (`discovery.zod.ts:591`): `staging`
-> `sandbox`, `test` -> `development`.

## Tests and gates

- `pnpm --filter @objectstack/spec exec vitest run --project local
src/api/discovery-environment-subset.pin.test.ts` — **4 passed**,
VERDICT command-exit 0.
- `pnpm --filter @objectstack/spec test` — **483 files / 13775 tests
passed**, VERDICT command-exit 0.
- `pnpm --filter @objectstack/spec typecheck` — exit 0 (`tsc --noEmit` +
`check:scripts-typecheck` + `check:test-typecheck`; the last compiles
the test layer under `tsconfig.test.json`, which is what reaches this
file).
- Dependency closure: `pnpm --filter '@objectstack/spec^...' build` ->
"No projects matched the filters" — `packages/spec` has no workspace
dependencies, so step 1 is an empty run. `packages/spec` itself was
built before every `dist/`-reading gate, and later `pnpm build` (73/73
tasks successful) before the four gates that refuse without one.
- Derived gate families: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` -> 78 commands, each run with its
exit code landed to disk before any pipe, reconciled with `--ran`: **78
derived, 78 run, 0 NOT-MEASURED, 0 UNRUN** (reconciler exit 0).
- 77 of the 78 are green. The one red is `pnpm
check:cross-package-test-inputs` (exit 1), and it is the **known,
already-filed** local-only signature: it flags `@objectstack/cli`
descending `packages/spec/dist/`, which exists only on a tree where
`packages/spec` has been built — cards objectstack-ai#18353 / objectstack-ai#18440. Not re-filed,
and not caused by this diff.
- `pnpm lint` equivalent, run whole rather than narrowed: `eslint .
--no-inline-config --format json` over the repo -> **6803 files, 0
errors, 0 warnings**, exit 0. The population figure is eslint's own
count from its `--format json` output, not an estimate.
- Control characters: `grep -naP` over the changed file finds none, and
`pnpm check:nul-bytes` is green.

## Changeset — `skip-changeset`, measured

`packages/spec`'s `files[]` is `dist · json-schema · liveness · prompts
· llms.txt · README.md · src/**/*.zod.ts · CHANGELOG.md · api-surface ·
spec-changes.json`. After a full build, grepping every one of those
paths:

- the text this PR adds: **0 hits** in the published paths, **0 hits**
in `src/**/*.zod.ts`;
- positive control `EnvironmentTypeSchema`: **12 files** in the
published paths, **1** in `src/**/*.zod.ts` — the instrument fires;
- a `.pin.test.ts` is not a `*.zod.ts` and nothing named `pin` is
emitted into `dist/`.

A comment inside a test file publishes nothing from any released
package, so `skip-changeset` is the right label and `patch` would be
wrong.

## Acceptance notes

Noted, not folded in — the same `objectstack-ai#16325` root, one line **inside the
test body**, which this card's dispatch explicitly ring-fences ("do not
change what the test asserts"):

`:61` of the same file, in the failure message of the second assertion,
reads "if a bucket was renamed on **the cloud side**, rename it here".
Post-objectstack-ai#16325 the enum is declared here, in the open-source spec, so as a
statement about *where a rename happens* it is stale. It is also
arguably still true as a statement about *where a rename originates* —
the `sys_environment` row and its taxonomy remain the cloud control
plane's, and `DiscoveryEnvironmentSchema`'s own `.describe()` still says
"that is `sys_environment` / EnvironmentTypeSchema". Ambiguous enough
that it is a reading for the seat, not a silent edit in a prose-only PR.
Reported, not filed by this seat.

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

Co-authored-by: Claude <noreply@anthropic.com>
os-try-charles pushed a commit that referenced this pull request Sep 19, 2026
…arketplace Protocol

The six cards on the protocol-stack section map one-to-one onto the namespaces
`packages/spec` publishes, and `./cloud` is no longer one of them: PR #17372 moved
the cloud control plane's contracts to the cloud repo while the package format
stayed as `@objectstack/spec/marketplace`. The card and the mermaid node under
"Why Separated Layers?" were two expressions of that stale namespace.

Rename the sixth card to `Marketplace Protocol` with the description "Packaging:
package format, versions, listings, templates.", swap its `Cloud` icon for
`Package`, and rewrite the mermaid node `System · Automation · Cloud Protocol` /
`… Multi-tenancy` to match. Multi-tenancy and Licensing leave the page with the
card they described.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…arketplace Protocol (objectstack-ai#19178)

Fixes objectstack-ai#17387

Clause-②: no

## What changed

`content/docs/concepts/architecture.mdx` only — 1 file, +5 / -5. No
code, no published surface, no workflow, nothing under `packages/spec`.

The six cards under "The Protocol Stack" map one-to-one onto the
protocol namespaces `packages/spec` publishes, and `./cloud` is no
longer one of them: PR objectstack-ai#17372 moved the cloud control plane's contracts
into the cloud repo while the package format stayed as
`@objectstack/spec/marketplace`. The sixth card and the mermaid node
under "Why Separated Layers?" were two expressions of that one stale
namespace, so both moved together — changing only one would have left
the page self-contradictory.

1. The sixth card is now titled `Marketplace Protocol`, described as
"Packaging: package format, versions, listings, templates."
2. `Multi-tenancy` and `Licensing` leave the page with the card that
described them.
3. The mermaid node `System · Automation · Cloud Protocol` / `Auth ·
Permissions · Orchestration · Multi-tenancy` now reads `System ·
Automation · Marketplace Protocol` / `Auth · Permissions · Orchestration
· Packaging`.

Two deviations from the literal ruling text, both declared rather than
silent:

- **The card's icon changed too.** The card imported the `Cloud` glyph
from `lucide-react`; a card titled `Marketplace Protocol` carrying a
cloud icon is the same drift in a third expression, so the import and
the `icon=` prop now name `Package` instead. `Package` is already the
icon other pages use for packaging
(`content/docs/protocol/kernel/plugin-spec.mdx`,
`content/docs/protocol/objectql/schema.mdx`) and it is exported by the
installed `lucide-react@1.31.0`.
- **A trailing period** was added to the description, because the other
five card descriptions on the same page all carry one. Typography only;
the ruling's wording is otherwise verbatim.

## Ruling executed, not re-weighed

The card body listed three options and called the choice a framing
decision. The director seat ruled **A** on 2026-09-11T11:53Z (issue
comment `5634035531`, class-one self-adjudication recorded in the summon
objectstack-ai#22 ledger for the maintainer's later ratification), on the standing
ruling 「文档应该以实际实现为准」. Triage graded it `priority:p3` / type `Bug`
(`5690506939`). This PR executes ruling A; it does not reopen the
options.

## Evidence

Premise re-verified on this branch's base `30288da04` (the PM measured
on `83649b870`; the reading is unchanged):

```
$ git grep -n -i 'cloud protocol' -- content/docs        # before
content/docs/concepts/architecture.mdx:49:    title="Cloud Protocol"
content/docs/concepts/architecture.mdx:63:    OS[... System · Automation · Cloud Protocol ... Multi-tenancy ...]

$ python3 -c "import json;print(sorted(json.load(open('packages/spec/package.json'))['exports']))"
['.', './ai', './api', './automation', './contracts', './data', './identity', './integration',
 './kernel', './marketplace', './meta-spelling', './openapi.json', './package.json', './qa',
 './security', './shared', './studio', './system', './ui']          # no './cloud', './marketplace' present
```

The card's own predicate, re-measured after the edit, with its
same-shape control:

```
$ git grep -n -i 'cloud protocol' -- content/docs        # after
(no output, exit 1)

$ git grep -l -i 'marketplace protocol' -- content/docs  # control — 6 files before, 7 after
content/docs/concepts/architecture.mdx      (this page joins the existing vocabulary)
content/docs/getting-started/glossary.mdx
content/docs/getting-started/quick-reference.mdx
content/docs/references/index.mdx
content/docs/references/marketplace/index.mdx
content/docs/references/marketplace/marketplace.mdx
content/docs/references/marketplace/meta.json
```

A zero with no control is not a reading; the control still hits, and it
grew by exactly this page.

## Gates

`node scripts/pm/dispatch-gates.mjs --commands` re-derived against the
real changed set (not a hand-written list) at `30288da04`: **39
commands, all run, all exit 0**, reconciled with `--ran` carrying each
exit code:

```
✓ dispatch-gates --ran: 39 derived famil(ies) accounted for — 39 run,
  0 NOT-MEASURED (a DERIVED zero — all 39 recorded an exit code and none of them is 3).
```

Five of the 39 first exited `3` / `1` with `PREREQUISITE NOT MET`
(unbuilt `@objectstack/lint`, `@objectstack/formula`,
`@objectstack/spec`, `@objectstack/client-react`). Those are not
findings; the packages were built under the shared verify lock and all
five then exited 0. Notably `check:docs-section-name` — the gate the
card named — passes both plainly and with `--self-test`, so the card's
claim that no gate reads this page's card titles holds as measured, not
as quoted.

Outside the derived 39 and **not measured here**: the 52 artifact-roster
families, the 11 declared wide-population families, the 14 families that
would apply once a changeset existed, and the 2 path-scheduled CI jobs
(`ci.yml · Test Core`, `ci.yml · Build Docs`). CI owns those.

`skip-changeset` is correct and measured, not assumed: no package's
`files[]` array names anything under `content/`, and `apps/docs` is
`"private": true`. Nothing published moves. The ruling names
`skip-changeset` as well.

## Acceptance notes

- **Reported, not decided:**
`content/docs/concepts/architecture.mdx:147` carries a third occurrence
of the word on the page — `- Multi-tenancy and data isolation`, a bullet
in the "Layer 2: Kernel (Control Protocol)" responsibilities list. It is
not part of the card or the mermaid node, it predates this drift, and it
states something about the Kernel rather than about a spec namespace.
The ruling's Execution paragraph scopes the work to "the card and the
mermaid node", so this PR leaves `:147` untouched and hands the question
up rather than deciding it. If the intended reading of "drop
Multi-tenancy from the page" includes that bullet, it is a one-line
follow-up.
- Noted, not filed: the page's `ArrowRight` and `CheckCircle` imports
are unrelated to this change and were left alone.

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

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

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation needs:contract-review protocol:system size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] Should the cloud control-plane contracts leave @objectstack/spec and live in the cloud repo? (upstream of #12036)

1 participant