Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[finding] CloudOperations is a phantom surface: the installed @objectstack/client has no cloud namespace, so deploy() fabricates a deploymentId for work that never happened #4152

Closed

Description

@yinlianghui

Found while implementing #3720 (measuring what the deploy API's accepted vocabulary actually is). Filed unassigned, recording only — PD #10.

Fact

packages/data-objectstack/src/cloud.ts exports a CloudOperations class whose four methods all call into client.cloud?.*. The installed @objectstack/client@17.0.0-rc.5 exports no cloud namespace at all — its export list is ObjectStackClient, ScopedProjectClient, RealtimeAPI, QueryBuilder, FilterBuilder and their types. There is no deploy, no getDeployment, no marketplace.

The constructor's getClient is typed to return any, so every one of these optional chains resolves undefined at runtime and falls through to its literal fallback:

method what it returns when the client has no cloud surface (i.e. always)
deploy(appId, config) { deploymentId: 'deploy-' + Date.now(), status: 'pending' }
getDeploymentStatus(id) { status: 'unknown' }
searchMarketplace(...) []
installPlugin(id) { success: false }

CloudOperations and the three Cloud*Config / CloudMarketplaceEntry types are re-exported from packages/data-objectstack/src/index.ts (lines 3783-3784), so this is published public surface of @object-ui/data-objectstack. Nothing in this repo constructs it except src/v3-compat.test.ts, whose two cloud cases assert only that the fallback has the right keys — which is why the emptiness has never shown up as a failure.

Why this is graded observation-class (and why that grade may be wrong)

By the stated criterion it is dormant: no in-repo consumer wires CloudOperations, so nobody is bitten today.

The reason I would not be surprised to see this promoted: deploy() does not degrade to an error, it manufactures a plausible-looking success. A caller gets a well-formed deploymentId and status: 'pending' for an operation that never left the process, then polls getDeploymentStatus(thatId) and receives { status: 'unknown' } forever. That is the consumer-side-tolerance shape AGENTS.md #12 exists to forbid, sitting in shipped public API rather than in a private helper — a latent defect rather than merely an observation. Severity judged at filing time being unreliable, I am recording both readings and leaving the grade to triage.

Adjacent, same file, same theme

The module header still reads Cloud namespace integration for @objectstack/spec v3.0.0 / Replaces the legacy Hub namespace. The resolved spec is 17.0.0-rc.5, and the "cloud deployment, hosting, and marketplace schemas" it claims to integrate are not what the package consumes. Noted here rather than as its own card, since any disposition of the surface above settles it too.

Possible directions (left for triage)

Whichever way it goes, #3720's pin test and comment on environment stay correct — they document a deploy-target vocabulary and explicitly name the missing producer as the reason nothing is imported.

Refs #3720, PR #4151.


Generated by Claude Code

Activity

  1. claude commented on Aug 11, 2026

    @claude
    Contributor

    Maintainer ruling — 2026-08-11. From the four-lens decision review (platform long-term coherence / measured business pull / AI-agent error-resistance / startup scope discipline); the maintainer accepted the recommendation set in full.

    Ruling: remove the CloudOperations surface — the class and the Cloud*Config / CloudMarketplaceEntry types leave the public export of @object-ui/data-objectstack; if a stub must remain for compile compatibility, every method throws a loud NotImplemented. Fabricated success (deploy()'s synthetic deploymentId, the eternal status: unknown poll) is banned — it is the most dangerous shape for an AI consumer, which will build downstream logic on the fake id rather than get suspicious. The false module-header claim goes with it. State: needs-user-decision + finding → pm:queue.

    Recorded by the triage seat Routine (#5474 pilot) on the maintainer's direct instruction — ruling record, not a claim.


    Generated by Claude Code

  2. self-assigned this
    on Aug 11, 2026
  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    ContributorAuthor

    CLAIM (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3) — executing the maintainer ruling above; dispatching a dev agent now.

    • Branch claude/issue-4152-retire-cloud-operations, worktree objectui-issue-4152 (S/M container)
    • Scope = the ruling as written: CloudOperations + the Cloud*Config/CloudMarketplaceEntry types leave @object-ui/data-objectstack's public export; a compile-compat stub (only if measured necessary) throws loud NotImplemented; fabricated success is banned; the false module header goes. v3-compat.test.ts's two cloud cases retire/convert with the surface; [finding] CloudDeploymentConfig.environment 手写第三套环境拼写('development'|'staging'|'production'),与 spec 两个枚举均不吻合 #3720's environment pin gets measured and reconciled (its subject may retire with the types — state what happens to it). Changeset per the repo's retirement precedent, bump-policy gate arbitrates, never major.

    Generated by Claude Code

  4. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    ContributorAuthor

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4239, executing the maintainer's removal ruling, CI fully green.

    The verdict discipline is the story here. The premise was re-probed at rc.6 before anything moved (the ruling was made against rc.5's export list; it holds — .cloud is still undefined), and the report names the two REAL namespaces (projects for /cloud/environments, packages for marketplace installs) that any future revival should target instead. Stub-vs-delete was decided by measurement, not preference: 34 of 46 workspace projects type-check green with the surface gone, so the stub the ruling permitted "if demonstrated necessary" was demonstrated UNnecessary — "a second phantom guarding the first." The #3720 reconciliation went the full distance: the pin retired WITH its subject after a grep proved it was the only in-repo reader of the upstream enums it guarded, and its pending empty changeset was removed with the touch on another card's artifact flagged explicitly. The replacement negative pin reads BOTH the runtime export list and index.ts's source text, and the reverse verification measured exactly why both halves exist — export type erases at runtime, so the three type names stayed green in the runtime half and were caught only by the source half. The predicted-count correction (8 not 7, direction right) is stated rather than smoothed. Changeset minor per the #4145 retirement precedent. #4241 (the four non-phantom Deep Integration modules) graded observation-class correctly — they do what they claim, which is the exact line this card's subject crossed. Flipping ready + auto-merge.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions