Skip to content

fix(cli): os environments runs on the os cloud login session through one shared resolver - #21400

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21360-environments-cloud-session
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21360-environments-cloud-session

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21360
Clause-②: yes (widening)

What changes

The five os environments subcommands (list, show, create, bind, switch) built their client with createApiClient, which reads only ~/.objectstack/credentials.json (the os login session). With only ~/.objectstack/cloud.json, which is the state after os cloud login, all five exited 1 with Authentication required before sending a request. Meanwhile os login --help sends hosted users to os cloud login, so the documented hosted flow looped.

This follows triage's ruling 5947754514 (shape 1). All five now choose their session in one shared resolver, createControlPlaneApiClient in packages/cli/src/utils/api-client.ts, which picks in this order:

  1. credentials.json's session where it targets the server the command talks to. With no --url / OS_CLOUD_URL, it names the server itself, so a user with an os login session sees no change.
  2. Otherwise cloud.json's session, on the same terms. Its server is its recorded url, or https://cloud.objectos.ai when it records none.
  3. Otherwise, when an explicit url names neither file's server, credentials.json's session as before. cloud.json's token is never sent to a url other than its own.

Explicit flags and env vars (--url / OS_CLOUD_URL, --token / OS_TOKEN, OS_ENVIRONMENT_ID) still win field by field, as in createApiClient. Other details:

  • The active environment sent as X-Environment-Id comes from the chosen session's file.
  • The result carries session: 'credentials' | 'cloud'. switch and create --activate read it and skip the credentials.json write when they ran on the cloud session. Without that skip, an id from cloud.json's server would land in a file that names a different server, and every later os data / os meta call would send it there. The cloud.json write still goes through the existing url gate in active-environment.ts.
  • With no session at all, the refusal (requireControlPlaneAuth) names os cloud login as well as os login. The Authentication required prefix is unchanged.
  • Unchanged: createApiClient, requireAuth, and their data / meta / datasource / whoami callers.

Where the resolver lives, and why os package publish does not use it

  • Location: api-client.ts, beside createApiClient. The resolver needs both stores and the url gate isSameControlPlane from active-environment.ts. Putting it in cloud-config.ts would create an import cycle, because active-environment.ts imports cloud-config.ts.
  • os package publish keeps its own lane. It reads only cloud.json, by design: its source says it deliberately does not fall back to credentials.json, and content/docs/deployment/cli.mdx documents that. The ruling's order puts credentials.json first. Moving publish onto this resolver would change which token publish sends whenever credentials.json names the same server, so publish.ts is untouched.

Measured: the five subcommands, spawned from source

Setup: HOME holds only cloud.json, pointed at a local echo control plane. The CLI runs through bin/run-dev.js, with packages/cli/dist absent so the source is what runs.

subcommand before (51550933db) after (97c52b5ce7)
list exit 1, 0 requests, Authentication required. Please run os login ... exit 0, GET /api/v1/cloud/environments with Bearer cloud_tok
show env_1 exit 1, 0 requests exit 0, GET .../environments/env_1 with Bearer cloud_tok
create --org org_1 --name Dev exit 1, 0 requests exit 0, POST .../environments + POST .../env_new/activate with Bearer cloud_tok; id recorded in cloud.json
bind env_1 --artifact ... exit 1, 0 requests exit 0, GET + PATCH .../environments/env_1 with Bearer cloud_tok
switch env_1 exit 1, 0 requests exit 0, GET + POST .../env_1/activate with Bearer cloud_tok; id recorded in cloud.json

Pins: packages/cli/src/commands/environments/cloud-session.test.ts

The file has 49 cases. They run in-process through Command.run against two real node:http echo control planes on 127.0.0.1, with HOME redirected to a temp directory. Every group runs over all five subcommands:

  • Only cloud.json: the cloud bearer and the cloud active environment go to the cloud url. A --url that cloud.json does not name gets exit 1 and zero requests.
  • Only credentials.json (the control): the runtime bearer and active environment go to the runtime url, including with an explicit --url, as before.
  • Both stores:
    • credentials.json wins where both name the same server, with and without --url.
    • With no --url, credentials.json still picks the server.
    • A --url naming cloud.json's server selects the cloud session.
  • Writes: switch and create on the cloud session leave credentials.json alone. With only cloud.json, they record the id in cloud.json.
  • No session: the refusal names os cloud login.

Red before the fix, measured with the pin commit eb9dcdaeb4 on top of 51550933db: 19 failed, 25 passed. The failures were the cloud-only bearer (5), the --url naming cloud.json's server (5), the four write cases, and the no-session remedy (5). The 25 passing cases were the controls, the both-stores ordering cases and the foreign-url guard. These hold before and after the change by design.

Ablations

All 15 ablations ran at 97c52b5ce7 through scripts/ablation-replace.mjs in WRAP mode, with the fix already committed. The subject is reached by relative import into packages/cli/src, so no dist is on the path. Every leg's anchor hit exactly once, and the mutation was proven on disk by anchor count and blob change. Every restore was proven by blob == HEAD and an empty git diff HEAD. Each leg went red exactly where predicted:

leg mutation red
L1 drop the cloud.json candidate 14: cloud-only bearer x5, --url cloud x5, the 4 write cases
L2 no-url order swapped to cloud first 10: both-same-server x5, no-url picks credentials x5
L3 delete the credentials url match 5: credentials wins on the shared --url server
L4 ungated cloud fallback for a foreign url 5: cloud token never sent to a foreign --url
L5 drop the legacy credentials leg 5: control with explicit --url
L6 cloud active environment dropped 10: cloud-only x5, --url cloud x5
L7 runtime active environment dropped 10: control x5, both-same-server x5
L8 / L9 switch / create runtime write ungated 1 each: leaves credentials.json alone
L10 remedy reverted to the old sentence 5: no-session names os cloud login
L11-L15 each subcommand back on createApiClient that subcommand's cases only (2, 2, 4, 2, 4)

Tests and gates, at 97c52b5ce7

  • pnpm --filter @objectstack/cli typecheck: exit 0, which covers tsc --noEmit over src (the new pin is under src) plus check:test-typecheck OK.
  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2:
    • First run: 245 of 247 files passed (3509 passed, 29 skipped).
    • The two failing files, published-subpath-console.pin and published-subpath-hook-body.pin, refused because packages/cli/dist was absent, a build prerequisite.
    • After the workspace build, those two files passed (29 tests).
  • Integration tier: declared to CI. The diff touches no integration-tier file and no spawn entry.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 65 commands, and every one was run with its exit code recorded before any pipe.
    • Four gates first exited 3 (PREREQUISITE NOT MET, no dist): check:dual-build-cjs-loads, check:i18n, check:i18n-coverage and check:i18n-walk-parity. After turbo run build they exit 0.
    • --ran: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN.
  • Lint, a declared narrowing of pnpm lint. ESLint ran with --no-inline-config --format json on the 10 changed .ts files.
    • The JSON reports 10 files linted, none ignored, 0 errors and 0 warnings.
    • The repo's single eslint.config.mjs never enables type-aware linting (no parserOptions.project, no typed rules; it says so itself). Its only cross-file inputs are two baseline JSON files this diff does not touch, so no untouched file's verdict can move.

Acceptance notes

  • packages/cli/src/utils/active-environment.ts header says "os environments authenticating as the runtime identity is deliberate". That sentence is now false. The file is outside this card's claimed surface, so it is not edited here. It is a one-sentence comment change.
  • package/publish.ts and plugin/publish.ts each carry their own copy of the cloud.json-only resolution. That duplication predates this card, and this card does not touch it.
  • content/docs/deployment/cli.mdx has no os environments sentence that this change makes false, so it is not edited.
  • The legacy leg (an explicit url that neither file names gets credentials.json's session) is kept so the control holds byte for byte. Narrowing it would be its own decision.
  • In the README Cloud section, the table row and the paragraph and code comment that PR docs(cli): the README states the global flags and the os plugin group that the built os registers #21354 added are rewritten to state the new behaviour.
  • The gate derivation ran on a tree 8 commits behind origin/main. Its one stale input is scripts/sdui-manifest.record.json, which this diff does not touch. Upstream, only packages/cli/test/json-stdout-purity.e2e.test.ts changed under packages/cli.

Generated by Claude Code

claude added 3 commits October 2, 2026 10:39
Five subcommands against a local echo control plane: only cloud.json,
only credentials.json (the control), and both. Red on 5155093: with
cloud.json alone every subcommand refuses before sending a request.

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
…one resolver

createControlPlaneApiClient (utils/api-client.ts) chooses the stored
session for list, show, create, bind and switch: credentials.json's
session where it targets the server (with no --url it names it), else
cloud.json's on the same terms; an explicit url neither file names keeps
credentials.json's session as before and never gets the cloud token.
The active environment comes from the chosen file, and switch / create
--activate no longer write a cloud environment id into credentials.json
when they ran on the cloud session. The refusal with no session names
os cloud login too.

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
…oth stores share

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/l label Oct 2, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 23 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/cli/README.md, packages/cli/src/utils/active-environment.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/environment-routing.mdx (via /api/v1/cloud/environments (route, a path literal in a comment on a changed line))
  • content/docs/concepts/north-star.mdx (via /api/v1/cloud/environments (route, a path literal in a comment on a changed line))
  • content/docs/deployment/cli.mdx (via os environments bind (command, read off packages/cli/src/commands/environments/bind.ts), os environments create (command, read off packages/cli/src/commands/environments/create.ts), os environments list (command, read off packages/cli/src/commands/environments/list.ts), os environments show (command, read off packages/cli/src/commands/environments/show.ts), os environments switch (command, read off packages/cli/src/commands/environments/switch.ts))
  • content/docs/deployment/publish-and-preview.mdx (via os environments create (command, read off packages/cli/src/commands/environments/create.ts), os environments switch (command, read off packages/cli/src/commands/environments/switch.ts))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via os environments create (command, read off packages/cli/src/commands/environments/create.ts))
  • content/docs/releases/v17/17-3.mdx (via os environments create (command, read off packages/cli/src/commands/environments/create.ts), os environments list (command, read off packages/cli/src/commands/environments/list.ts))
  • content/docs/releases/v17/17-6.mdx (via os environments create (command, read off packages/cli/src/commands/environments/create.ts))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/cli/README.md, packages/cli/src/utils/active-environment.ts) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 26 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 85986144c2ef6f379955137677c5cbfb00e194d2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from fc4cbe6a7676852d6f1ce129b7e986b5db2e486f — the merge of head c97a04430400ad4991dc8f530a52a182af2f1ffc into base 85986144c2ef6f379955137677c5cbfb00e194d2, 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 fc4cbe6a7676852d6f1ce129b7e986b5db2e486f && git checkout fc4cbe6a7676852d6f1ce129b7e986b5db2e486f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 85986144c2ef6f379955137677c5cbfb00e194d2 c97a04430400ad4991dc8f530a52a182af2f1ffc && git checkout -B drift-repro 85986144c2ef6f379955137677c5cbfb00e194d2 && git merge --no-ff c97a04430400ad4991dc8f530a52a182af2f1ffc

node scripts/docs-audit/affected-docs.mjs --json 85986144c2ef6f379955137677c5cbfb00e194d2

⚠️ 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 85986144c2ef6f379955137677c5cbfb00e194d2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…e active-environment header

The five subcommands now accept the cloud.json session they used to
refuse, so the changeset declares Clause-② yes (widening) at minor. The
active-environment.ts header no longer says os environments authenticates
only as the runtime identity.

Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c97a04430400ad4991dc8f530a52a182af2f1ffc
Local-runs: none

Read for this record: card #21360 body and its three comments (triage ruling 5947754514, claim 5950327876, os-dev-report 5951504787); #21310's round report 5947221232; PR #21400 body, file list (13 files, +571/-50) and the net diff against merge base 51550933db (origin/main at review time 85986144c2); the called code at the head sha; the four published pages; the head's check-runs collapsed latest-per-name.

① Derived judgments

(a) Shape 1 as ruled — RIGHT. One resolver: chooseControlPlaneSession in packages/cli/src/utils/api-client.ts, reached only through createControlPlaneApiClient; each of list|show|create|bind|switch calls that factory once and no other file under packages/cli/src/commands reads both stores, so there is no per-command copy. createApiClient and requireAuth are untouched, as are their eleven callers (data x5, meta x4, whoami). The branches, read from the code: no --url/OS_CLOUD_URL gives fromCredentials ?? fromCloud — credentials.json when present (its url, else http://localhost:3000, byte for byte the old path), else cloud.json (its url, else https://cloud.objectos.ai); an explicit url naming credentials' server (isSameControlPlane: scheme+host+path, trailing slash stripped, an absent url never matches) gives the credentials session; an explicit url naming cloud's server and not credentials' gives the cloud session with the cloud token to that url; an explicit url naming neither gives credentials' session, never cloud's — the pre-change control held byte for byte. Exfiltration: the cloud bearer leaves only when baseUrl is cloud.json's own url (the no-url branch sets it to that url; the url branch requires same-control-plane), so it is never sent to a server cloud.json does not name — pinned as five refusals with zero requests on both planes. Credentials' token to the cloud server while the files name different servers: no — with both files, a url naming cloud's server selects cloud and no url selects credentials' own url; only with cloud.json ABSENT does an explicit cloud url carry the credentials token, which is the pre-change behaviour and the ruling's control. --token/OS_TOKEN and OS_ENVIRONMENT_ID win field by field exactly as in createApiClient. One target move, named not failed: with cloud.json alone and OS_TOKEN set and no url, the request now goes to cloud.json's url where it went to http://localhost:3000 before — the ruling's own consequence (cloud.json alone names the server), consistent with the control's existing OS_TOKEN plus credentials.json → credentials' url, remedied by --url/OS_CLOUD_URL as the README row states; not pinned.

(b) The switch/create write gate — RIGHT. session === 'cloud' skips the credentials.json write; the cloud.json write still passes recordCloudActiveEnvironmentId's url gate. On the cloud session baseUrl is cloud.json's url by construction, so the recorded id belongs to that server, and credentials.json (which names another server, else credentials would have won) is left alone — pinned for both writers. An os login user loses nothing: with credentials.json present and no url, or a url naming its server, the session is credentials and both writes proceed as before (publish-active-environment-store.test.ts keeps asserting the credentials.json write with both files on one server). The only write now skipped is one the old code made wrongly — a cloud-server id into a file naming a different server — reachable before only with a token valid on both, and the module header's invariant says such an id does not resolve there. switch on a cloud-only run prints "(also recorded in cloud.json ...)" — loose wording, not a false claim.

(c) os package publish not moved — RIGHT. The claim left it to the dev ("whether publish.ts moves onto it"). publish.ts lines 496-516 resolve cloud.json only and say they deliberately do not fall back to credentials.json; cloud-config.ts's header makes "impossible to publish with a runtime-scoped token" the design; cli.mdx 2142-2147 documents it. Under the ruling's credentials-first order a publish on this resolver would send the runtime token whenever credentials.json names the cloud — a reversal the ruling never ordered; the ruling cites publish as the precedent, not as a member. "One resolution for the hosted control-plane family" holds for the family the ruling names, the five os environments subcommands. The pre-existing twin of publish's own resolution in plugin/publish.ts 170-178 is noted in the acceptance notes; not a defect, no card owed.

(d) Published text — RIGHT, nothing false is left. README Cloud: the table row, the paragraph and the flow comment are rewritten to the new order and match the resolver branch for branch; the lead sentence "do not share one session or one flag spelling" stays literally true (no single session covers all of them; --server vs --url). The active-environment.ts header (patch round) now says os environments runs on either stored session, chosen once in createControlPlaneApiClient — true; its "credentials.json's url falls back to http://localhost:3000" is still true (DEFAULT_RUNTIME_URL). cli.mdx: the Cloud Environments table (2078-2082) names no session and no os login, "visible to the current session" is not false, and the publish paragraph (2142-2158) describes publish, which is unchanged. publish-and-preview.mdx 149-179 is publish-only. environment-routing.mdx has no CLI session sentence at all (the drift listing is a word hit on "session", not a drift). No page tells a hosted user to run os login first. Nit, not failing: ApiClientOptions.environmentId's TSDoc still says the stored id comes from credentials.json, and for the new factory it may come from cloud.json.

(e) Pins — no weakened assertion. cloud-session.test.ts counts to 49: 10 cloud-only (bearer and active id x5; foreign-url refusal with exit 1 and zero requests on both planes x5), 10 control (x5 plain, x5 with explicit url), 20 both-stores (four orderings x5, one with a trailing-slash url), 4 writes (gate x2, cloud-only record x2), 5 remedy. expectServedBy asserts exit undefined, at least one request, every bearer, and the OTHER plane empty — preservation and refusal halves both present; the measured red-before (19 of 44 on eb9dcdaeb4) matches the groups the fix reaches. create-clone-from.test.ts: the mock key is renamed, the stub shape unchanged, and BASE_ARGV keeps --no-activate so the write gate is never on its path — a control re-greened, nothing loosened. publish-active-environment-store.test.ts: comment only; its assertion is exactly the credentials-first branch.

② Semver level

@objectstack/cli minor with Clause-②: yes (widening) on PR line 2 and in the changeset body — RIGHT. Five subcommands that refused a cloud.json-only HOME now accept it: an additive widening of a published package's surface, which the gate's own "WHICH LEVEL" rule grades at least minor; Check Changeset is green on this head. No BREAKING banner and no ADR-0087 marker is right: nothing is removed or renamed — no export, flag, env var or stored shape; ApiClientResult.session is optional and absent from createApiClient's result. Narrowing search, invocation by invocation: credentials.json-only HOME with and without --url — unchanged and pinned; both files, no url — credentials wins, pinned; both files, url naming cloud's server — the token moves from credentials' to cloud's, and that invocation could only have succeeded before when one server issued both, in which case it still succeeds; the write gate moves no exit code; the refusal keeps its Authentication required prefix and no other pin asserts the old sentence (remote-api-utils.test.ts pins requireAuth, untouched). The one target move (cloud.json-only HOME plus OS_TOKEN, no url) is the ruling's cloud-url consequence, not an accept-set narrowing; it is named in ①(a). No hidden narrowing found.

③ Boundary flags

  • Every dev deviation is answered in the PR body and judged here: the write gate (①b, right); the remedy naming os cloud login (right — the ruling says the os login --help redirect "becomes true"); the two edited pins (①e); the README paragraph and flow comment beyond the table (right — PR docs(cli): the README states the global flags and the os plugin group that the built os registers #21354's sentences stated the old behaviour and would now be false); active-environment.ts left to the seat and patched in c97a044304 together with the level correction (one comment file added to the surface, no code change, no second resolver); the integration tier declared to CI (Dogfood Verify CLI and Test Core ran on the head); the model-free trailer pair verified on all four commits.
  • Stop clause honoured: no stored file format changed; os login and os cloud login untouched; shape 3 not attempted.
  • Not governed: no path under docs/adr/**, .claude/**, skills/**, AGENTS.md or CLAUDE.md; 621 changed lines; head repo equals base repo. No ADR governs which session os environments uses (docs/adr and scripts/adr-anchors grep: no hit), so no Prime Directive [WIP] Add Chinese version of the documentation #13 reversal.
  • Fixes #21360 — RIGHT. The card's shape choice was ruled (5947754514, shape 1, overturnable by the maintainer) and the claim dispatched that ruling as Fixes; this head delivers it on all five subcommands with the README. Shape 3 is out of scope by the ruling, so nothing on the card stays open after this lands. The card this PR closes must claim this branch and Part-of PR must not also close its card are green.
  • Check-runs on the head c97a044304, collapsed latest per name, converged at 2026-10-02T12:16:02Z (background poll, third pass): 34 names, 29 success, 5 skipped (Auto Label, Check PR Size, Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)), 0 failure, 0 pending. All seven required contexts success: Lint & Repo Gates, TypeScript Type Check, Test Core (and its six shards), Dogfood Regression Gate (and its three shards), Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. Also success: Check Changeset, Dogfood Verify CLI, Flag docs affected by code changes, the four Type Check · jobs, the three claim/closure guards. The PR is a draft with no auto-merge armed and mergeable_state: blocked — the landing is the owning seat's act after this record.
  • Residuals for the seat, none blocking: the unpinned OS_TOKEN-on-cloud-session target move (①a); the ApiClientOptions.environmentId TSDoc nit (①d); switch's "(also recorded ...)" wording on a cloud-only run (①b).

Implemented-by: claude/issue-21360-environments-cloud-session
Reviewed-by: session_01VvcEokUG1tvVxkceYfR5XB

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 2, 2026 12:19
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 2, 2026 12:19
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit 4b20c84 Oct 2, 2026
43 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21360-environments-cloud-session branch October 2, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants