Skip to content

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

Description

@hotlong

Maintainer direction given in a direct PM session on 2026-09-06, verbatim (⛔ quoted, not translated):

我一直觉得 cloud 的协议应该放在云端,没必要开源

Filed as a decision card on the maintainer's instruction in the same session ("开决策卡"). This card is
UPSTREAM of #12036: if the control-plane schemas leave the open-source contract package, #12036's
camelCase-vs-snake_case fork stops being a cross-repo contract decision and becomes one repo's internal
coding style. #12036 is therefore moved out of the decision inbox and blocked on this card.

⛔ Nothing was changed. No branch, no edit. All readings below are read-only.


RULED 2026-09-07 — option B (director seat, decision batch #62, comment 5563914257; maintainer
verbatim 「同意」). This card is now the objectstack half of the ruled work, not an open question. The
analysis below is kept as the record the ruling was made on; ⛔ do not re-adjudicate it.

Blocked-by: #16450
Blocked-by: objectstack-ai/objectui#8225
Blocked-by: #16526


Readings

Taken 2026-09-06T04:42Z. objectstack-ai/objectstack origin/main d5d8d50db;
objectstack-ai/cloud origin/main daaac081; objectstack-ai/objectui origin/main 0558e0f78.

1. The boundary already exists — it was drawn half-way

@objectstack/spec's main entry does not re-export the cloud namespace. ./cloud is a standalone
published subpath in the package's exports map (packages/spec/package.json), with its own
browser/import/require conditions.

$ grep -cE "^export" packages/spec/src/index.ts          # control — the file does export things
59
$ grep -cE "^export.*cloud" packages/spec/src/index.ts   # term under test
0

⇒ 59 export lines and zero of them name cloud: the absence is real, not a broken pattern. Reaching these
schemas already requires the explicit @objectstack/spec/cloud subpath.

2. The open-source repo's real dependency on the control-plane half is ONE symbol

Every other in-repo occurrence of @objectstack/spec/cloud outside packages/spec/src/cloud/ is a
comment, a test, or a re-export of something that lives elsewhere:

site what real dependency?
packages/spec/src/api/discovery.zod.ts:8 import type { EnvironmentType } from '../cloud/environment.zod' ⭐ YES — the only one. A string-union type
packages/spec/src/api/package-api.zod.ts:10 import { ArtifactReferenceSchema } from '../cloud/marketplace.zod' YES, but on the PACKAGE-FORMAT half (see §3)
packages/metadata/src/plugin.ts:895 dynamic import('@objectstack/spec/cloud') for EnvironmentArtifactSchema No — packages/spec/src/cloud/environment-artifact.zod.ts is a pure re-export whose body is export { … } from '../system/environment-artifact.zod'. Re-point the import at @objectstack/spec/system and the dependency is gone
packages/client/src/index.ts:2233,2350 comments only No — see §4
packages/cli/test/package-publish-namespace.test.ts CreatePackageRequestSchema test, package-format half
packages/client/src/return-type-precision.test.ts Environment type test

3. spec/cloud holds two families with different owners

half files lines what it actually is
control-plane private environment.zod, environment-package.zod, tenant.zod, developer-portal.zod, marketplace-admin.zod, app-store.zod 2087 the cloud service's own row shapes and back-office. Open-source code touches one type from it
package & marketplace format package.zod, package-version.zod, marketplace.zod, package-l10n, template-manifest.zod 1400 ⚠️ not a cloud protocol. A package author needs it; the open-source CLI's os package publish path uses CreatePackageRequestSchema; spec/api/package-api.zod.ts depends on its ArtifactReferenceSchema

Intra-directory dependency roots: package.zod ← marketplace.zod ← {developer-portal, app-store};
tenant.zod ← environment.zod. So the two halves are separable, with one edge to cut
(app-store and developer-portal sit on marketplace.zod).

4. The SDK already refuses to bind these — with a maintainer ruling on record

packages/client/src/index.ts:2228-2248 is a 20-line docblock stating that the 15 environments.* methods
keep their erased any deliberately, naming #11925/#12036 and the casing conflict. At :2340-2372 a
prior ruling is recorded verbatim — maintainer, 2026-08-29: 「同意」— option 甲 — choosing the INLINE wire
shape and ⛔ NOT binding @objectstack/spec/cloud's ProvisionEnvironmentResponseSchema.

⇒ The direction on this card has effectively been ruled once already, per-method. This card asks whether it
becomes the rule for the whole subpath.

5. Producer and consumers of the control-plane half both live in the cloud repo

From the repo:cloud reading already on #12036 (comment 5520459478, cloud 9b6abe0), re-confirmed at
daaac081: 12 import sites, including a runtime parse at
packages/service-tenant/src/environment-provisioning.ts:750
(ProvisionEnvironmentRequestSchema.parse(request)) and const environment: Environment = { … } at :1094
and :1429. The schemas' only real consumer is the repo they would move to.

6. objectui's dependency is one blanket re-export line

$ git grep -n "@objectstack/spec/cloud" origin/main   # objectstack-ai/objectui @ 0558e0f78
packages/types/src/index.ts:1106:export type * as Cloud from '@objectstack/spec/cloud';

Every other hit in that repo is a comment, and each of those comments says the type was deliberately
re-declared inline instead of imported
(app-shell/src/console/marketplace/marketplaceApi.ts:85,
usePackageL10n.ts:6, app-shell/src/views/metadata-admin/PackagesPage.tsx:75).

The options

A — move the whole ./cloud subpath to the cloud repo. All 3487 lines leave @objectstack/spec; the
subpath export is deleted.
⚠️ Cost: the package/marketplace format leaves with it, so os package publish and
spec/api/package-api.zod.ts lose their declarations — a package author would need a cloud-repo package to
author a package for the open-source CLI.

B — cut by owner (recommended). The control-plane half (2087 lines, 6 files) moves to the cloud repo.
The package/marketplace half (1400 lines, 5 files) STAYS in the open-source spec, relocated out of
src/cloud/ under a name that says what it is (package format, not cloud protocol). EnvironmentType is
re-declared in spec/api. packages/metadata re-points at @objectstack/spec/system. The ./cloud
subpath export is deleted.

C — no move. The boundary stays where it is, and the decision reverts to #12036's three-way casing fork
(spec moves to snake_case / the wire moves to camelCase / a transform is documented).

What B costs, stated plainly

  1. Deleting a published public subpath export is a breaking change to @objectstack/spec — major
    version plus a release note. Precedent exists on this exact API family: content/docs/releases/v17.mdx:218
    shipped "client.projects.* becomes client.environments.*, with no aliases".
  2. objectstack-ai/objectui packages/types/src/index.ts:1106 must drop or re-point its
    export type * as Cloud line before the subpath is removed.
  3. Ordering is a real constraint: cloud and objectui must stop importing BEFORE objectstack removes the
    export, because cloud consumes this through its .objectstack-sha pin. Three cards, not one PR.
  4. Whether a deprecation window is left is NOT reopened here: the maintainer's 2026-08-27 ruling (verbatim:
    「项目在创业阶段,用户也很少,短期不考虑渐进。」) makes immediate removal with no alias the default.
    Say so explicitly in the answer if a window is wanted instead.

Not a duplicate, and not in conflict with the open-core work

#16215 and #16130 move multi-organization RUNTIME capability INTO the open core. This card moves the
commercial CONTROL PLANE's contracts out of it. Same boundary, opposite sides — the framework grows, the
service's private shapes leave. Searched repo:objectstack-ai/objectstack state:open for spec/cloud
in:title (2 hits: #12036, #16084 — neither is this), cloud contracts boundary (7), and
open source boundary spec (81); no existing card asks this question.

推荐:B。 ①领起(把错放的搬走,让契约包里每样都真是契约);③让误绑在结构上不可能,而不是靠一段 docblock;④净收窄。②不反对,但要如实说它不紧急 —— 这不是 P0,客户今天不坏。
回退:C(不动)。 若你认为三仓窗口此刻不值得,C 是诚实的选择,代价是回到 #12036 的三选一,且窗口随消费者增加而变贵。
⛔ 不荐 A。 它会把包/市场格式一起搬走,而 os package publish 是开源 CLI 的能力、包作者需要那个格式;A 会为了搬走 2087 行而误伤 1400 行。
置信缺口(本分析看不见什么): 未量 cloud 侧接收这 6 个文件的施工成本(它今天 import 的是已发布包,改成本地声明要动 12 个站点、含一处运行时 .parse())。⚠️ 这是执行排序的输入,不改变方向判断;若你选 B,该由 repo:cloud 席先出一份接收面读数再排卡。

Governing text: AGENTS.md Prime Directive #12 — "Contract-first — fix the metadata, not the runtime.
packages/spec is the one contract between metadata…"
; and the maintainer's 2026-08-27 ruling on staged
retirement (verbatim: 「项目在创业阶段,用户也很少,短期不考虑渐进。」).

Downstream


维护者速读

这是什么事 —— 我们的开源包 @objectstack/spec 里,放着一批只有云端在用的类型:环境、租户、后台管理的数据结构,2087 行。它们的生产者和消费者都在云端仓库,只有声明留在开源包里。你说的「cloud 的协议应该放在云端」,实测站得住 —— 开源那边对这批东西的真实依赖只有一个类型,其余全是注释和测试。

为什么值得现在处理 —— 不是因为坏了。客户今天不受影响,SDK 那 15 个方法本来就没类型、而且是故意没类型(代码里有你 2026-08-29 的裁决记录)。值得处理是因为:只要这批类型还挂在开源包上,就会一直有人想把它们和方法接起来,而那样做编译通过、运行时读到空值。搬走之后,这个类型在开源包里根本不存在,误接在结构上就不可能了。同时它还顺手解决了 #12036 那个字段命名之争 —— 那个争议之所以要你拍板,唯一原因就是声明和实现分在两个仓。

选项 —— A:整个 cloud 子路径全搬走(我不推荐:会连"包格式"一起搬走,而那是包作者发包要用的,属于开源侧的东西)。B:按归属切开 —— 云端私有的 2087 行搬去云端,包格式那 1400 行留在开源包里但换个不叫 cloud 的名字。C:不动 —— 回到 #12036 的字段命名三选一。

我的意见 —— 推荐 B。但要如实说两点:一,这不是急事,客户今天不坏,你完全可以先放着;二,B 要花一次跨三个仓的协调(云端、objectui、开源仓各一张卡,还有先后顺序),不是一次改动能完成的。另外,删掉一个已发布的公开子路径算破坏性变更,要升 major 写发行说明 —— 这个我们 v17 对同一批 API 做过一次,当时就是不留别名直接换。

你要做的 —— 选一个:A / B / C。

Activity

  1. added theissue type on Sep 6, 2026
  2. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Ruling recorded — option B, cut by owner (director seat, decision batch #62, 2026-09-07)

    Maintainer reply, verbatim: 「同意」 (all five batch #62 recommendations adopted). Direction, verbatim from the maintainer's own filing: 「我一直觉得 cloud 的协议应该放在云端,没必要开源」.

    Ruling. The control-plane half of packages/spec/src/cloud/ (six files, 2087 lines: environment, environment-package, tenant, developer-portal, marketplace-admin, app-store) leaves @objectstack/spec and becomes the cloud repo's own declarations. The package / marketplace format half (five files, 1400 lines: package, package-version, marketplace, package-l10n, template-manifest) stays in the open-source spec, relocated out of src/cloud/ under a name that says what it is. The ./cloud subpath export is deleted. No alias, no deprecation window (maintainer 2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进」). Not urgent: customers are not broken today.

    Three cards, in order.

    1. cloud: take ownership of the control-plane contracts — local declarations for the six spec/cloud control-plane schemas, 12 import sites re-pointed (step 1 of 3 of the #16325 ruling) #16450 — cloud takes ownership (local declarations, 12 import sites, casing becomes cloud's internal choice matched to the wire).
    2. objectui#8225 — drop export type * as Cloud from '@objectstack/spec/cloud'.
    3. This card (objectstack half): relocate the package-format half out of cloud/, re-declare EnvironmentType in spec/api, re-point packages/metadata at @objectstack/spec/system, delete the ./cloud subpath; major version, release note (written centrally at release time — the PR's input is its changeset). pm:blocked on 1 and 2. Clause-② yes.

    #12036 (camelCase vs snake_case) is closed as superseded: once the declarations live beside their producer, the casing is one repo's internal style.

    Labels: needs-user-decision → pm:blocked (Blocked-by #16450, objectui#8225). Ledger on #12708 (batch #62).


    Generated by Claude Code

  3. hotlong commented on Sep 7, 2026

    @hotlong
    ContributorAuthor

    Half-state repair — pm:blocked carried no Blocked-by: index line

    Card author's session (the direct maintainer session that filed this card), 2026-09-07T05:59Z readings.
    ⛔ Not a claim, not a dispatch, not a re-adjudication — see the last section.

    The defect

    Ruling comment 5563914257 states this card is "pm:blocked on 1 and 2", and the label was set. But that
    sentence is prose; the unlock scan reads the machine-grepable Blocked-by: line in the body, and this
    body had none:

    $ gh api /repos/objectstack-ai/objectstack/issues/16325 --jq '.body' | grep -cE "^Blocked-by:"
    0
    # control — the same predicate on a card known to carry one, same hour:
    $ gh api /repos/objectstack-ai/objectstack/issues/12036 --jq '.body' | grep -cE "^Blocked-by:"
    1
    

    ⇒ The zero is a real absence, not a broken pattern. Consequence: when #16450 and objectstack-ai/objectui#8225
    close, nothing reverse-indexes to this card and it stays pm:blocked indefinitely — a silent permanent
    block, not a tracked wait.

    The repair

    Two lines added to the body, in the ruling's own order, plus a pointer marking the analysis below as the
    pre-ruling record rather than an open question:

    Blocked-by: #16450
    Blocked-by: objectstack-ai/objectui#8225
    

    Read back after the write: both present at body lines 18-19. Labels untouched (enhancement,
    priority:p2, domain:spec, pm:blocked) — the label was already correct; only its index was missing.

    What this seat did NOT do, and why

    The domain:spec seat post (#6017) shows an incumbent, session_01T6HeZvT9wdSJD1ZxJb5Eno, with seat
    activity at 2026-09-07T05:20Z — 39 minutes before this reading. Seat mutual exclusion therefore holds
    against this session:

    The repair above is a state-index fix on a card this session authored, of the kind the half-state patrol
    reports; it changes no decision and no ownership.

  4. hotlong commented on Sep 7, 2026

    @hotlong
    ContributorAuthor

    Step 1 of 3 (cloud stops importing) is OPEN as a draft, not merged: objectstack-ai/cloud#2037 for #16450.

    What the receiving side measured, for this card's step 3 to read before deleting ./cloud:

    • On cloud 9b85d761, only environment.zod and tenant.zod of the six control-plane files have a consumer. developer-portal.zod, marketplace-admin.zod, app-store.zod and environment-package.zod export 90 symbols and none of those names appears in the cloud repo outside the lockfile — cloud did not re-declare them (reported on cloud: take ownership of the control-plane contracts — local declarations for the six spec/cloud control-plane schemas, 12 import sites re-pointed (step 1 of 3 of the #16325 ruling) #16450 as a PM decision, not settled by the dev). If step 3 deletes them, nothing on the cloud side notices.
    • EnvironmentType(Schema), EnvironmentRole/EnvironmentMember (sys_environment_member was dropped in cloud) and PackageInstallation(Status)Schema also have zero cloud consumers.
    • environment-artifact.zod.ts's two cloud import sites were re-pointed at @objectstack/spec/system — no cloud-side dependence on the re-export remains.
    • After the PR, the only @objectstack/spec/cloud imports left in cloud are the two PackageSchema sites (routes/package-publish.ts, test/package-id-declarations.test.ts) — the package-format half this ruling keeps open-source; step 3 re-points them when it relocates that half.

    "Landed" (merged) is the PM's call; this comment is the open-PR heads-up only.


    Generated by Claude Code

  5. hotlong commented on Sep 7, 2026

    @hotlong
    ContributorAuthor

    Additive correction to comment 5566370040 — a count in it goes stale at merge

    Dispatching PM seat, session session_f95e3874-e532-4748-a921-044aa2752a2b, 2026-09-07T07:4xZ.
    ⛔ The earlier comment is left standing; this is the correction, not a rewrite of it.

    That comment (the step-1 heads-up from objectstack-ai/cloud#2037's dev) says:

    the only @objectstack/spec/cloud imports left in cloud are the two PackageSchema sites

    True on the PR branch, and false shortly after it merges. objectstack-ai/cloud main has since taken
    #2031 and #2032; #2032 adds two more PackageSchema from '@objectstack/spec/cloud' importers
    (packages/service-cloud/src/routes/package-install.ts, packages/service-cloud/test/package-export.test.ts).
    Post-merge the count is 4, not 2.

    Nothing about step 3 changes. All four are the package-format half — the half this ruling deliberately keeps
    in the open-source spec — so what step 3 (#16325) must do is unchanged in kind, only in count: re-point four
    PackageSchema importers, not two, at the relocated open-source path.

    The acceptance criterion the step-1 PR was actually held to is unaffected and still holds: zero control-plane
    schema importers remain
    (symbol-level grep for Environment* / TenantPlan* / TenantRoutingConfig* /
    Provision* against @objectstack/spec/cloud ⇒ 0 on the branch).

    The dev spotted this while doing an unrelated patch round and correctly did not amend it — the instruction
    for that round was "nothing else", and silently editing a landed statement is worse than appending the fix. The
    correction is the dispatching seat's to make, which is this comment. Whoever picks up step 3 should count the
    importers on the tree in front of them rather than trusting either number here.

  6. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Blocked-by: #16526 discharged — ruled A (director seat, decision batch #77, 2026-09-07, maintainer 「其他同意」): cloud hosts only environment.zod and tenant.zod; step 3 deletes the four consumer-less files with the ./cloud subpath. This card's deletion half is executable. pm:blocked → pm:queue.


    Generated by Claude Code

  7. hotlong commented on Sep 7, 2026

    @hotlong
    ContributorAuthor

    ⚠️ Step 3 landmines, mapped in advance — two numeric pins in objectstack-ai/objectui will break the moment ./cloud leaves the spec

    Dispatching PM seat, 2026-09-08T00:5xZ. Filed here, on step 3's own card, because this is what step 3 walks into —
    ⛔ not on the step-2 card where it was discovered and where nobody doing step 3 would look.

    Why this exists

    Step 2 (objectui#8225, PR objectui#8360) was FAILed by contract review on a defect nobody predicted: removing the
    repo's last '@objectstack/spec/cloud' string literal broke an anti-vacuity floor pin that counts distinct
    @objectstack/spec specifiers across a tree — a guard that exists so the scan cannot silently go empty. Nothing in
    the card mentioned it; it surfaced only as a red CI shard.

    ⇒ This seat asked the dev to sweep for the whole class — who counts instances of these specifiers — precisely
    because step 3 deletes the subpath outright and will hit every counter that exists. The sweep found two more, in the
    same file, and they are NOT step 2's to touch.

    The two pins

    objectstack-ai/objectui, scripts/__tests__/vite-objectstack-spec-dist.test.ts, measured on 21b1bf51f:

    line assertion reads
    :152 expect(declared.length).toBe(19) the installed @objectstack/spec/package.json exports-map entry count
    :608 expect(injectedSpecKeys).toHaveLength(19) the same manifest's injected subpath keys

    Both are exact pins, not floors — so both go 19 → 18 the moment objectui installs a spec release whose
    manifest no longer exports ./cloud. The file's own comment at :146-151 records the previous move (18 → 19 on the
    17.2.0 refresh), so this pin has a history of tracking exactly this kind of change.

    Correctly untouched by step 2: installed spec is still 17.3.0, which still exports ./cloud (verified), and both
    pins are green. Moving them now would break a true assertion to pre-empt a future one.

    ⇒ They must be re-pinned in the same PR that bumps objectui's @objectstack/spec to the release that deletes the
    subpath.
    Not before — the pins are honest until that bump — and not after, or objectui's CI goes red on a bump PR
    for a reason that will look unrelated.

    Also checked, and clear

    • scripts/check-spec-range-floors.mjs reconciles subpaths mentioned in built artifacts against the spec manifest's
      exports map (finding opaque-subpath). After PR objectui#8360 no artifact mentions /cloud, so deleting it
      upstream produces no finding there.
    • scripts/check-eager-closure-budget.mjs:109 and its test :696 mention /cloud in prose only — no counter.
    • Remaining /cloud mentions in the scanned roots after step 2: 5, all comments (4 code comments plus
      packages/auth/README.md:220); the collector reads code extensions only, so none is counted. Code literals: 0.

    Scope note

    ⛔ This is not a new blocking edge on this card and no Blocked-by: is being added — the pins live in objectui and
    move with objectui's own spec-version bump, which is downstream of step 3's release, not a precondition for it. It is
    recorded so the person doing that bump does not rediscover it from a red shard, the way step 2 did.

    The same class question is worth asking once inside objectstack-ai/objectstack itself before step 3 deletes the
    subpath: what in that repo counts @objectstack/spec subpaths or exports-map entries. This seat has not measured
    that, and says so rather than implying the sweep was repo-wide — it covered objectui.

  8. 3 remaining items

  9. self-assigned this
    on Sep 10, 2026
  10. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    Claim: PM direct maintainer dispatch (no numbered round — direct-dispatch channel); step 3 of 3, all three Blocked-by: edges now closed
    Session: session_f95e3874-e532-4748-a921-044aa2752a2b
    Branch: claude/issue-16325-spec-drops-cloud-subpath
    Worktree: objectstack-issue-16325
    Domain: domain:spec
    File surface: packages/spec/package.json (the ./cloud exports entry) · packages/spec/src/cloud/** (delete four zod files + three companion .test.ts; relocate the package-format five out of this directory) · packages/spec/src/api/{discovery,package-api}.zod.ts · packages/spec/src/system/index.ts · packages/metadata/src/plugin.ts · packages/metadata/vitest.config.ts · packages/runtime/vitest.config.ts · packages/runtime/src/spec-subpath-alias-coverage.pin.test.ts · packages/spec/scripts/export-origins.test.ts · packages/cli/test/package-publish-namespace.test.ts · packages/client/src/return-type-precision.test.ts · .changeset/ (stop on breach; explain in the report)
    Container & model: L, mode:subagent, model: claude-fable-5-1 (= CONTRACT_REVIEW_TIER; clause ② — deleting a published subpath export from @objectstack/spec is a public-surface change by definition)
    Clause-②: yes
    Serial constraints cleared: #16450 closed/merged 2026-09-07T08:19Z, objectstack-ai/objectui#8225 closed/merged 2026-09-07T17:15Z, #16526 closed/completed 2026-09-10 — all three edges CLOSED, not merely declared discharged. No in-flight objectstack card declares packages/spec/src/cloud/**; the domain:spec seat post (#6017) has an incumbent but this dispatch runs on the direct-dispatch channel, ⛔ no seat seized.


    Maintainer authorisation, verbatim (⛔ quoted, not translated): 「你直接负责派发处理」, following 「同意 A」 on #16526.

    Premise re-check at dispatch time (⛔ not inherited — the card's readings are 5 days old)

    origin/main 706ad0fcc (the card was written against d5d8d50db). All five work items re-verified as still undone:

    # item reading
    1 ./cloud still exported packages/spec/package.json ⇒ 1 hit
    2 package-format half still under src/cloud/ 22 matching entries
    3 EnvironmentType still imported by api/discovery.zod.ts 1
    4 ArtifactReferenceSchema still imported by api/package-api.zod.ts 1
    5 packages/metadata/src/plugin.ts still imports @objectstack/spec/cloud 1

    ⚠️ The four files to delete carry three companion test files the card does not name: app-store.test.ts,
    developer-portal.test.ts, marketplace-admin.test.ts (environment-package.zod.ts has none).

    ⭐ Three counters inside THIS repo that name cloud explicitly — the class that already broke step 2

    Step 2 (objectstack-ai/objectui#8360) was FAILed in review because removing the last @objectstack/spec/cloud
    literal broke an anti-vacuity floor pin nobody had listed. That sweep covered objectui only, and this seat said so
    rather than implying it was repo-wide. It is now done for objectstack, and it found three:

    1. packages/runtime/src/spec-subpath-alias-coverage.pin.test.ts:126 —
      expect(PUBLISHED_SUBPATHS).toContain('@objectstack/spec/cloud'); — an assertion that cloud is a published
      subpath, derived at :85 from Object.keys(specPackageJson.exports). It goes red the moment the entry is
      removed. :123 is also a floor, toBeGreaterThanOrEqual(15) — check whether the post-deletion count still
      clears it rather than assuming.
    2. packages/runtime/vitest.config.ts and packages/metadata/vitest.config.ts — alias tables enumerating
      published subpaths, cloud among them. Their own comments record incident packages/runtime's vitest alias list omits @objectstack/spec/cloud, so any runtime test that loads a compiled artifact dies with ENOTDIR #9457, where a MISSING entry made
      @objectstack/spec/cloud resolve to the garbage path …/spec/src/index.ts/cloud and die with ENOTDIR. A
      stale entry pointing at a deleted directory is the same failure with the sign flipped.
    3. packages/spec/scripts/export-origins.test.ts:53 — ['./cloud', () => import('../src/cloud/index')], a
      table entry that dynamically imports the cloud barrel.

    Cross-repo state, already handled — ⛔ do not re-do it

    • objectstack-ai/cloud no longer imports any control-plane schema (step 1, merged). Its remaining
      @objectstack/spec/cloud importers are 4 files, all PackageSchema — the package-format half, whose new
      home this card decides. They are the cloud seat's to re-point after this lands, ⛔ not from here.
    • objectstack-ai/objectui dropped its export type * as Cloud re-export (step 2, merged). Its two exports-map
      pins (scripts/__tests__/vite-objectstack-spec-dist.test.ts:152 and :608, both 19) go 19 → 18 on the objectui
      PR that bumps the spec dependency to the release carrying this deletion — that PR's job, ⛔ not this one's.
  11. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 16325,
      "status": "done",
      "branch": "claude/issue-16325-spec-drops-cloud-subpath",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17372",
      "premise_still_valid": true,
      "summary": "Step 3 of the cloud-contracts chain, implemented as ruled (option B cut by owner; option A on the four consumer-less files). The ./cloud subpath is deleted (exports map, tsup entry, packages/spec/src/cloud/** whole); the six control-plane modules leave with 62 defs declared in RETIRED_DEFS_BY_MAJOR[18] plus one semantic entry cloud-subpath-retired; the package & marketplace format five relocate to packages/spec/src/marketplace/ and publish as @objectstack/spec/marketplace with 30 defs carried through RENAMED_DEFS (plus 0-key carries cloud/EnvironmentType to api/EnvironmentType and cloud/Sha256Digest to system/Sha256Digest); EnvironmentType(Schema) is re-declared in api/discovery.zod.ts; packages/metadata reads the artifact envelope from @objectstack/spec/system; cli/client/create-objectstack re-point. Premise re-check at 706ad0fcc: all five work items were still undone (each hit count matched the PM's table); the tree I got was the same commit. One gate defect was fixed in place under the bounded exemption: the #4666 authorable-defaults ratchet carried the baseline KEY set through RENAMED_DEFS but not its DEFAULTS, so the category move read as 22 false added flips - carryDefaultsThroughRenames now carries both halves, with four discrimination tests (negative control included). Naming choice: marketplace over package because src/package/ would emit a shard literally named package.json in six generated-artifact directories; flagged for the maintainer in the PR's 维护者速读 as the one reversible-before-merge choice. The objectui pin (.objectui-sha = 53ded82bf) predates step 2's merge and still carries the type-only export type * as Cloud line; build-console.sh was run against this tree and is GREEN (8817 modules, spec-injection assertion passed), so main does not go red on merge - the pin bump stays the objectui-side follow-up the card already records.",
      "tests": "All on the final head edfbc7f22 unless noted. Typecheck (pnpm --filter X typecheck, VERDICT command-exit 0): spec (tsc + scripts + test-typecheck OK), metadata, runtime, cli, client (check:test-typecheck 0 files / 0 errors - the ts-expect-error on the retired Environment import is consumed), create-objectstack. Tests: spec 'Test Files 467 passed (467) / Tests 13163 passed (13163)'; runtime '251 passed / 3532 passed'; client '41 passed / 492 passed'; metadata '53 passed / 788 passed'; create-objectstack '15 passed | 1 skipped / 195 passed | 8 skipped'; cli --project unit '190 passed / 2662 passed, 5 failed' - all 5 are /var/folders vs /private/var/folders tmpdir-realpath mismatches in published-subpath-console.pin.test.ts and published-subpath-hook-body.pin.test.ts (host-environmental, untouched by this diff); cli integration tier declared to CI. Reverse verification on the committed head: packages/metadata/src/plugin.ts import flipped back to @objectstack/spec/cloud (on-disk proof grep -c cloud 0 to 1, system 1 to 0), tsc --noEmit went RED with 'src/plugin.ts(895,60): error TS2307: Cannot find module @objectstack/spec/cloud', restored with git checkout HEAD -- FILE, blob hash identical before/after (0bdb1484...), git diff HEAD empty. Direction: red, as expected. Ablation of the gate fix: scripts/authorable-defaults.test.ts negative control reproduces the added flip without the carry (27/27 pass). Build: pnpm build (turbo, 73 tasks) green; spec build green ('34/34 declared declaration files present'). Console Pin Gate: scripts/build-console.sh at pin 53ded82bf against this tree - 'Console bundle carries THIS tree's @objectstack/spec, and only it', exit 0. Narrowing declared: turbo ls --affected = 77 packages (spec is the graph root); the packages above were run in full, the rest and pnpm lint are CI's. Verify-lock wrapper reported UNLOCKED (no usable flock on this host) on every run, declared in the PR body.",
      "gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands on the actual changed set: 147 commands. --ran reconciliation: '147 derived, 147 run, 0 NOT-MEASURED, 0 UNRUN'. 141 exited 0 on first run; check:skill-examples exit 1 until @objectstack/client-react was built then '258 prose examples type-check' exit 0; check:dual-build-cjs-loads and check:i18n-coverage exit 3 PREREQUISITE NOT MET until the full pnpm build then both exit 0 ('check-i18n-coverage: OK (13 config(s), 621 baselined untranslated string(s), none new)'); check:bash32-floor and check:merge-driver self-tests exit 1 and reproduce identically on clean main cca1dc0bf (host shell) - NOT MEASURED locally, CI's. Ratchet families re-run on the final head edfbc7f22 (clean tree): 23/23 exit 0 - spec check:generated (15/15 artifacts current), check:api-surface, check:authorable-surface, check:export-origins, check:docs, check:llms-txt, check:browser-reachable-entries, check:dual-source-exports, check:entry-nameability, check:template-manifests, check:yaml-examples, check:skill-examples; root check:spec-parsed-alias, check:nul-bytes, check:cross-package-test-inputs, check:test-source-alias, check:type-check-coverage, check:docs-redirects, check:quick-reference-counts, check-system-context-census, check-adr-0087-registration ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition'), check-changeset-no-major ('This diff introduces no major bump'), check-empty-changeset. Point-named by the dispatch: nul-bytes green; no engine double, no error code, no .claude/agents edit.",
      "counter_resweep": "PM's three confirmed and moved (runtime alias-coverage pin :126 names marketplace, floor 15 clears at 16; the two vitest alias tables needed NO edit - both are the anchored regex rule from #9457, cloud survives only in their incident prose; export-origins.test.ts table entry). Beyond them, this sweep found and moved: kernel/package-dependency-dual-source.test.ts (owner ./cloud + origin path), system/environment-artifact.test.ts (the ./cloud re-export half, replaced by holdersOf equals ['./system']), cloud/tenant.test.ts (the #4739 system/contracts half relocated to system/tenant-provisioning-family-retired.test.ts with holdersOf('TenantPlan') empty), type-alias-convention.pin.test.ts (the ADR-0122 registry check:spec-parsed-alias reads: 30 pins left, EnvironmentTypeSchema re-pinned under api/discovery, count 811 to 782), file-description.test.ts (PAGES.cloud fixture, cloud/package link fixture, openingOf, corpus floor 140 to 130 on a measured 138, exact 37 to 31 level-1 openers), root-meta.test.ts 14-category list, build-docs.ts section/blurb tables, lib/category-title.ts, build-schemas.ts and check-yaml-examples.ts namespace maps, lib/renamed-defs.ts, check-template-manifests.ts import, unemitted-schemas.baseline.json (Cloud.EnvironmentArtifactSchema duplicate row), browser-reachable-entries.json, tsup entries, llms.txt row + heading count (check:llms-txt), quick-reference.mdx section counts (check:quick-reference-counts), check-system-context-census NON_READ_ANCHORS + system-context.mdx 'four declarations', client return-type-precision.test.ts (Environment near-miss guard moved to an import-level ts-expect-error), apps/docs/redirects.mjs. Controlled zero after the sweep: git grep for spec/cloud and src/cloud across packages/ and docs/design returns only prose that names the retirement or the #9457 incident (control: the same grep on 706ad0fcc returned 40+ code hits). Not this PR's: objectui's two exports-map pins (19 to 18 on the bump PR) and cloud's 4 PackageSchema importers.",
      "deviations": [
        "Changeset level minor with a BREAKING banner and adr-0087: registered cloud-subpath-retired, not major - scripts/check-changeset-no-major.mjs refuses major under the launch-window convention; the dispatch text said MAJOR. Gate output quoted in the PR.",
        "./marketplace publishes WITHOUT a browser condition (./cloud had one): measured dist/marketplace/index.mjs links pg-connection-string 0 times (dist/kernel/index.mjs 4, the control); the poisoned members were the deleted environment/tenant modules. check:browser-reachable-entries green.",
        "In-place gate fix (carryDefaultsThroughRenames in packages/spec/scripts/lib/authorable-defaults.ts + build-schemas.ts + tests) under the bounded exemption; file surface added to the claim in this report: packages/spec/scripts/lib/authorable-defaults.ts, packages/spec/scripts/authorable-defaults.test.ts, packages/spec/scripts/build-schemas.ts.",
        "Two 0-key defs carried by RENAMED_DEFS rather than retired (cloud/EnvironmentType to api/EnvironmentType, cloud/Sha256Digest to system/Sha256Digest) - retiring them would claim they left the published set.",
        "Docs redirects added for the retired references/cloud pages (the table's own convention); docs/adr/** and AGENTS.md untouched (governed) - AGENTS.md row filed as #17370.",
        "Local verification narrowed and declared (see tests): 77 affected packages, six run in full locally, cli integration tier and the rest to CI; pnpm lint is CI-owned and was not run.",
        "Verify-lock wrapper UNLOCKED on this host (no usable flock); nothing was serialized."
      ],
      "line_budget": "not applicable - no file under skills/** is touched (git grep on the diff: 0 hits under skills/ and .claude/)",
      "files_changed": 170,
      "mcp_calls": "0 - every GitHub read and write went through gh (REST): issue + comments read, search/issues x2 (directed, with a control term that returned 24), issues POST for #17370, pr create, labels POST, pulls GET read-backs, this comment",
      "open_questions": [
        {
          "question": "The relocated half's name: marketplace was chosen over package (both offered by the dispatch as honest). Consequence of the choice: the public subpath @objectstack/spec/marketplace that cloud's 4 PackageSchema importers and objectui prose will re-point to. Reason package was not chosen: src/package/ would produce a shard literally named package.json inside api-surface/, export-origins/, declaration-map/, json-schema.manifest/, authorable-surface/ and authorable-defaults/.",
          "options": [
            "A keep marketplace (as implemented)",
            "B rename to package-format or another word before merge (mechanical: one directory + the RENAMED_DEFS targets + regenerate)"
          ],
          "recommendation": "A, because it says what the half is (the marketplace protocol a package author publishes into) and avoids the package.json shard footgun; B is cheap while the PR is a draft if the maintainer prefers another word."
        },
        {
          "question": ".objectui-sha (53ded82bf) predates step 2's merge cb847fd6d and still carries export type * as Cloud from @objectstack/spec/cloud. Measured: build-console.sh against this tree is green (type-only, erased), so AGENTS.md Post-Task #4's premise (main goes red) does not hold here. When should the pin move?",
          "options": [
            "A land this PR without a pin bump; bump .objectui-sha (with pnpm sdui:manifest) on the objectui bump card that also moves the 19-to-18 pins",
            "B require the pin bump in this PR"
          ],
          "recommendation": "A, because the gate is measured green and a pin bump brings 76+ unrelated objectui commits and the Playwright sdui:manifest step into a spec contract PR; the card already records the bump as objectui-side follow-up."
        }
      ],
      "out_of_scope_findings": [
        "filed as #17370: AGENTS.md Protocol Domains table still lists the Cloud | cloud/ row (governed surface; no gate reads the table; one-line docs-only fix after this lands)"
      ]
    }
  12. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 16325,
      "status": "done",
      "branch": "claude/issue-16325-spec-drops-cloud-subpath",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17372",
      "premise_still_valid": true,
      "summary": "Patch round 1 (contract review), same claim, same branch, no rebase: head edfbc7f22 -> ef1fda259, one docs-only commit (3 files, 7 lines). (1) content/docs/deployment/troubleshooting.mdx's 'Available subpaths' now lists the ./* entries of packages/spec/package.json's exports map in the map's own order (16 entries, meta-spelling included) and names that source; the controlled sweep over content/docs/** found two more hand-written enumerations of the same set that no gate reads and corrected them: content/docs/plugins/packages.mdx's 15-namespace list, and content/docs/getting-started/glossary.mdx (its 15-namespace list, the layers table incl. tenants->packages in the purpose column, the '### Cloud Protocol' section now '### Marketplace Protocol', and the Tenant entry that named the deleted cloud/tenant.zod.ts as the schema's home). (2) The PR body's wrong claim is corrected: the retirement is registered in RETIRED_DEFS_BY_MAJOR[18] and the cloud-subpath-retired semantic entry (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'; pre-existing 18-bucket entries such as api/CrudEndpointPattern are absent from both, verified by grep), so neither carries it and neither is stale; a 'Patch round 1' section records both items and the sweep table. PR body edited via REST PATCH and read back byte-identical (first write stored no footer; a second write with a rule-less session-URL footer stored exactly one). Nothing else touched: the four consumer-less files' disposition, the naming, .objectui-sha and the checkRenameTable merge weakness the reviewer noted are untouched; docs/qa/platform-checklist/FOLLOW-UPS.md:269 untouched as history. concepts/architecture.mdx still carries a 'Cloud Protocol' card in its six-card conceptual layering; reported and filed (#17387), not edited, since it is a framing choice rather than a namespace list.",
      "tests": "Gates for this round: node scripts/pm/dispatch-gates.mjs --commands over the three docs files derives 39 families (the full changed-set derivation is 147, printed with a STALE TREE warning: 14 commits behind origin/main, not rebased per the dispatch). All 39 ran in the foreground on ef1fda259 with redirect-then-capture; all exit 0. Four first returned PREREQUISITE NOT MET (exit 3: check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift; exit 1: check:skill-examples, 'packages/client-react/dist holds no .d.ts') until 'pnpm exec turbo run build --filter=@objectstack/lint... --filter=@objectstack/client-react... --concurrency=2' (34 tasks, 1m35s) and 'pnpm --filter @objectstack/spec build' (39s) ran under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 both; 'UNLOCKED (declared)' — no usable flock on this host); then 0 with the gates' own lines: '✅ 258 prose examples type-check across 3 surface(s)', '✓ check-docs-transcript-drift: 4 declared transcript value(s) across 401 page(s) under content/docs/ equal what the registry derives today', '✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 438 files / 1374 TS blocks judged clean', '✅ 27 ObjectSchema.create example(s) in 227 marked block(s) across 239 prose file(s) ... carry an os validate-clean security posture'; check:docs, check:liveness, check:empty-state, check:yaml-examples, check:variant-docs, check:strictness-ledger, check:corpus-claim-drift, check:role-word, check:docs-redirects, check:doc-anchors, check:nul-bytes and the rest of the 39 all exit 0. --ran reconciliation with per-command exit codes: '39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero)'. Declared narrowing: the 108 code-derived families of the full set derive from files unchanged since edfbc7f22, where they ran last round (CI on edfbc7f22: 35 success / 6 skipped / 0 failed); not re-run for a docs-only delta; pnpm lint and the cli integration tier remain CI's. Docs sweep on ef1fda259 (git grep -E over content/docs, each zero beside a same-shape control that fires): import-shaped @objectstack/spec/cloud = 0 (controls: spec/data 131, spec/marketplace 8; a first attempt with a backslash-s class had a 0 control on macOS git grep and was discarded as not a reading); namespace lists naming Cloud ('Kernel, Cloud' / 'Security, Cloud' / the backticked cloud-then-qa fragment) = 0 (control naming Marketplace: 4); spec source paths cloud/NAME(.zod).ts = 0 (control marketplace/: 8); any 'spec/cloud' mention = 2, both historical (permissions/system-context.mdx:54 'left this repo with the ... subpath'; generated references/system/environment-artifact.mdx:12 're-exported this file until') with control spec/marketplace 9 lines / 5 files; residual 'cloud protocol' (case-insensitive) = 2 lines, both concepts/architecture.mdx (49, 63), filed #17387. Edits landed by an anchored script that refused unless each anchor occurred exactly once and each replacement landed exactly once (6/6). CI on ef1fda259 at report time: 46 check runs — 19 success, 7 skipped, 16 in progress, 4 queued, 0 failed (the required contexts Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance were still running); not waited for, per the dispatch contract.",
      "mcp_calls": "0 — every GitHub read and write went through REST (gh api): PR read/PATCH x2 with read-back, issue comments read, two issue creates, the report comment. Duplicate search before filing used the REST search endpoint (targeted queries; the control query for 'spec/cloud subpath' returned #16325 among 16 hits).",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #17387 (label finding, unassigned): content/docs/concepts/architecture.mdx still presents a 'Cloud Protocol' card (Management: Multi-tenancy, Marketplace, Licensing) and a 'System · Automation · Cloud Protocol' mermaid node after the ./cloud subpath left the spec — a framing choice for the maintainer (rename to Marketplace Protocol / keep as a product-level card worded as cloud-hosted / fold into System), reported rather than edited",
        "filed as #17388 (label finding, unassigned): no gate holds the hand-written docs enumerations of @objectstack/spec subpaths and protocol namespaces (troubleshooting.mdx, plugins/packages.mdx, glossary.mdx x3) equal to the exports map — every mechanical counter moved with the ./cloud deletion and all three pages stayed stale; cheapest fix is extending the runtime subpath-coverage pin that already derives PUBLISHED_SUBPATHS from package.json"
      ]
    }

    Generated by Claude Code

  13. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    ACCEPT — PR #17372 at ef1fda259. Contract-review-tier verdict: PASS. Step 3 of 3 goes to the queue.

    Reviewer of record for clause ②: the contract-review-tier subagent (this seat does not hold that tier, so the
    clearance is not this seat's to give). Its PASS was issued on edfbc7f22; the patch round that followed is
    docs-only and leaves that verdict's subject untouched — verified below, not assumed.

    The check I most wanted, and how it was answered

    The dev modified a ratchet while using it (carryDefaultsThroughRenames). That is the canonical way a
    regression hides, so it was the review's first priority. It was answered by a three-leg ablation against the real
    gate, not by reading the code:

    leg action result
    A as committed exit 0, 🔒 1168 default(s) unchanged
    B remove carryDefaultsThroughRenames exit 1, 22 (none) → value (added) — reproduces the dev's account verbatim
    C keep the fix, mutate a real default (src/marketplace/package.zod.ts:219 visibility 'private' → 'org') exit 1, marketplace/Package:visibility: "private" → "org" (changed)

    ⭐ Leg C is the discriminating one: after the fix, a genuine default change on a renamed def is still caught.
    The change removes false positives only. Files restored and status clean after each leg.

    The published-surface arithmetic closes, both directions

    exports map delta: only ./cloud leaves, ./marketplace arrives. Base ./cloud carried 94 defs =
    62 entered RETIRED_DEFS_BY_MAJOR[18] + 32 carried by RENAMED_DEFS; intersection empty, and
    base − (retired ∪ renamed) empty in both directions ⇒ no def silently vanished. At symbol level, the 76
    exports coming from the five retained modules are identical base → head; four of the five relocated files are
    blob-identical, the fifth differs by three docblock lines. EnvironmentTypeSchema's seven members and their
    .describe text are verbatim identical to the deleted original.

    Rest of the checklist

    item reading
    ⚠️ ./marketplace drops the browser condition ./cloud had measured sound: dist/marketplace/index.mjs links pg-connection-string / node:* / process. 0 times (control dist/kernel/index.mjs 4); a source-level transitive walk never reaches pg-url-grammar.server. The condition's only mechanism was swapping that server module — this graph has no such seam
    counter re-sweep spec/cloud|src/cloud under packages/ docs/design: head 60 vs base 313 (firing control). All 60 classified — CHANGELOG, prose, the #9457 incident comments, registry strings, substring false hits. No import, no alias table, no counter
    weakened assertions each re-derived as measured, not made to pass: file-description.test.ts exact 31 independently cross-checked (6 of 7 deleted files opened at level 1, 37−6=31); type-alias-convention 782 recomputed at runtime; census anchors pointed at files that no longer exist
    CI 49 check runs, reconciled against total_count: 49 — 42 success, 7 skipped, 0 otherwise. Each skip legitimately filtered (Console Pin Gate's path filter; opt-in canary/tarball; duplicate PR Automation activity)
    path surface pulls/17372/files paginated: 173 == changed_files 173; 0 files under docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md, content/docs/releases/. Not a governed-surface PR
    patch-round delta edfbc7f22..ef1fda259 = 3 docs files, 7 lines; git diff --name-only over packages/spec/package.json, src/**, scripts/** ⇒ 0 ⇒ the reviewed contract surface is untouched

    ⚠️ Two divergences recorded, neither blocking

    1. The ruling said "major"; this ships minor. scripts/check-changeset-no-major.mjs carries a launch-window
      guard that mechanically refuses any changeset declaring major. The substance is preserved — the changeset
      leads with **BREAKING**, quotes the no-alias/no-window ruling, carries adr-0087: registered cloud-subpath-retired, and tables the FROM→TO for four consumer classes. The structural cost is that
      changesets will file it under "Minor Changes" in the CHANGELOG. Raised with the maintainer; ⛔ this seat did not
      override a mechanical gate to satisfy a ruling's wording.
    2. This seat's landmine list was one-third wrong. Of the three counters named in the dispatch, the two
      vitest.config.ts alias tables needed no edit — cloud survives there only inside the packages/runtime's vitest alias list omits @objectstack/spec/cloud, so any runtime test that loads a compiled artifact dies with ENOTDIR #9457 incident
      prose. Reading grep hits without separating code from comment; the dispatch's instruction to re-sweep rather
      than trust the list is what caught it, and the dev's own sweep then found ~25 counters the list had missed.

    Landing

    Ready + queue. objectstack-ai/objectstack has a merge queue — ruleset 12119582 (main), rule types
    deletion, non_fast_forward, merge_queue, pull_request, required_status_checks, merge_method: SQUASH,
    grouping_strategy: ALLGREEN. ⛔ Nothing bypassed. Watching to MERGED.

    Follow-ups filed during this step, all measured rather than guessed: #17370 (AGENTS.md still lists the Cloud
    protocol row — governed, docs-only), #17383 (pre-existing checkRenameTable merge hole the reviewer found
    while checking the gate fix), #17387 (concepts/architecture.mdx's Cloud Protocol card — a framing choice,
    reported not edited), #17388 (⭐ no gate holds the docs' hand-written subpath enumerations equal to the
    exports map
    — every mechanical counter moved with this deletion and all three prose pages stayed stale until
    the patch round; the cheapest fix extends the existing pin that already derives PUBLISHED_SUBPATHS from
    package.json).

  14. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    MERGED — the three-step chain is complete. pm:dispatched stripped in the same write.

    Dispatching PM seat, 2026-09-10T11:5xZ.

    Two readings, with same-shape firing controls on every zero.

    1. pulls/17372 ⇒ merged: true, merged_at: 2026-09-10T11:44:40Z, merge commit 776d64cd3, via the merge queue.
    2. On the ref, not a working tree:
    $ git log --oneline -1 origin/main
    776d64cd3 feat(spec)!: drop the `@objectstack/spec/cloud` subpath …
    
    $ git show origin/main:packages/spec/package.json | grep -c '"\./cloud"'          → 0
    $ git show origin/main:packages/spec/package.json | grep -c '"\./marketplace"'    → 1   ← control, same shape
    $ git ls-tree --name-only origin/main packages/spec/src/cloud/       | wc -l      → 0
    $ git ls-tree --name-only origin/main packages/spec/src/marketplace/ | wc -l      → 10  ← control
    

    ⇒ each zero sits beside a same-shape reading that fires, so it is a real absence rather than a broken pattern.

    Auto-close left pm:dispatched on a closed card — the same half-state #16450 and objectui#8225 left. Stripped;
    domain:spec, enhancement, priority:p2 stay, because ownership and type are not state.

    The chain, end to end

    step card PR merged
    1 — cloud owns its control-plane contracts #16450 objectstack-ai/cloud#2037 2026-09-07T08:19Z
    2 — objectui drops the Cloud re-export objectstack-ai/objectui#8225 objectstack-ai/objectui#8360 2026-09-07T17:15Z
    3 — spec deletes ./cloud, package format → ./marketplace this card #17372 2026-09-10T11:44Z

    Both rulings are discharged: option B (cut by owner) and option A on the four consumer-less files.

    What #12036 asked, and what actually answered it

    This chain began at #12036 — "the cloud contracts are camelCase, the wire is snake_case, 15 client methods cannot
    be bound". That card was closed not_planned as superseded, and the reason is worth keeping: the casing question
    was never answerable, because it was the wrong question.
    It only looked like a cross-repo contract decision
    because the declarations lived in the open-source spec while the implementation lived in the cloud repo. With the
    declarations beside their producer, casing became one repo's internal style — decided there, with no maintainer
    ruling and no three-repo window.

    Follow-ups left open, every one measured rather than guessed

    card what
    objectstack-ai/cloud#2041 environmentType silently defaulted at two layers — staging / test / preview / trial provision the wrong environment type with no error
    objectstack-ai/cloud#2036 objectos ambient stub masking two strict-null errors
    objectstack-ai/objectui#8361, #8404 four scripts/__tests__ suites are host-dependent on macOS (BSD regex \b, BSD grep binary-file line, bash 3.2, missing timeout)
    #17370 AGENTS.md still lists the Cloud protocol row (governed, docs-only)
    #17383 pre-existing checkRenameTable merge hole, found while adversarially checking this PR's gate fix
    #17387 concepts/architecture.mdx's Cloud Protocol card — a framing choice, reported not edited
    ⭐ #17388 no gate holds the docs' hand-written subpath enumerations equal to the exports map

    ⭐ #17388 is the one worth acting on, and it is this chain's own lesson turned on itself. Deleting the subpath
    moved every mechanical counter — the anti-vacuity floor pin that FAILed step 2, the alias-coverage pin, the
    export-origins table, ~25 more the dev's sweep found. All three prose pages that enumerate the same set stayed
    stale, because nothing reads them. The chain spent three steps fixing declarations that had drifted from reality;
    the documentation is the same defect in the corner no gate covers. The cheapest fix extends the pin that already
    derives PUBLISHED_SUBPATHS from package.json.

    Downstream work this card creates, for whoever picks it up

    • objectstack-ai/cloud has 4 PackageSchema from '@objectstack/spec/cloud' importers to re-point at
      @objectstack/spec/marketplace when it bumps its spec dependency.
    • objectstack-ai/objectui must re-pin scripts/__tests__/vite-objectstack-spec-dist.test.ts:152 / :153 / :608
      (all 19) in the same PR that bumps its spec dependency. ⚠️ Diff both manifests rather than assuming 18 — the
      final number depends on that release's full exports-map delta.
    • .objectui-sha bump stays the objectui-side follow-up. build-console.sh was measured green against this tree,
      and the gate that would actually catch a regression is cut-rc.yml's pnpm objectui:build, ⛔ not the
      path-filtered Console Pin Gate, which runs on neither this PR nor main.
  15. hotlong commented on Sep 10, 2026

    @hotlong
    ContributorAuthor

    ⚠️ Correction to the closing note above — .objectui-sha is an objectstack-side file, not objectui-side

    Dispatching PM seat, 2026-09-10T14:1xZ. ⛔ The note above stands; this is the correction, not a rewrite.

    What I wrote: ".objectui-sha bump stays the objectui-side follow-up."

    What is true: .objectui-sha lives in this repo — objectstack-ai/objectstack, repo root — and is read by five workflows here (ci.yml, cut-rc.yml, lint.yml, release.yml, showcase-smoke.yml). It is objectstack's pin OF objectui, so bumping it is work in this repo, not in objectui. Routing it to the wrong repo would have left it with no owner, since neither seat would have found it in their own queue.

    Also measured, and it changes the framing: the pin is 584 commits behind objectui's main (53ded82bf → efead6c60). So this is ordinary pin drift on a routine cadence, ⛔ not a loose end this chain created — the chain merely gave one specific reason to care, namely that objectui#8360 (step 2, dropping the Cloud re-export) is among those 584 commits.

    Filed as #17429 in this repo, pm:queue, so it has an owner. It is not urgent: build-console.sh was measured green against the merged tree, because the line step 2 removed was type-only and erased at build time. The gate that would actually catch a regression here is cut-rc.yml's pnpm objectui:build, ⛔ not the path-filtered Console Pin Gate, which runs on neither a normal PR nor main.

    The two genuinely chain-created downstream cards, both pm:on-hold with machine-readable restart conditions, are unaffected by this correction: objectstack-ai/cloud#2173 (re-point 4 PackageSchema importers) and objectstack-ai/objectui#8979 (re-pin the three exports-map counts).


    ⚠️ Edited once, immediately after posting: this comment first named the new card #17434, a number written before the card was created rather than read back from the response. The card is #17429. Recorded rather than silently fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions