Skip to content

Commit e0b8c2c

Browse files
docs(skills): teach os deploy, the managed-package dev loop, in the platform skill (#31019)
<!-- claude-projects-attribution: {"github_login":"huangyiirene"} --> _Requested by **Irene** · [project thread](https://claude.ai/code/project/chan_01TJXtQ1tVD26ecLDZxFS67x?thread=cmsg_01TJXtQ1tVD26ecLDZxFS67xBr4PNiYAvCJMKBx5JSNzPr)_ Fixes #30968 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:`objectstack-platform` skill 现在教 `os deploy`:`references/operations.md` 的「Environments & deploy」表新增一行(何时用:在组织的开发环境上看到改动;入口 `os deploy` / `npm run deploy` / `os dev` 里按 `d`;如何选环境;`--no-wait`、`--wait-timeout`);表下一段讲 `os deploy` 退出码 1 的三种读法(写入前拒绝、环境自身构建失败、等待超时)及下一步。`SKILL.md` 的 Part 3 指针点名 `os deploy`。新增 eval 17。 - **为什么改**:PR #30933 已合入 `os deploy`,但 skill 里 0 处提到它,助手会继续教「先 `os package publish` 再手动安装」。 - **风险与回滚**:只改 skill 文本、一条 eval 和 `operations.md` 一行 token 天花板(+179,按裁决实测抬高);回滚即 revert,无运行时影响。 - **先看**:`skills/objectstack-platform/references/operations.md` 的 `os deploy` 行和其下的 exit 1 段落,对照 `packages/cli/src/commands/deploy.ts` 与 `utils/install-target.ts`。 - **两分钟验证**:`node scripts/check-skills-token-ratchet.mjs`,预期 exit 0,两文件读数为 6011/6011 与 3287/3287。 - **需要你做的**:`skills/**` 属 Tier H,需要 `os-zhuang` 或 `hotlong` 批准。 ## What changes Every flag, entry point and message the skill names was read from the landed CLI at `69e133ea0` (PR #30933, `780c6c34`), not from the card: | Taught | Source | |:--|:--| | `os deploy` builds, publishes `<manifest.version>-dev.<timestamp>`, installs, waits until served | `packages/cli/src/commands/deploy.ts` header and steps 1–6; `utils/dev-version.ts` | | `npm run deploy` | `packages/create-objectstack/src/templates/blank/package.json:13` and `packages/cli/src/commands/init.ts:851` (`"deploy": "objectstack deploy"`) | | `d` in `os dev` | `packages/cli/src/utils/dev-deploy-key.ts` (`deployKeyHint`: "Press d to deploy this app…") | | Environment: `--env <id>`, else the one `os environments switch` recorded, else the organization's only one | `utils/install-target.ts` `resolveInstallTarget` (flag → active → the only installable row) | | Kept across `os cloud login` | `commands/cloud/login.ts` `mergeCloudLogin` (same control plane and account) | | `--no-wait`, `--wait-timeout <seconds>` (600) | `deploy.ts` flags `wait` (`allowNo`) and `wait-timeout` (default 600) | | A refusal before any write publishes nothing (not logged in, no / several / unreadable environments, local build, non-semver `manifest.version`) | `deploy.ts` steps 1–4 ("write nothing anywhere") and their `printError` lines | | With no environment at all, create one in the ObjectOS Cloud console | `install-target.ts` refusal for zero candidates ("Create one in the ObjectOS Cloud console, then run this again.") | | `could not build …: <reason>`, keeps serving the previous build | `reportVerdict` case `failed` | | `Timed out after …`; install recorded; a failing build reads as pending for minutes; `--wait-timeout` or the ObjectOS Cloud console | `reportVerdict` case `timeout` | `skills/objectstack-platform/evals/config-plugins-ops.json` gains case 17: "I edited an object in my app locally. How do I see my change on my ObjectOS Cloud environment?" expects `os deploy`; its `must_contain` is `os deploy` alone (what any correct answer carries, and in its own `expected_output`); `must_not_contain`: `os push` (the name PR #30933 rejected). No existing eval in the skill teaches the old publish-then-install loop (`grep -n -i "deploy\|package publish" skills/objectstack-platform/evals/`). ## Token budget Readings from `node scripts/check-skills-token-ratchet.mjs`: | File | Before (`69e133ea0`) | After | Ceiling before → after | |:--|--:|--:|:--| | `skills/objectstack-platform/SKILL.md` | 5998 | 6011 | 6011 → 6011 (unchanged; the pointer fits the 13 headroom) | | `skills/objectstack-platform/references/operations.md` | 3108 | 3287 | 3108 → 3287 | | bundle total (whole shipped tree, eval included) | 228186 | 228660 | — | The order measured `operations.md` at 3116/3119; on `69e133ea0` it reads 3108/3108 (headroom 0), reported to the seat before editing. Deleted in `operations.md` first (restatement, each with its surviving home in the same file): 1. The `os dev` row's "The seeded admin is promoted to **platform admin**, so Setup/Studio work on first login." Home: Common ops pitfalls, "Login works but **Setup / Studio missing**" (auto-granted to the first registered human; "`--seed-admin`/`--fresh` does this"). 2. The `os validate` row's "the `os build` gates, no artifact emitted". Home: Verify your work, the code block (`os validate … — no artifact`; `os build # the same three gates, plus emits dist/objectstack.json`), which the row still links. 3. "Both run the same gates:" before the numbered gate list. Home: the line above it, `os build # the same three gates …`. 4. The Docker / Kubernetes row's "refuses to start unless `OS_DATABASE_URL`, `OS_AUTH_SECRET` and `OS_SECRET_KEY` are set" (review S5). Home: Common ops pitfalls, "The official image exits at start naming `OS_DATABASE_URL` / `OS_AUTH_SECRET` / `OS_SECRET_KEY`". The row keeps the image name, "at runtime, never baked in" and the SQLite-on-a-volume line. Review round 1 (contract review PASS at `965b2d64`, comment 6691650241): S1 eval `must_contain` trimmed to `os deploy`; S4 the `os package publish` row keeps main's "versioned package" (it publishes whatever `manifest.version` or `--version` says); S5 deletion 4 above saved 12 tokens, which S3 spent exactly (the zero-environment remedy), so the measurement stays 3287 and the row is unchanged. S2 (the post-write cases: another install replaced it, an unreadable installation, a publish that reported no install) does not fit inside that saving and is left out; the skill teaches the three cases the card names. The remainder, +179 measured, raises only the `operations.md` row, pinned at the measurement, under the standing ruling quoted in `scripts/check-skills-token-ratchet.mjs` (ruling record 6513306068 on #27847), maintainer ruling 2026-10-27, tap A, verbatim: 「A:先削重复,余额按实测提高」. ## Review tier `dispatch-gates` mandates the contract-review tier for `skills/**` (clause ①); waived by the maintainer's standing order in this project (2026-11-03). The seat runs the contract review. ## Gates `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `2586f5ac` (5 paths vs merge base `69e133ea0`): **65 commands**, each run under `timeout 900` with `NODE_OPTIONS=--max-old-space-size=8192` after a full `pnpm build` (119/119 tasks), exit code captured before any pipe → **65 × exit 0** (the first round at `965b2d64` was also 65 × exit 0). `--ran` reconciliation: `65 derived famil(ies) accounted for — 65 run, 0 NOT-MEASURED`. `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD` → exit 0 before each push. `origin/main` is now `33bc8ae5f`; since the branch point it touches `skills/objectstack-ai`, `objectstack-api` and `objectstack-data` but no `objectstack-platform` file and not `scripts/check-skills-token-ratchet.mjs`, and `git merge-tree` merges it cleanly, so the per-file readings above stand (the bundle total is this branch's own tree). <details><summary>Ran list</summary> ``` node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs --self-test :: exit 0 node scripts/check-doc-route-spelling.mjs --advisory :: exit 0 node scripts/check-doc-route-spelling.mjs --self-test :: exit 0 node scripts/check-dts-emitted.mjs --self-test :: exit 0 node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 node scripts/check-empty-changeset.mjs --self-test :: exit 0 node scripts/check-self-test-wired.mjs :: exit 0 node scripts/check-self-test-wired.mjs --self-test :: exit 0 node scripts/check-self-test-workflow-commands.mjs :: exit 0 node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0 node scripts/check-skills-token-ratchet.mjs :: exit 0 node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0 node scripts/check-whole-set-label-write.mjs :: exit 0 node scripts/check-whole-set-label-write.mjs --self-test :: exit 0 node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0 node scripts/ci/snapshot-next.mjs --self-test :: exit 0 node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0 node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 node scripts/release-pending-publish.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 pnpm --filter @objectstack/spec run check:skill-docs :: exit 0 pnpm --filter @objectstack/spec run check:skill-refs :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:bash32-floor :: exit 0 pnpm check:changeset-gate-self-tests :: exit 0 pnpm check:ci-filter-parity :: exit 0 pnpm check:cli-command-ids :: exit 0 pnpm check:closed-ceremony-text :: exit 0 pnpm check:corpus-claim-drift :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:dts-closure :: exit 0 pnpm check:dual-build-cjs-loads :: exit 0 pnpm check:entry-guard :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:lean-entry-closure :: exit 0 pnpm check:merge-driver :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:parse-guard :: exit 0 pnpm check:pm-dispatch-gates :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:pnpm-filter-targets :: exit 0 pnpm check:pnpm-flag-spellings :: exit 0 pnpm check:published-files :: exit 0 pnpm check:ratchet-remedy-authority :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:role-word :: exit 0 pnpm check:scripts-symbol-anchors :: exit 0 pnpm check:skill-compatibility :: exit 0 pnpm check:skill-frame-sync :: exit 0 pnpm check:skill-identifier-liveness :: exit 0 pnpm check:skill-top-level-keys :: exit 0 pnpm check:sourcemap-no-sources-content :: exit 0 pnpm check:watch-hint-literal :: exit 0 ``` </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01J8VVAnbXbG1XBtvCznvwYJ <!-- signed: domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7 --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 769adff commit e0b8c2c

5 files changed

Lines changed: 43 additions & 7 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the platform skill teaches `os deploy`, the managed-package dev loop
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`objectstack-platform/references/operations.md` gains an `os deploy` row in "Environments & deploy": use it to see an edit on the organization's ObjectOS Cloud environment (also `npm run deploy` in a scaffolded project, or `d` in `os dev`); it builds, publishes `<manifest.version>-dev.<timestamp>`, installs that version and waits until the environment serves it. The row names how the environment is picked (`--env`, else the one `os environments switch` recorded, kept across `os cloud login`, else the organization's only one) and the `--no-wait` and `--wait-timeout` flags. A short paragraph reads an `os deploy` exit 1: a refusal before any write (with no environment at all, create one in the ObjectOS Cloud console), the environment's own build failure, and the wait timeout, each with the next step. `SKILL.md`'s Part 3 pointer names `os deploy`. Eval 17 of `evals/config-plugins-ops.json` asks how to see a change on the environment and expects `os deploy`. Paid inside `operations.md` by deleting four restatements (the `os dev` row's platform-admin sentence, the `os validate` row's gate summary, the "Both run the same gates:" lead-in, the Docker row's list of the official image's three required variables); the remainder raises that file's token ceiling by the measurement.

‎scripts/check-skills-token-ratchet.mjs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,18 @@ export const CEILINGS = new Map([
10511051
// 花板,怎么付?」, tap A, verbatim and untranslated: 「A:先削重复,余额按实测提高」.
10521052
['skills/objectstack-data/references/seeds.md', 1566],
10531053
['skills/objectstack-platform/references/bootstrap.md', 1351],
1054-
['skills/objectstack-platform/references/operations.md', 3108],
1054+
// 3108 -> 3287: `os deploy`, the managed-package dev loop — when to deploy
1055+
// rather than publish a release, its entry points, how it picks the
1056+
// environment, its flags, and how to read its exit 1 (refusal before any
1057+
// write, the environment's build failure, the wait timeout). Paid first by
1058+
// deleting restatement in the same file (the `os dev` row's platform-admin
1059+
// sentence, homed in the Setup / Studio pitfall; the `os validate` row's
1060+
// gate summary and the "Both run the same gates:" lead-in, homed in Verify
1061+
// your work; the Docker row's list of the image's three variables, homed in
1062+
// the image pitfall row); the remainder, +179 measured, raises the row,
1063+
// pinned at the measurement. Maintainer ruling 2026-10-27 on the decision card 「skills 修复卡的教学和 eval 都撞 token 天
1064+
// 花板,怎么付?」, tap A, verbatim and untranslated: 「A:先削重复,余额按实测提高」.
1065+
['skills/objectstack-platform/references/operations.md', 3287],
10551066
['skills/objectstack-platform/references/plugin-development.md', 3137],
10561067
['skills/objectstack-upgrade/references/examples-upgrade.md', 1197],
10571068

‎skills/objectstack-platform/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ existing account on the boot banner.
401401

402402
---
403403

404-
→ Part 3 — Operations: CLI, Testing, Deployment: [references/operations.md](./references/operations.md).
404+
→ Part 3 — Operations: CLI, Testing, Deployment (`os deploy` to see an edit on your Cloud environment): [references/operations.md](./references/operations.md).
405405

406406
---
407407

‎skills/objectstack-platform/evals/config-plugins-ops.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,20 @@
287287
"OS_DEV_CRYPTO_KEY"
288288
]
289289
}
290+
},
291+
{
292+
"id": 17,
293+
"prompt": "I edited an object in my app locally. How do I see my change on my ObjectOS Cloud environment?",
294+
"expected_output": "Run `os deploy` from the project (`npm run deploy` in a scaffolded project, or press `d` in `os dev`). It builds, publishes `<manifest.version>-dev.<timestamp>`, installs it into your organization's only environment (or `--env <id>`, or the one `os environments switch` recorded) and waits until the environment serves it; `--no-wait` returns once the install is recorded. `os package publish` is for publishing a release version, not for this loop. On exit 1, a refusal before any write published nothing (log in with `os cloud login`, or pass `--env`); `could not build` is the environment's build failing while it keeps serving the previous build; a timeout means the install is recorded, so deploy again with a longer `--wait-timeout` or check the build in the ObjectOS Cloud console.",
295+
"files": [],
296+
"assertions": {
297+
"must_contain": [
298+
"os deploy"
299+
],
300+
"must_not_contain": [
301+
"os push"
302+
]
303+
}
290304
}
291305
]
292306
}

‎skills/objectstack-platform/references/operations.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
| Command | What it does |
66
|:--------|:-------------|
77
| `os init` | Scaffold a new project (alternative to `npx create-objectstack`) |
8-
| `os dev` | Start the dev server with hot metadata reload. `--seed-admin` (default **on** for plain `os dev`) seeds a loginable dev admin **in-process via the runtime** (env vars `OS_SEED_ADMIN*`) on an **empty** DB only — idempotent, never overwrites an existing account (default `admin@objectos.ai` / `admin123`; override with `--admin-email` / `--admin-password`; disable with `--no-seed-admin`). `--fresh` = ephemeral clean OS_HOME/DB, implies `--seed-admin`. The seeded admin is promoted to **platform admin**, so Setup/Studio work on first login. |
8+
| `os dev` | Start the dev server with hot metadata reload. `--seed-admin` (default **on** for plain `os dev`) seeds a loginable dev admin **in-process via the runtime** (env vars `OS_SEED_ADMIN*`) on an **empty** DB only — idempotent, never overwrites an existing account (default `admin@objectos.ai` / `admin123`; override with `--admin-email` / `--admin-password`; disable with `--no-seed-admin`). `--fresh` = ephemeral clean OS_HOME/DB, implies `--seed-admin`. |
99
| `os dev --ui` | Also mount the bundled Console portal at `/_console/` (there is no separate `os studio` command) |
10-
| `os validate` | Validate `objectstack.config.ts`: the `os build` gates, no artifact emitted — see [Verify your work](#verify-your-work) |
10+
| `os validate` | Validate `objectstack.config.ts` — see [Verify your work](#verify-your-work) |
1111
| `os lint` | Style/convention lint on metadata files |
1212
| `os info` | Print a metadata summary of the config (objects, apps, and other collections; `--json`) |
1313
| `os doctor` | Diagnose common setup issues |
@@ -36,8 +36,6 @@ os validate # Zod schema + CEL predicates + widget bindings — no artifact
3636
os build # the same three gates, plus emits dist/objectstack.json
3737
```
3838

39-
Both run the same gates:
40-
4139
1. **Zod protocol schema** — the stack conforms to `@objectstack/spec`.
4240
2. **CEL / predicate validation (ADR-0032)** — every `visible` / `disabled` /
4341
`requiredWhen` / validation rule / flow condition / sharing rule is parsed
@@ -97,9 +95,12 @@ Port resolution is the same for `os dev` and `os start` (both spawn `os serve`):
9795
| `os environments list` / `create` / `switch` | Manage cloud environments (prod/staging/dev) |
9896
| `os register` | Register the local stack as a deployable target |
9997
| `os cloud login` / `logout` / `whoami` | Cloud auth subcommands (these three only — there are no `os cloud logs/metrics/status`) |
98+
| `os deploy` (or `npm run deploy`, or `d` in `os dev`) | See an edit on your org's ObjectOS Cloud environment: build, publish `<manifest.version>-dev.<timestamp>`, install, wait until served. Environment: `--env <id>`, else the one `os environments switch` recorded (kept across `os cloud login`), else your organization's only one. `--no-wait`; `--wait-timeout <seconds>` (600) |
10099
| `os package publish [dist/objectstack.json] [--env … --install --visibility org]` | Upload the compiled artifact as a versioned package to the cloud catalog (ADR-0008 P3). Listing file, README, review and every screen the customer's administrator meets: [Marketplace handbook](https://objectstack.ai/docs/deployment/marketplace-handbook) |
101100
| `os package install <manifest-id │ ./dist/objectstack.json> [--version │ -u/--url http://localhost:3000]` | Install a package into a **running** runtime. Catalog mode (by manifest id) or air-gapped local-artifact mode. Auths as a **target runtime** user (`-t/--token` or `--email/--password`; env `OS_RUNTIME_TOKEN` or `OS_RUNTIME_EMAIL`/`OS_RUNTIME_PASSWORD`), not the cloud login |
102101

102+
**`os deploy` exit 1:** a refusal before any write publishes nothing: log in (`os cloud login`), pick an environment (`--env`; none: create one in the ObjectOS Cloud console), or fix the local build or `manifest.version`. `could not build …: <reason>`: the environment's build failed, it keeps serving the previous one; fix and deploy again. `Timed out after …`: the install is recorded and a failing build reads as pending for minutes; deploy again with a longer `--wait-timeout`, or see the build in the ObjectOS Cloud console.
103+
103104
> **Cloud connection & marketplace (`@objectstack/cloud-connection`, ADR-0008/0009):**
104105
> the open runtime-side cloud client; its `MarketplaceInstallLocalPlugin` serves the
105106
> install-local endpoint `os package install` targets.
@@ -154,7 +155,7 @@ the other imports: [Using Plugins](./plugin-development.md#using-plugins).
154155
| Edge (Cloudflare Workers, Vercel Edge) | `driver-turso` (**cloud / EE only**) | `@objectstack/hono` | Cold-start friendly; LiteKernel only |
155156
| Serverless (Lambda, Vercel functions) | `driver-sql` (`pg` with pooler) / `driver-mongodb` | `@objectstack/hono` | Mind cold-start: prefer LiteKernel |
156157
| Browser / WebContainer | `driver-sqlite-wasm` | none (in-process) | Playground, demos |
157-
| Docker / Kubernetes | any | any | Use `os start` as the entrypoint; pin `PORT` and `EXPOSE` it (see [Ports & networking](#ports--networking)). The official image (`ghcr.io/objectstack-ai/objectstack`) refuses to start unless `OS_DATABASE_URL`, `OS_AUTH_SECRET` and `OS_SECRET_KEY` are set at runtime, never baked in; an explicit `OS_DATABASE_URL=file:/path/on/a/mounted/volume/app.db` passes, so SQLite on a volume still works |
158+
| Docker / Kubernetes | any | any | Use `os start` as the entrypoint; pin `PORT` and `EXPOSE` it (see [Ports & networking](#ports--networking)). The official image (`ghcr.io/objectstack-ai/objectstack`): its required variables (pitfalls below) set at runtime, never baked in; an explicit `OS_DATABASE_URL=file:/path/on/a/mounted/volume/app.db` passes, so SQLite on a volume still works |
158159

159160
## Health & observability
160161

0 commit comments

Comments
 (0)