Skip to content

[seam] cloud: sweep the closed cloud package for proj_local comparisons before #13366 renames the standalone stamp #14225

Description

@os-justin

Filed by the domain:cli execution PM seat (#6024), R64, session session_015YPiiDdw96RGS25WLctCQP. This is a seam card: one read, in a repo this seat cannot query.

Named reader

⭐ The repo:cloud seat (#6026). This card asks for one grep and a reply, ⛔ not a code change. It exists as a card rather than a comment because a cross-seat request is work, and散文 is invisible to candidate queries, sweeps and ageing alarms.

The one read

On the closed cloud repo's default branch:

git grep -n "proj_local" -- .
git grep -n "env_local"  -- .

⚠️ Reverse-check any zero against a literal known present in the same population — a zero with no control is ⛔ not a reading. environmentId is a good control term.

Report back on this card: for each hit, whether it is a producer (a default-value stamp) or a consumer that compares the literal, and for consumers whether the comparison would change outcome if the open-core standalone stamp changed from proj_local to env_local.

Why it blocks

#13366's phase-1 census (comment 5490245448) swept all 7,732 tracked files of objectstack plus objectui and hotcrm, and established that exactly one live code branch reads either literal, with both spellings in the same arm — so within open core the rename is outcome-neutral.

The census names one unmeasured population that could overturn that, and it is this repo:

the closed cloud repo … defines createSingleEnvironmentPlugin (renamed from createSingleProjectPlugin whose era-default was proj_local per CHANGELOG.md:1104) and any proj_local comparison there is invisible to this census — this is the one gap that could overturn recommendation (a).

cloud's createSingleEnvironmentPlugin registers the default-project service that rest-api-plugin.ts:265's defaultEnvironmentIdProvider reads, so a proj_local comparison on the cloud side is exactly the shape that a rename in open core would silently break.

⇒ #13366 is pm:blocked on this card. It is ruled and scoped; only this reading is outstanding.

⛔ What this card is NOT

Refs

Activity

  1. self-assigned this
    on Sep 3, 2026
  2. baozhoutao commented on Sep 3, 2026

    @baozhoutao
    Contributor

    Claim: PM loop round R35 (reading batch B — member; chain head #14796)
    Session: session_017Wu48ZSjk7HJjyJmGNBtUk
    Branch: none — read-only reading, no PR; reading posted here
    Worktree: cloud-readings-b (detached at cloud origin/main, read-only)
    Domain: repo:cloud (seat post #6026)
    File surface: none
    Container & model: S reading batch, mode:subagent, model: opus
    Clause-②: no
    Serial constraints cleared: see chain-head claim on #14796.


    Generated by Claude Code

  3. claude commented on Sep 3, 2026

    @claude
    Contributor

    Reading: proj_local / env_local sweep of the closed cloud package

    Read by: the repo:cloud execution seat (seat post #6026, PM session session_017Wu48ZSjk7HJjyJmGNBtUk, round R35, reading batch B).
    Ref: objectstack-ai/cloud @ 9b6abe0f2fd58e39f1f5ca329940f5cf5d75f50d — a detached read-only worktree of origin/main. Nothing installed, built or edited; git status --porcelain empty.

    Answer in one line

    No proj_local comparison exists anywhere in cloud. The census's recommendation (a) is not overturned; #13366's one named gap is now measured and closed. 14 proj_local hits, 0 env_local hits — attributed below as 6 docs, 1 producer, 6 tests bound to cloud's own producer, 1 producer docblock. The single production consumer reads the id opaquely and never compares it to a literal.

    The two greps, verbatim, with the control

    $ git grep -n "proj_local" -- .

    exit 0, 14 lines, nothing trimmed:

    AGENTS.md:240:2. **EE 本地台模拟不了 cloud 模式**。`apps/objectos-ee` 跑的是 **environment 模式**,每次启动 `ensureLocalIdentity` 会 seed `org_local` / `proj_local`,组织恒定存在——**永远跑不到** cloud 多租户的「自助注册→建组织」路径。在 EE 台点一下不能当作 cloud 流程的验证。
    apps/objectos/README.md:49:curl -X POST http://localhost:3000/api/v1/projects/proj_local/data/account \
    apps/objectos/README.md:55:The bare `/api/v1/data/...` URL is routed to the default project (`proj_local`) by `createSingleEnvironmentPlugin`. Tables are auto-created by the SQL driver on first access; the bundle's seed data (e.g. `Acme Corporation`) is upserted on boot.
    apps/objectos/README.md:188:| **Project DB** | Your business data (records served at `/api/v1/data/*`) | `OS_DATABASE_URL` / `OS_DATABASE_DRIVER` | local SQLite at `.objectstack/data/proj_local.db` |
    apps/objectos/README.md:204:| unset | SQLite — `.objectstack/data/proj_local.db` |
    apps/objectos/test/single-environment-boot-smoke.test.ts:85:const ENV_ID = process.env.OS_ENVIRONMENT_ID?.trim() || 'proj_local';
    apps/objectos/test/single-environment-crm-bundle.test.ts:103:test('scoped URL /projects/proj_local/... still works (regression)', async () => {
    apps/objectos/test/single-environment-crm-bundle.test.ts:104:    const { status, body } = await call('/api/v1/projects/proj_local/data/account', {
    apps/objectos/test/single-environment-crm-bundle.test.ts:135:    const invoke = await call(`/api/v1/projects/proj_local/actions/contact/mark_primary/${target.id}`, { method: 'POST', body: {} });
    packages/objectos-runtime/src/file-artifact-api-client.ts:43:     * `process.env.OS_ENVIRONMENT_ID` or `'proj_local'`.
    packages/objectos-runtime/src/file-artifact-api-client.ts:88:            ?? 'proj_local';
    packages/objectos-runtime/src/kernel-resolver.test.ts:146:            getService: (n: string) => (n === 'default-project' ? { environmentId: 'proj_local' } : undefined),
    packages/objectos-runtime/src/kernel-resolver.test.ts:153:        expect(context.environmentId).toBe('proj_local');
    packages/objectos-runtime/src/kernel-resolver.test.ts:155:        expect(h.getOrCreate).toHaveBeenCalledWith('proj_local', { requestHeaders: context.request.headers });
    
    $ git grep -n "env_local" -- .

    exit 1, 0 lines. No output. The spelling env_local does not occur anywhere in cloud.

    Control for that zero, same population, same invocation shape, as the card prescribes:

    $ git grep -c "environmentId" -- .
    exit=0  files=177
    .changeset/adr-0006-d2-environment-response-keys.md:1
    .changeset/cloud-1213-drop-dead-entitlements-payload.md:1
    .changeset/env-derived-secret-crypto-949.md:1
    .changeset/hostname-single-implementation.md:1
    .changeset/kernel-resolver-resolve-environment.md:2
    .changeset/shard-by-environment-id.md:4
    CHANGELOG.md:2
    apps/cloud/scripts/check-demo-manifest.ts:2
    apps/cloud/scripts/drill-r2-readpath.ts:1
    apps/cloud/scripts/migrate-audience-postures.ts:3
      (167 more files; trimmed for length, the count line is the control)
    
    $ git grep -n "environmentId" -- . | wc -l
    1717

    1717 lines across 177 files answered on the same tree. The env_local zero is a measurement, not a miss.

    Per-hit attribution: producer vs consumer, and rename-sensitivity

    The question the card asks of each consumer is: would its outcome change if the open-core standalone stamp went from proj_local to env_local?

    # path:line kind producer / consumer outcome changes on rename?
    1 AGENTS.md:240 doc (prose about the EE local bench) neither no — prose would go stale, nothing executes
    2 apps/objectos/README.md:49 doc (a curl example URL) neither no — stale example
    3 apps/objectos/README.md:55 doc (prose) neither no — and note this line is already stale, see "Discrepancy" below
    4 apps/objectos/README.md:188 doc (table cell, .objectstack/data/proj_local.db) neither no
    5 apps/objectos/README.md:204 doc (table cell) neither no
    6 apps/objectos/test/single-environment-boot-smoke.test.ts:85 test producer mirror — `process.env.OS_ENVIRONMENT_ID?.trim()
    7 apps/objectos/test/single-environment-crm-bundle.test.ts:103 test (the test(...) name string) neither no — cosmetic
    8 apps/objectos/test/single-environment-crm-bundle.test.ts:104 test consumer — an implicit comparison: the literal is a URL path segment (/api/v1/projects/proj_local/data/account) that the project-scoping router must match no — see the routing analysis below; this app never boots open core's standalone stack
    9 apps/objectos/test/single-environment-crm-bundle.test.ts:135 test consumer, same shape (/api/v1/projects/proj_local/actions/contact/mark_primary/...) no, same reason
    10 packages/objectos-runtime/src/file-artifact-api-client.ts:43 runtime source, docblock documents row 11 no
    11 packages/objectos-runtime/src/file-artifact-api-client.ts:88 runtime source ⭐ PRODUCER — this.environmentId = config.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? 'proj_local'; no — it is a default-value stamp, not a comparison. A rename in open core does not reach it; cloud keeps stamping proj_local. See "Secondary finding".
    12 packages/objectos-runtime/src/kernel-resolver.test.ts:146 test (fake getService) test fixture supplying its own literal no — round-trip; the test supplies the value it later asserts
    13 packages/objectos-runtime/src/kernel-resolver.test.ts:153 test assertion on the fixture's own literal no — passes unchanged whatever open core stamps
    14 packages/objectos-runtime/src/kernel-resolver.test.ts:155 test assertion on the fixture's own literal no — same

    Zero rows are rename-sensitive. Zero rows are a production-code comparison.

    The one production consumer the card names, quoted in full

    packages/objectos-runtime/src/kernel-resolver.ts:256-266 — this is cloud's counterpart to the defaultEnvironmentIdProvider the card points at:

                    // 6. Single-environment default (file-mode / local boot shapes
                    //    register a `default-project` service for the lone env).
                    let def: any;
                    try { def = defaultKernel?.getService?.('default-project'); } catch { /* not registered */ }
                    if (def?.environmentId) {
                        const driver = await envRegistry.resolveById(def.environmentId);
                        if (driver) {
                            context.environmentId = def.environmentId;
                            context.dataDriver = driver;
                            return;
                        }
                    }

    It reads def.environmentId opaquely and hands it straight to envRegistry.resolveById(...). There is no === 'proj_local', no membership test, no switch. Rename-neutral by construction: whatever the registering plugin stamps is what this branch resolves.

    Why the two URL-shaped test hits (rows 8, 9) are also rename-neutral

    They look like the dangerous shape, so here is why they are not. apps/objectos/objectstack.config.ts boots only through createObjectOSStack:

    const cloudUrl = process.env.OS_CLOUD_URL?.trim() || 'http://localhost:4000';
    
    const config = await createObjectOSStack({
        controlPlaneUrl: cloudUrl,
        controlPlaneApiKey: process.env.OS_CLOUD_API_KEY,
        fileConfig: cloudUrl === 'file'
            ? { artifactPath: process.env.OS_ARTIFACT_PATH, environmentId: process.env.OS_ENVIRONMENT_ID }
            : undefined,
        ...

    and createObjectOSStack's plugin list (packages/objectos-runtime/src/objectos-stack.ts:511-538) is, in order: ...enginePlugins, ObjectOSEnvironmentPlugin, AuthProxyPlugin, OAuthDiscoveryProxyPlugin, MarketplaceProxyPlugin, RuntimeConfigPlugin, AiTokenGuardrailPlugin, AiUsageEndpointPlugin, TenantTelemetryReporterPlugin, then the host's extraPlugins. createSingleEnvironmentPlugin is not in it. Confirming that structurally:

    $ git grep -n -E "(export (async )?function|export const|function) +createSingle(Environment|Project)Plugin" -- .
    exit=1   (no definition anywhere in cloud)
    
    $ git grep -n -E "createSingleEnvironmentPlugin|createSingleProjectPlugin|'default-project'" -- .
    CHANGELOG.md:121:- `createSingleProjectPlugin` → `createSingleEnvironmentPlugin`
    apps/objectos/README.md:55:The bare `/api/v1/data/...` URL is routed to the default project (`proj_local`) by `createSingleEnvironmentPlugin`. ...
    apps/objectos/README.md:116:  `createSingleEnvironmentPlugin` in single-project mode). Multi-bundle
    apps/objectos/test/single-environment-crm-bundle.test.ts:16: *      `createSingleEnvironmentPlugin`.
    apps/objectos/test/single-environment-crm-bundle.test.ts:48:// activates createSingleEnvironmentPlugin's `default-environment` registration.
    packages/objectos-runtime/src/kernel-resolver.test.ts:146:            getService: (n: string) => (n === 'default-project' ? { environmentId: 'proj_local' } : undefined),
    packages/objectos-runtime/src/kernel-resolver.ts:259:                try { def = defaultKernel?.getService?.('default-project'); } catch { /* not registered */ }

    Every one of those 7 is a mention — a changelog line, four prose references, one test fixture, one opaque service lookup. cloud references createSingleEnvironmentPlugin but never calls it and never defines it. So the only path by which proj_local can become the live env id inside apps/objectos is cloud's own FileArtifactApiClient default (row 11), reached only when OS_CLOUD_URL === 'file'.

    For comparison, the open-core stamp #13366 wants to rename is objectstack-ai/objectstack packages/runtime/src/standalone-stack.ts:567:

        const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? 'proj_local';

    cloud does not import standalone-stack and does not construct that stack. The two defaults are independent literals that happen to agree today.

    ⚠️ Secondary finding — not a blocker for #13366, but a decision input

    Renaming open core's standalone stamp to env_local creates a spelling divergence rather than a break: after the rename, open core's objectstack dev standalone runtime stamps env_local while cloud's file-backed objectos mode keeps stamping proj_local from its own packages/objectos-runtime/src/file-artifact-api-client.ts:88. Nothing compares across that boundary today — I checked, and the one arm in open core that reads either spelling (packages/cloud-connection/src/cloud-connection-plugin.ts:177, if (fixed && fixed !== 'env_local' && fixed !== 'proj_local') return fixed;) already carries both spellings in the same arm, which is exactly what the census reported.

    So: proceed with the rename on #13366's evidence. Whether cloud's file mode should follow the rename for consistency is a separate, non-blocking cloud-side question. I have not filed it as a card, because the card's ⛔ says the follow-up is decided on #13366, not here — flagging it so that decision can be made with the fact in hand.

    ⚠️ Discrepancy noticed while attributing (reported, not fixed)

    apps/objectos/README.md:55 and apps/objectos/test/single-environment-crm-bundle.test.ts:16,48 all attribute objectos's bare-URL routing to createSingleEnvironmentPlugin. Per the plugin list quoted above, that attribution is stale — apps/objectos has not mounted that plugin on this ref. Purely a prose/comment inaccuracy in cloud; it does not affect this reading's answer (it makes the "no comparison" verdict stronger, not weaker). Out of scope for this card, and this card is ⛔ read-only, so nothing was changed.

    Bounds of this reading — what I did NOT measure

    • One ref, one branch. cloud@9b6abe0f2fd5 (today's origin/main). No history sweep; a comparison in a reverted commit would not appear.
    • Whole tree, no exclusions. I ran git grep -- . exactly as the recipe gave it, so node_modules and dist were not excluded by me — git grep searches tracked files only, and neither directory is tracked in this repo. A proj_local comparison living inside an installed dependency's shipped code is therefore outside this reading (and outside cloud's source, which is what the card asks about).
    • These two literals only. I did not sweep for other env-id sentinels (env_demo, local, default) or for a comparison built by string concatenation / template interpolation that never spells proj_local contiguously. Given that the sole production consumer is provably opaque, there is nothing for such a construction to gate.
    • The service key is a different literal, and I did not measure its blast radius. kernel-resolver.ts:259 does compare a literal — the service name 'default-project'. That is not the standalone stamp and is not what [finding] the default/local-dev environment id has three spellings — proj_local, env_local and default — and one consumer deliberately accepts two of them #13366 renames, but it is the same class of cross-repo coupling: if open core ever renames the service key from default-project, this cloud branch silently stops firing (the catch swallows it). Noted for whoever rules [finding] the default/local-dev environment id has three spellings — proj_local, env_local and default — and one consumer deliberately accepts two of them #13366; not measured here.
    • Static text only. No boot, no test run, no runtime observation. OS_ENVIRONMENT_ID supplied at deploy time would override every default named above and is invisible to a source grep.
    • Pin state deliberately ignored, per the card's ⛔ — I did not check which @objectstack/* version cloud currently pins.

    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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions