Skip to content

docs(skills): teach os environments use-runtime and os mcp connect - #31225

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-0r0grk
Nov 8, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-0r0grk

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread

Fixes #31143

Clause-②: no
Metadata-protocol: none

dispatch-gates --tier reads a MANDATORY model tier for skills/** (clause ① of the model-tiering ruling); it is waived by the standing order of the maintainer in this project (2026-11-03), and this card ran on the project's default model. The dispatching seat runs the contract review.

Cross-seat note: content/docs/deployment/cli.mdx and content/docs/deployment/publish-and-preview.mdx are folded into this domain:skills card by the triage (6708594558), so the skill and the docs say the same thing. content/docs/ai/connect-mcp.mdx § "Claude Code (one command)" (the page that gives the claude mcp add line) was confirmed by the seat by name before it was edited.

维护者速读

What changes

1. The platform skill (skills/objectstack-platform/references/operations.md)

-| `os login` / `logout` / `whoami` | Auth against the ObjectStack cloud control plane |
-| `os register` | Register the local stack as a deployable target |
+| `os environments use-runtime <url> --token <key>` | Deploy to a self-hosted runtime instead: the key (one it issued) is tried first (`--no-verify` skips), then kept in `~/.objectstack/cloud.json` (0600) across cloud login/logout. `--forget` or `os environments switch` targets the cloud again |
-… `--no-wait`; `--wait-timeout <seconds>` (600) |
+… `--no-wait`; `--wait-timeout <seconds>` (600). A `use-runtime` target, no `--env`: build, dry-run plan, install, live on `hotLoaded`; `--confirm-access-upgrade <token>` |
-| `os package install <manifest-id │ ./dist/objectstack.json> [--version │ -u/--url http://localhost:3000]` | … not the cloud login |
+| `os package install <manifest-id │ ./dist/objectstack.json> [--version │ -u/--url]` | … not the cloud login. No `-u`: the `use-runtime` runtime and, with no `-t`/`--email`, its key; else `http://localhost:3000` |
+| `os mcp connect` | Register the cloud environment's MCP server (picked as `os deploy` picks it) with Claude Code (`claude mcp add`); `--url <base>` for a self-hosted or local server (named `objectstack`); `--name`; `--print` runs nothing (other clients). Stores no token: the client runs the server's own OAuth |

The skill rows stay short and the full sequence lives in the CLI reference (the order's preference).

2. The docs

  • content/docs/deployment/cli.mdx: a use-runtime row in § Cloud Environments; new § "Deploy to a self-hosted runtime" (examples, the key and its probe, --no-verify, storage, which of use-runtime / switch wins, --forget, a flag table); § os deploy names the self-hosted target in its lead, gains a --confirm-access-upgrade row and a "To a self-hosted runtime" subsection (the four steps and the d key); new § MCP / os mcp connect (examples, environment resolution, --url, names, --client, --print, failure exits, no token, revoke, a flag table); § os package install's -u and -t rows now give the remembered runtime as the default.
  • content/docs/deployment/publish-and-preview.mdx § 3 "Install a version": one paragraph on remembering a runtime with use-runtime so os deploy and the d key deploy there, the os package install fallback, and how to target the cloud again.
  • content/docs/ai/connect-mcp.mdx § "Claude Code (one command)": one paragraph plus a fence before the hand-typed lines, which stay.

Code site per taught sentence (main 66bf5b4a)

Taught Code site
os environments use-runtime <url> --token <key>; -t reads OS_RUNTIME_TOKEN packages/cli/src/commands/environments/use-runtime.ts args.url (L40–L42), flags.token (L45–L49)
The key is tried first on GET /api/v1/marketplace/install-local; 401/403, 404, unreachable refused, nothing recorded use-runtime.ts L93–L100, probeRuntime (L142–L178)
--no-verify records anyway (a container not up yet) use-runtime.ts verify flag (L50–L54, allowNo), header L21–L25
URL is an http(s) base URL, no query or fragment normalizeRuntimeUrl (L118–L135)
--forget drops the record; cloud is the target again; not with a url use-runtime.ts L65–L77; forgetRuntimeTarget (packages/cli/src/utils/runtime-target.ts L65–L69)
Stored in ~/.objectstack/cloud.json, 0600, its own record; key sent only to its url packages/cli/src/utils/cloud-config.ts header L31–L44, getCloudCredentialsPath L106, writeFile(..., { mode: 0o600 }) L132; runtime-target.ts header L23–L27
Cloud logout keeps it; cloud login keeps it cloud-config.ts deleteCloudConfig L193–L195, writeCloudConfig L187–L190 (header L40–L43)
os environments switch <id> (when recorded in cloud.json) makes the cloud the target again, keeping the runtime packages/cli/src/commands/environments/switch.ts L78–L79; activateCloudDeployTarget (runtime-target.ts L76–L81)
os deploy with a runtime target and no --env deploys there; --env names a cloud environment packages/cli/src/commands/deploy.ts L152–L156
Self-hosted sequence: build → dry run (plan printed; a runtime that predates it installs anyway, said, exit 1) → install (a register failure is refused here) → hotLoaded ⇒ 0 deploy.ts deployToRuntime L288–L335, header L49–L71; install.ts L511–L522 (dry run ignored ⇒ INSTALLED, exit 1) and L617–L622 (an inline manifest whose register throws is refused 422 PLUGIN_REGISTER_FAILED)
--confirm-access-upgrade <token> passed through; refused when the target is the cloud deploy.ts flag L138–L142, L157–L164, L320
The d key runs the same os deploy, no cloud login asked packages/cli/src/utils/dev-deploy-key.ts header L26–L31, deployKeyHint L74–L75, L118
os package install with no -u: the remembered runtime (its key when no -t / --email and the url is its own), else http://localhost:3000 runtime-target.ts runtimeInstallTarget L92–L108, DEFAULT_RUNTIME_INSTALL_URL L38; packages/cli/src/commands/package/install.ts flags L218–L234, L328–L337
os mcp connect resolves the cloud environment as os deploy: --env, active, only; none/several refused naming --env; a remembered runtime is not consulted packages/cli/src/commands/mcp/connect.ts header L8–L15, resolveInstallTarget call L202–L212 (no runtime-target read in the file)
MCP URL = environment origin + /api/v1/mcp; registered under its slug / display name / id, lower-cased, other runs → - connect.ts MCP_ENDPOINT_PATH L54, environmentServerName L78–L84, L234–L235
--url skips the cloud, adds /api/v1/mcp, refuses a URL ending in it, name objectstack connect.ts mcpUrlFromBase L91–L113, L182–L191; OBJECTSTACK_MCP_SERVER_NAME = 'objectstack' (packages/mcp/src/skill-md.ts L51)
--name overrides (letters, digits, -, _) connect.ts flag L143–L145, SERVER_NAME L60, L173–L177
--client claude-code is the only value and the default; runs claude mcp add --transport http NAME URL connect.ts MCP_CLIENTS L57, flag L146–L150, claudeMcpAddArgs L63–L65
--print prints the endpoint and the line, runs nothing; other clients add the endpoint themselves connect.ts flag L151–L154, header L35–L41, L243–L248
claude missing or claude mcp add failing exits non-zero; remove or pick another --name connect.ts L251–L270
No token stored; --token only finds the environment; sign-in via /mcp; revoke in Account → Connect an AI Assistant connect.ts header L24–L33, token flag L161–L165, L273–L281

Token readings and every deletion's home

node scripts/check-skills-token-ratchet.mjs on fresh main 66bf5b4a: operations.md 3285 / 3287 (headroom 2). With the teaching added: 3489 (+202 over). After the deletions below and the review's S1 correction: 3340, 13140 → 13360 bytes (+220 net: −594 cut, +814 added). The remainder, +53 measured, raises only that file's row 3287 → 3340, pinned at the measurement, under the standing maintainer ruling 2026-10-27, verbatim and untranslated: 「A:先削重复,余额按实测提高」 (ruling record 6513306068 on #27847). No other row changes. (The seat's ruling expected about +47; the extra 6 tokens are an accuracy fix found after it: the MCP row first said "the os deploy environment", but with a self-hosted target os deploy goes to the runtime while os mcp connect still resolves a cloud environment, so the row now says "the cloud environment (picked as os deploy picks it)".)

Every deletion, with every fact it held and where each survives (seat ruling: cuts 1–7 accepted):

# Cut (bytes) Facts it held Surviving home
1 os dev row: " — idempotent, never overwrites an existing account" (53) seeding never touches an existing account the same row, whose "on an empty DB only" now reads "with no login yet" (review S1: the seed is gated on the absence of a login, not an empty user table, packages/cli/src/commands/dev.ts L777–L781; −2 bytes)
2 Verify item 2: "A bare field (missing record.) fails here." (47) os validate refuses a bare field ref the section intro (a bare field ref in a predicate faults; "Both are caught at author time by one command") and item 2's record.<field> existence check
3 "(os lint is a separate style/convention pass and does not replace os validate.)" (86) lint is a style/convention pass; it is not the gate Daily-loop os lint row ("Style/convention lint on metadata files") and the rule of thumb ("never report … done until os validate passes")
4 Production port row: " — a shifted port breaks reverse-proxy upstreams, better-auth callback URLs, and CORS trusted-origins as opaque 403/502s" (122) what a shifted port breaks Production guidance "Keep these in sync when you change the port (mismatch ⇒ better-auth Invalid origin 403 / CORS failures)" list: reverse-proxy upstream, OS_AUTH_URL / baseURL + callbackURL, OS_TRUSTED_ORIGINS. The word "502" is the one fact not restated.
5 Production guidance: " Don't rely on the 3000 default; it collides easily on shared hosts." (72) don't rely on the default "Pin the port explicitly — PORT=8080 os start" and the resolution chain showing 3000 as the default
6 Testing: " — with the in-memory driver a full bootstrap is cheap" (58) a fresh kernel per test is cheap the section intro: "tests run in milliseconds"
7a Row os login / logout / whoami · "Auth against the ObjectStack cloud control plane" (88) misstated: os login authenticates against the runtime server and stores credentials.json (login.ts L240–L253); cloud auth is os cloud login content/docs/deployment/cli.mdx § Authentication; the os cloud login row stays in the skill
7b Row os register · "Register the local stack as a deployable target" (68) misstated: os register creates an account and stores credentials (register.ts L64) content/docs/deployment/cli.mdx § Authentication

Cut 6 joins its two lines into one (the deletion spans the line break); no other line is rewrapped. No test or script pins any cut text (grepped packages/, scripts/, .claude/, skills/, content/; only packages/skills/dist and CHANGELOGs carry copies).

Eval cases (skills/objectstack-platform/evals/config-plugins-ops.json)

  • 21 · "We run ObjectStack's Docker image on our own server at https://objectos.example.com. How do I deploy my app there from the project without typing its URL and key every time?" → use-runtime <url> --token <key>, probe and --no-verify, cloud.json (0600) across login/logout, then os deploy / d: build, dry-run plan, inline install, live on hotLoaded, --confirm-access-upgrade; os package install fallback; --forget / switch back. must_contain: os environments use-runtime, --token; must_not_contain: os deploy --url (no such flag).
  • 22 · "Connect Claude Code to our ObjectOS Cloud environment's MCP server, and to my local os dev server too." → os mcp connect (runs claude mcp add --transport http <name> <url>, environment as os deploy picks it, named after it), --url http://localhost:3000 registered as objectstack, --name, --print for other clients, no token, /mcp sign-in. must_contain: os mcp connect, --url; must_not_contain: --client cursor (the only client is claude-code).

Swept the skill's evals for the old claims: no case encodes the cut text, os login / os register, or the localhost:3000 install default; case 5's os lint and case 17's cloud os deploy stay true.

Other sites found (not edited)

  • content/docs/ai/agents.mdx L99 and content/docs/getting-started/build-with-claude-code.mdx L477–L502 still give only the hand-typed claude mcp add line; content/docs/releases/v16.mdx is release history.
  • packages/cli/src/commands/environments/create.ts (--activate) does not call activateCloudDeployTarget, so only os environments switch returns the deploy target to the cloud; the docs teach only switch. Whether create --activate should too is a CLI question, not a teaching one.

Gates

Head 4e0b6486 (two commits on merge base 66bf5b4a; the second is the review delta: S1, N3, N4, N7 and the N6 re-flow of the one ragged os mcp connect paragraph). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives the same 87 commands as at e15e06e1; all 87 exited 0, 0 NOT MEASURED among them. Each ran with timeout 900 under NODE_OPTIONS=--max-old-space-size=8192, after pnpm install --frozen-lockfile and a full pnpm build (exit 0). The list with each exit code: gates-4e0b6486.txt in the card folder (round 1: gates-e15e06e1.txt, 87/87 exit 0). It includes pnpm check:skill-identifier-liveness, pnpm --filter @objectstack/spec run check:skill-refs, pnpm check:skill-compatibility, node scripts/check-skills-token-ratchet.mjs (and --self-test), pnpm check:doc-authoring, pnpm check:doc-anchors, pnpm --filter @objectstack/spec run check:docs, pnpm check:cli-command-ids, pnpm check:cli-examples-parity and node scripts/check-empty-changeset.mjs --base origin/main.

The CI-only steps dispatch-gates names as NOT MEASURED (Build Docs, Build Core, the type-check lanes, dogfood and shard attestation) run in CI. node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD exit 0 before the push.

The platform skill's operations reference and the deployment docs now
teach the self-hosted deploy target (`os environments use-runtime`, the
self-hosted `os deploy` sequence, `--confirm-access-upgrade`, the
`os package install` fallback) and `os mcp connect`, each sentence read
from the merged CLI code. connect-mcp.mdx points to `os mcp connect`
before the hand-typed `claude mcp add` lines.

operations.md pays by cutting restatement in the same file and two rows
that misstated `os login` / `os register`; the measured remainder raises
its token-ratchet row under the standing ruling. Two eval cases added.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnBUEjCrpMSAcQv3Gp
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 7, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract-review-claim: session_01J8VVAj7CvjkGXvgXzd6RZ7 · head e15e06e · 2026-11-07T18:36Z

Full contract review of PR #31225 (#31143, claim 6711070771). The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e15e06e1cfdb735a930c4e20735d6e65a8472db7
Local-runs: probe — node scripts/check-skills-token-ratchet.mjs, run in a throwaway worktree of the head (/tmp/claude-0/seat3/wt-31225, since removed), because the brief asks for the token readings from the head's own ratchet (exit 0; operations.md 3341 / 3341, headroom 0; 57 authored files within ceilings). In the same worktree I also ran node scripts/pm/check-governed-merges.mjs --pr 31225 (exit 3, GOVERNED, Tier H). Main-side bytes were taken by hand (git show 66bf5b4a:<file> | wc -c = 13140, so ceil(13140/4) = 3285; the head is 13362, so 3341). Each cut's bytes were measured against the base file with a scratch script (total 594). The os:check gates were not re-run; the gate record shows all 87 at exit 0.
Scope: the 7 files of git diff $(git merge-base origin/main refs/seat3/pr-31225) refs/seat3/pr-31225 (merge base 66bf5b4a; one commit e15e06e1c): .changeset/skills-platform-self-hosted-deploy-mcp-connect.md, skills/objectstack-platform/references/operations.md, skills/objectstack-platform/evals/config-plugins-ops.json, content/docs/deployment/cli.mdx, content/docs/deployment/publish-and-preview.mdx, content/docs/ai/connect-mcp.mdx, scripts/check-skills-token-ratchet.mjs. origin/main is now fe97d85b0, 5 commits past the base (b48490e6d, a5638b6c7, 3f4cb2124, 1c669888c, fe97d85b0). None of them touches these files or any code cited below; the only CLI-side change is a test, packages/cli/test/released-port-production-spawn.pin.test.ts. Cited code read on origin/main: packages/cli/src/commands/environments/use-runtime.ts (whole file), packages/cli/src/utils/runtime-target.ts (whole file), packages/cli/src/utils/cloud-config.ts (header L31-44, writeCloudFile L120-138, writeCloudConfig L187-190, deleteCloudConfig L193-195, writeDeployTargetRecord L203-206, resolveCloudCredential L221-234), packages/cli/src/commands/cloud/login.ts L248 and logout.ts L39, packages/cli/src/commands/environments/switch.ts (whole file), packages/cli/src/commands/environments/create.ts L118-160, packages/cli/src/commands/deploy.ts (whole file), packages/cli/src/commands/package/install.ts (header L40-73, flags L218-234, target L316-337, dry run L494-521, hot-load L612-640), packages/cli/src/utils/dev-deploy-key.ts (header, deployKeyHint, act, runCliChild, startDevDeployKey), packages/cli/src/utils/install-target.ts (header, L137-162), packages/cli/src/commands/mcp/connect.ts (whole file), packages/mcp/src/skill-md.ts L51, packages/cli/src/commands/login.ts L240-253, packages/cli/src/commands/register.ts L62-75, packages/cli/src/commands/dev.ts L422-426 and L777-782, packages/plugins/plugin-auth/src/dev-admin-seed-gate.ts header.

① Derived judgments

Inputs: card #31143 (body; scope comment 6708465377; triage 6708594558; claim 6711070771; "Claim confirmations" 6711424579 with the two mid-flight rulings), the PR body (the same as /mnt/project-files/card-31143/body-v1.md, apart from one trailing blank line), the order at /mnt/project-files/skills-seat3/order-31143.md, the gate record gates-e15e06e1.txt (87 commands, all exit 0), the commit and the net diff. Each taught fact was checked against code:

  • Item 1, os environments use-runtime. TRUE.
    • Arguments: one positional url. -t, --token is filled from OS_RUNTIME_TOKEN. --verify defaults on, with allowNo, so --no-verify exists. --forget is a boolean. --forget together with a url is refused ("Pass either a runtime url or --forget, not both"), so "Takes no url" is right.
    • The URL must be http(s) with no query and no fragment. Trailing slashes are dropped and the path is kept (normalizeRuntimeUrl). A missing token is refused and names Setup → API Keys.
    • The probe: GET {url}/api/v1/marketplace/install-local with the bearer key. 401/403 means the key is rejected, 404 means the runtime has no install-local, and a network error or a 15 s timeout means it cannot be reached. Each prints its reason plus "Nothing was recorded", and the command exits 1. Other non-2xx answers are refused too. The docs list three classes and do not say others pass, so that is fine.
    • Storage: recordRuntimeTarget writes { runtimeTarget: { url, token, recordedAt }, deployTarget: 'runtime' } through writeDeployTargetRecord into ~/.objectstack/cloud.json. writeCloudFile writes it with mode: 0o600 and then calls chmod 0o600. It is a separate named record. resolveCloudCredential reads only token, so no cloud request sees the key.
    • Cloud login and logout keep it. os cloud login calls writeCloudConfig, which carries the deploy-target half across (L188-189). os cloud logout calls deleteCloudConfig, which rewrites the file with only that half (L194).
    • os environments switch returns the target to the cloud. activateCloudDeployTarget runs only when recordCloudActiveEnvironmentId recorded the id for cloud.json's control plane (switch.ts L78-79). It sets deployTarget: 'cloud' and keeps runtimeTarget. cli.mdx and publish-and-preview.mdx state that condition. The skill row and eval 21 leave it out (N1).
    • The thread's claim about create --activate is TRUE. create.ts calls recordCloudActiveEnvironmentId but never activateCloudDeployTarget. A repo-wide grep finds only switch.ts calling it. So create --activate leaves deployTarget: 'runtime' in place. The docs teach only switch, which is right.
  • Item 2, self-hosted os deploy. TRUE.
    • Target choice: const runtime = flags.env ? undefined : await activeRuntimeTarget() (L152). So --env (or OS_ENVIRONMENT_ID) still names a cloud environment. activeRuntimeTarget returns the record only when deployTarget === 'runtime'.
    • Sequence (deployToRuntime L288-335):
      • Build: compileForDeploy. A failure prints "Nothing was installed." and exits 1.
      • Dry run: PackageInstall is run in process with --url, --token and --dry-run, as embedded with retryCommand = 'os deploy'. install.ts prints "Dry run — nothing was installed or written" and the plan (renderUpgradePlan). If the outcome is not dry-run, the command exits 1.
      • Install: --confirm-access-upgrade is passed through when given. If the outcome is not installed, it exits 1.
      • Live: hotLoaded gives Live: {url} now serves {id} {version}. and exit 0. Otherwise it prints "loads when the runtime restarts" and exits 1.
      • The build really is sent inline: the artifact path goes to install-local.
    • --confirm-access-upgrade with a cloud target is refused before any credential read (L157-164). TRUE.
    • The d key: startDevDeployKey spawns os deploy as a child and drops OS_ENVIRONMENT_ID from its environment. With an active runtime target, act() skips os cloud login, and the offer line names the runtime. TRUE.
    • The os package install fallback (runtimeInstallTarget):
      • With no -u (and no OS_RUNTIME_URL), the stored runtime is used, active or not.
      • Its key is used when there is no -t, no --email and no --password, and the URL is the runtime's own (isSameControlPlane).
      • Otherwise the URL is http://localhost:3000.
      • The skill row ("No -u: the use-runtime runtime and, with no -t/--email, its key; else http://localhost:3000") and the cli.mdx -u / -t rows are TRUE.
    • Two edges of the four-step text mirror deploy.ts's own header and are harmless (N3).
  • Item 3, os mcp connect. TRUE.
    • Credential and environment: resolveCloudCredential then resolveInstallTarget with explicit: flags.env. That is the same pair of calls, with the same --server / --token / --env flags and env fill, that os deploy uses on its cloud path. The order is --env, then the active environment for this control plane, then the organization's only one. None or several is refused, naming --env.
    • connect.ts imports nothing from runtime-target.ts. A grep for activeRuntimeTarget, storedRuntimeTarget and runtimeTarget in the file finds nothing. So with a use-runtime target active, os deploy goes to the runtime, while os mcp connect still resolves a cloud environment.
    • URL: readCloudEnvironment(...).origin + '/api/v1/mcp'. The name is environmentServerName (slug, else display name, else id, made safe; N4).
    • --url: http(s), no query or fragment. A path ending in /api/v1/mcp is refused. The default name is OBJECTSTACK_MCP_SERVER_NAME = 'objectstack'. --url asks for no cloud login.
    • --name must match /^[A-Za-z0-9_-]+$/. --client has options: ['claude-code'], which is the default. --print prints the Endpoint, Name and Connect lines and skips the spawn.
    • Failures: a missing claude (ENOENT) exits 1. A non-zero claude mcp add exits with its status and names claude mcp remove.
    • No token is stored. claudeMcpAddArgs is mcp add --transport http NAME URL, with no header and no token. The sign-in line says "This command stored no token", and Revoke points to Account → Connect an AI Assistant.
    • The thread's deviation. The statement is TRUE, and the +7 is honest. "the os deploy environment" would have been false whenever a runtime target is active. "the cloud environment (picked as os deploy picks it)" is true: it names the cloud environment, and the pick rule is deploy's own cloud rule, run through the same function. cli.mdx makes it explicit ("A self-hosted runtime os environments use-runtime remembered is not consulted; name it with --url"). connect-mcp.mdx says "your ObjectOS Cloud environment, picked as os deploy picks it". The cost: +"cloud " (6) + " (picked as os deploy picks it)" (33) − "os deploy " (12) = 27 bytes, which is 7 tokens. A shorter true wording ("(--env, active, only)") would save at most a token or two. The PR body states the overrun against the seat's ~+47 openly. Ruling 2 in 6711424579 raises the row "by the measured amount", with no cap. Not a budget breach.
  • Item 4, cut 7. TRUE that both rows were false.
    • os login takes "ObjectStack runtime server URL (your local ObjectOS or self-hosted control plane). For the hosted package registry, use os cloud login instead." It writes credentials.json, not anything on "the ObjectStack cloud control plane".
    • os register is "Create a new account and store credentials", with --url defaulting to http://localhost:3000. It is not "Register the local stack as a deployable target".
    • Home: cli.mdx § Authentication (L2953-3058 at the head) holds the true rows for os register, os login, os whoami, os logout and os cloud login. The skill keeps its true os cloud login / logout / whoami row.
  • Item 5, deletions and tokens. Each cut fact has a home. One home is itself imprecise (S1).
    • Cut 1, the os dev row: " — idempotent, never overwrites an existing account" (53 bytes). Facts: (a) a later boot does not reseed; (b) an existing account is never overwritten. The named home is "on an empty DB only", which implies both literally. But the code's condition is "no login yet, and the seed address unclaimed", not an empty DB. See S1.
    • Cut 2, Verify item 2: "A bare field (missing record.) fails here." (47). Home: the section intro ("a bare field ref in a predicate (done for record.done) faults … Both are caught at author time by one command"). Carried.
    • Cut 3, the os lint parenthesis (86, including the line break). Facts: lint is a style pass; it does not replace validate. Homes: the os lint row ("Style/convention lint on metadata files") and the rule of thumb. Carried.
    • Cut 4, the Production port row: " — a shifted port breaks reverse-proxy upstreams, better-auth callback URLs, and CORS trusted-origins as opaque 403/502s" (122). Home: Production guidance, "Keep these in sync … (mismatch ⇒ better-auth Invalid origin 403 / CORS failures)", whose list names the proxy upstream, callbackURL and OS_TRUSTED_ORIGINS. Only "502" is lost, and the body says so (N2).
    • Cut 5: " Don't rely on the 3000 default; it collides easily on shared hosts." (72). Home: "Pin the port explicitly", plus the resolution chain showing 3000 (default). The reason "collides on shared hosts" is lost (N2).
    • Cut 6, Testing: " — with the in-memory driver a full bootstrap is cheap" (58). Home: the intro, "tests run in milliseconds". This is a pure deletion across a line break. The comma and "and there is no reset()" were already there. It is not a re-wrap.
    • Cuts 7a and 7b (88 and 68): see item 4.
    • Sum: 53 + 47 + 86 + 122 + 72 + 58 + 88 + 68 = 594. Additions: 13362 − 13140 + 594 = 816. Both match the body.
    • Readings: main (base and origin/main alike) is 13140 bytes, so 3285 tokens against a ceiling of 3287. The head is 13362 bytes, so ceil(13362/4) = 3341. The new ceiling is 3341, pinned at the measurement, and the remainder over the old ceiling is +54. The head's script exits 0 with headroom 0.
    • The ratchet script diff changes one row (operations.md 3287 → 3341) and adds a comment block. The previous 3108 → 3287 comment is kept. The new comment names each cut and its home, gives the byte arithmetic, and quotes the ruling verbatim ("「A:先削重复,余额按实测提高」 (ruling record 6513306068 on [Decision] skills token ratchet: price evals/** out (they never enter a customer context), and make the standing 「先削重复,余额按实测提高」 ruling the gate-recognised path for teaching text — or keep the per-raise ruling round #27847)"). The body quotes the same. No other row moves.
    • No line was re-wrapped. Every removed line in operations.md is a cut or a row edit in place.
  • Item 6, docs pages. TRUE, and consistent with the skill.
    • cli.mdx: the use-runtime row; § Deploy to a self-hosted runtime (examples taken from the command's own examples, the key and its probe, storage, which of the two commands wins, --forget, a flag table); the os deploy lead, the --confirm-access-upgrade row and § To a self-hosted runtime; § MCP / os mcp connect; the -u / -t defaults on os package install. Every sentence matches the code above.
    • publish-and-preview.mdx § 3: one paragraph, TRUE, with the switch condition stated.
    • connect-mcp.mdx: a single hunk (L25-41), inside § "Claude Code (one command)", the section the seat confirmed by name. The rest of the page is unchanged. No extra change is needed for consistency. The hand-typed lines stay, and the /mcp sign-in text that follows still applies.
    • The anchors #os-mcp-connect, #deploy-to-a-self-hosted-runtime and #to-a-self-hosted-runtime resolve (check:doc-anchors exit 0 in the gate record).
  • Item 7, evals. The JSON parses. Ids run 1-22 with no gaps. In every case each must_contain appears in its expected_output and no must_not_contain does.
    • Case 21: TRUE. Use-runtime, the probe, --no-verify, cloud.json (0600) across login and logout, os deploy / d with build, dry-run plan, inline install, live on hotLoaded, --confirm-access-upgrade, the install fallback, and --forget / switch. must_not_contain: "os deploy --url" is right, because os deploy has no --url.
    • Case 22: TRUE. claude mcp add --transport http <name> <url>, the environment deploy would pick on ObjectOS Cloud, --url naming the server objectstack, --name, --print, no token, and /mcp. must_not_contain: "--client cursor" is right, because the only value is claude-code.
    • Neither case fails a careful answer, apart from the low risks in N5.
    • Sweep: no case rewards os login, os register, whoami or a localhost:3000 install default. Case 17 (cloud os deploy) names the cloud environment in its prompt and stays true. No other skill file teaches os login, os register, use-runtime or claude mcp add.
  • Item 8, process. Checked.
    • The changeset has '@objectstack/skills': patch. Clause-②: no and Metadata-protocol: none are line-initial in the changeset (L7-8) and in the body (L6-7). The body has Fixes #31143 (L4).
    • The model waiver is in the body and names no model ("the project's default model"). No model identifier appears in the body, the diff or the commit. The trailers are Co-Authored-By: Claude <noreply@anthropic.com> and Claude-Session:.
    • The cross-seat note for cli.mdx and publish-and-preview.mdx is in the body, and connect-mcp.mdx is named as seat-confirmed.
    • Only claimed or confirmed files are touched: the claim's surface, connect-mcp.mdx by ruling 1, the cuts by ruling 2, and the ratchet row under ruling 6513306068.
    • Tier: check-governed-merges --pr 31225 exits 3. GOVERNED, Tier H, skills/** ×2. The other five paths are off the register. The size is +221 / −18, under 3000. The PR is a draft with no assignees. Labels: documentation, size/m, tooling.
    • Main moved 5 commits (listed in Scope). None touches these files or the cited code.
    • The body's code-site table checks out line by line, apart from one off-by-two (N7).
  • Item 9, other sites. Confirmed below (③).

② Semver level

'@objectstack/skills': patch is right. The skill prose and an eval fixture change, and @objectstack/skills ships both. The three docs pages are site content with no package. The ratchet script is repo tooling. No CLI behaviour moves (both commands are already on main), so Clause-②: no and Metadata-protocol: none are right.

③ Boundary flags

  • Claim. All 7 paths are inside claim 6711070771 plus confirmations 6711424579. No breach. On record order: the confirmations comment (18:36Z) was posted after the commit (18:12Z) and after the PR opened (18:34Z). It says the two rulings were given mid-flight and put on the record before review. That matches the brief, so it is not a breach (N9).
  • Ratchet. One row moved, by exactly the measured remainder, under standing ruling 6513306068, quoted in the script and the body, after real same-file deletion. Not a budget breach.
  • Tier. GOVERNED, Tier H. Landing needs the maintainer's merge or an APPROVED review from GOVERNED_APPROVERS, and the body's 「需要你做的」 says so. Check-runs on this head, read 2026-11-07T18:43Z: 46 runs, 30 success, 12 skipped, 4 in progress, 0 failed.
  • Other sites outside the claim, confirmed for filing.
    1. content/docs/ai/agents.mdx L99. The Claude Code row gives only the hand-typed claude mcp add --transport http objectstack https://<your-deployment>/api/v1/mcp. It is still true, but it does not mention os mcp connect. Confirmed.
    2. content/docs/getting-started/build-with-claude-code.mdx L475-481. Two hand-typed lines (local and deployed), confirmed. L502 is the headless --header "x-api-key: …" form. os mcp connect stores no token and passes no header, so L502 is not a candidate. Only L477 and L480 are.
    3. content/docs/releases/v16.mdx L76 and L468 describe the dev banner's ready-to-paste claude mcp add line. This is release history, true as written. Confirmed; leave it.
    4. Extra, not on the thread's list: content/blog/the-ontology-is-the-software.mdx L282, a hand-typed line in a blog post. Historical; leave it.
    5. Extra, CLI question, not teaching: os environments create --activate does not call activateCloudDeployTarget (confirmed, item 1). The body already lists it.

④ Design judgment

  • should-fix S1: cut 1 deleted the accurate half of the os dev row and kept the inaccurate half as its home.
    • The cut text, "idempotent, never overwrites an existing account", is what os dev --help says (dev.ts L426).
    • The kept text, "on an empty DB only", is not the gate. dev.ts L777-781 says the gate is "no login yet, and the seed address unclaimed", "rather than on an empty user table". dev-admin-seed-gate.ts explains why: with declarative people seeds the DB is not empty, and the admin is still seeded.
    • The cut facts are carried only because "empty DB" literally implies them.
    • The text is older than this PR, so this is not a false claim the PR teaches. But the PR names it as a home.
    • Fix: write "with no login yet" in place of "on an empty DB only". That is −2 bytes and stays inside 3341. Update the ratchet comment's home and the body table to match.
  • nit N1: the skill row and eval 21 say os environments switch returns the target to the cloud with no condition. That is true only when the switch records the id in cloud.json, which means it ran against the os cloud login control plane (switch.ts L78-79). cli.mdx and publish-and-preview.mdx state the condition. It is the common case, and the row is short by design.
  • nit N2: cuts 4 and 5 drop two small reasons: "502" (the body says so) and "it collides easily on shared hosts". The rules themselves keep their homes.
  • nit N3: cli.mdx § To a self-hosted runtime mirrors deploy.ts's header on two edges. These are docs only and optional.
    • Step 2 says "nothing is installed". A runtime that predates the dry run ignores it and installs. install.ts L512-520 says so out loud and exits 1.
    • Step 4's "could not load … loads when the runtime restarts" branch is, per install.ts L622-624, never reached on the inline path that os deploy uses. There, a register failure is refused 422 at step 3.
  • nit N4: cli.mdx says the server name is the slug, else the display name, else the id. environmentServerName also lower-cases it and turns other characters into - ("Acme Prod" becomes acme-prod).
  • nit N5: two eval strings carry small risk. Case 21's --token fails an answer that uses only OS_RUNTIME_TOKEN=… os environments use-runtime <url>. Case 22's --client cursor could catch an answer that explains the limit. Both are low risk.
  • nit N6: the +7 edit left ragged line wraps. In connect-mcp.mdx: "environment's name; --url takes" / "a self-hosted or local server's base URL…". In cli.mdx: "name it with --url. Its". Docs are not ratcheted, so a re-flow costs nothing.
  • nit N7: the PR body has two stale numbers. "With the teaching added: 3481 (+194 over)" does not match 13140 + 816 = 13956 bytes, which is 3489 (+202). It was probably measured before the +7 fix. And forgetRuntimeTarget is at L65-69, not L67-71. The final readings (3341, +54) are right.
  • nit N8: two passages the PR did not touch describe only the cloud path. One is the skill's "os deploy exit 1:" paragraph ("log in (os cloud login)"). The other is cli.mdx § os dev L168-171 ("when you are not logged in to ObjectOS Cloud the same key runs os cloud login first"). Neither holds with a runtime target. The new rows and sections state the runtime path correctly, so this is optional.
  • nit N9: record order. The confirmations comment was posted 23 minutes after the commit. It says the rulings were mid-flight, so there is no breach. Next time, the record could come before the push.

Verdict: PASS

… edge cases

The `os dev` row says the dev admin is seeded while no login exists yet
(the seed's real gate), not on an empty database. The CLI reference's
self-hosted `os deploy` steps say a runtime that predates the dry run
installs anyway and the command exits 1, and drop the restart case the
inline install path refuses at the install step. `os mcp connect`'s
server name is described as lower-cased. The token-ratchet row follows
the new measurement down.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnBUEjCrpMSAcQv3Gp
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4e0b64866e2dcafd9a123a2f723460373ebd6df4
Local-runs: probe — node scripts/check-skills-token-ratchet.mjs, run in a throwaway worktree of the head (/tmp/claude-0/seat3/wt-31225, since removed), because the delta lowers operations.md's ceiling and the measurement must equal it (exit 0; operations.md 13360 bytes, so 3340 of 3340).
Scope: delta review. One commit, 4e0b64866 (only parent e15e06e1c, a normal push). git diff e15e06e1c refs/seat3/pr-31225 touches 3 files: skills/objectstack-platform/references/operations.md (one row), scripts/check-skills-token-ratchet.mjs (the operations.md row and its comment), content/docs/deployment/cli.mdx (§ To a self-hosted runtime and § os mcp connect). Also read: the PR body v2. Cited code read on origin/main: packages/cli/src/commands/package/install.ts (L49-50, L511-518 dry-run fallback, L614-622 inline register refusal), packages/cli/src/commands/deploy.ts (header L55-68, L311-322), packages/cli/src/commands/mcp/connect.ts L80.

① Derived judgments

  • S1 is fixed. The os dev row now says the admin is seeded "with no login yet", which matches the seed gate the first record cited. operations.md is 13360 bytes, so 3340 tokens. The row is lowered from 3341 to 3340, so the ceiling still equals the measurement. The comment names the corrected home and the new figures (+220 net, +53 remainder) under ruling 6513306068. No other row changed.
  • N3 is fixed. Step 2 now says a runtime that predates the dry run ignores it and installs, and that the command says so and exits 1. That matches install.ts L511-518 and deploy.ts L313-314. Step 3 adds a package the running kernel cannot register to the refusals. That matches install.ts L620-622: an inline manifest whose register throws is refused with 422, and os deploy sends the manifest inline. Step 4 drops the "loads when the runtime restarts" case, which only the cloud-fetched lenient path reaches.
  • N4 is fixed. The name is described as lower-cased with each run of other characters turned into one -. That matches connect.ts L80. The same line also trims leading and trailing -, which the page does not say. That is not worth a push.
  • N6 is done for the one paragraph. It is a re-flow in a docs page, which the ratchet does not count.
  • N7 is fixed in the body. It now says 3489 (+202), and forgetRuntimeTarget is at L65-69. The deletion table names the corrected home.

② Semver level

Unchanged: '@objectstack/skills': patch.

③ Boundary flags

The 3 files are all inside the claim. No new file was added. Main has moved since the base, and nothing that landed touches these files.

④ Design judgment

The delta does only what the first record asked for. One nit:

  • nit N10: the ratchet comment's first-cut sentence now runs as one long line (≈150 characters). It is a code comment, it is not ratcheted, and it is cosmetic.

Verdict: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

维护者速读

  • 改了什么: 平台技能 operations.md 教会两条新 CLI 命令:os environments use-runtime(把自托管运行时设为 os deploy 目标)与 os mcp connect(一条命令把 MCP 接入 Claude Code),并写清自托管 os deploy 的四步(构建 → 预演计划 → 内联安装 → hotLoaded 即上线)、--confirm-access-upgrade、os dev 的 d 键、os package install 省略 -u / -t 时的回落。文档 cli.mdx、publish-and-preview.mdx、connect-mcp.mdx(仅 § Claude Code 一条命令)同步。eval 新增 21、22。7 个文件。
  • 为什么改: docs + skill: teach os environments use-runtime and the self-hosted os deploy target (follow-up owed by #30888 / PR #31137) #31143。feat(cli): os deploy and the os dev d key deploy to a self-hosted runtime remembered by os environments use-runtime #31137、feat(cli): os mcp connect registers your environment's MCP server with Claude Code #31149 已合并,但技能与文档都没教这两条命令。
  • 风险与回滚: 只改技能文字、文档与 eval。operations.md 先删 7 处同文件重复或错误(−594 字节,其中两行 os login / os register 原描述与代码不符),余量 +53 按常设裁定 6513306068 只上调该文件一行:3287 → 3340。可整 PR revert。
  • 先看: operations.md 的 deploy / MCP 行,以及 cli.mdx § To a self-hosted runtime。
  • 两分钟验证: node scripts/check-skills-token-ratchet.mjs,exit 0(operations.md 3340 / 3340)。
  • 需要你做的: skills/** 是受治理 Tier H 面,需 os-zhuang 或 hotlong 在 GitHub 上 Approve 一次。达档复核:全量 6711532623 PASS(head e15e06e1),增量 6711759200 PASS,Head-sha 4e0b64866e2dcafd9a123a2f723460373ebd6df4。批准后本席落地。
  • 席位意见: ACCEPT。

席位核对 — ACCEPT · domain:skills#3 · head 4e0b64866e2dcafd9a123a2f723460373ebd6df4

  1. 形态: draft → main,Fixes #31143;'@objectstack/skills': patch,Clause-②: no,Metadata-protocol: none;作者 objectstack-fleet[bot];7 个文件,在认领面(认领 6711070771,席位确认 6711424579:connect-mcp.mdx 指名、7 处删减与余量裁定)之内。
  2. 前提实测: 复核逐项对照 main 代码:use-runtime.ts(参数、--no-verify、--forget、cloud.json 0600、云登录登出保留、switch 切回)、deploy.ts / runtime-target.ts / install.ts(四步、预演回落、内联注册失败 422)、dev-deploy-key.ts(d 键)、mcp/connect.ts(环境解析与 os deploy 同源、--url 注册为 objectstack、仅 claude-code、不存 token)、login.ts / register.ts(删去的两行确为误述)、dev.ts 与 dev-admin-seed-gate.ts(无登录时才种管理员)。
  3. 门禁(钉 head 4e0b6486): dev 派生门禁 87 条全部 exit 0(gates-4e0b6486),trailers 0;席位在 head 工作树跑 ratchet exit 0;check-runs 51 条:37 success、14 skip、0 失败,可合并(clean);check-governed-merges --pr 31225:Tier H(skills/**)。
  4. 接受的偏差: MCP 行改为"the cloud environment (picked as os deploy picks it)",+7 token,经复核为真(use-runtime 目标下 os mcp connect 仍解析云环境);余量由预估 +47 变为 +53,按实测钉住。S1(os dev 行"empty DB"→"no login")、N3、N4、N6、N7 已在增量中改正。N1(技能里 switch 的条件)不改:会加 token,文档已写明条件。N2、N5、N10 不改。
  5. 范围外去向: agents.mdx L99、build-with-claude-code.mdx L477/L480 仍手写 claude mcp add;技能"os deploy exit 1"段与 cli.mdx § os dev 的 d 键句只讲云路径 → Docs and skill still type the MCP connect line by hand and describe os deploy as cloud-only #31235。v16 发布说明与博客为历史页,不动。
  6. 落地条款: Tier H。保持 draft,挂 pm:awaiting-merge,请 os-zhuang、hotlong 审批;获批后本席过落地前检、清标、ready、auto-merge;维护者直合亦可。

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 7, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 7, 2026 23:41
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit a5e280f Nov 8, 2026
57 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-0r0grk branch November 8, 2026 01:03
@objectstack-fleet objectstack-fleet Bot removed the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 8, 2026
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/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs + skill: teach os environments use-runtime and the self-hosted os deploy target (follow-up owed by #30888 / PR #31137)

3 participants