Skip to content

Commit 1f92c72

Browse files
docs(skills): teach the stack's push block — direct transport, credentialRef only, relay not yet served (#30764)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DSNaEBXm47RQNvdu7w6Vook)_ domain:skills#1 · os-elon-musk · session_016WV4d1hJHYMCXtCDZwGBLV Fixes #30092 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:`objectstack-platform` 技能的运维参考页新增「Mobile push」一小节,讲清 stack 的 `push` 配置块怎么写;`SKILL.md` 里 `requires` 表的 `push` 那一格加了一个指向它的链接;#29969 那条推送 eval 用例补上 `transport: 'direct'` 与 `credentialRef` 的断言。 - **为什么改**:技能原先只说有 `push` 这个键,没教怎么填。AI 要给 `os serve` 开推送时会去猜配置、可能把 APNs/FCM 密钥直接写进配置,或选一个尚未上线的中继通道。 - **风险与回滚**:纯技能文档 + eval,`@objectstack/skills` patch;token 棘轮靠同文件删除重复内容付账(SKILL.md −2、operations.md −3),未抬任何上限。回滚即撤这一个 commit。 - **先看**:① `skills/objectstack-platform/references/operations.md` 新增的 `## Mobile push` 段;② 同文件里为付账删掉的两行「Common ops pitfalls」(与 Ports & networking 一节重复);③ `evals/config-plugins-ops.json` 第 12 条。 - **两分钟验证**:对照 `packages/spec/src/system/push-config.zod.ts` 与 `packages/services/service-push/src/capability-arg.ts`,确认今天只服务 `direct`、中继(含空 `{}`)启动即拒、密钥只经 `credentialRef`。 - **需要你做的**:`skills/**` 属 Tier H,需一位受管审批人(os-zhuang 或 hotlong)批准后合并。 ## What changes - `skills/objectstack-platform/references/operations.md` gains `## Mobile push`: `requires: ['push']` reads the top-level `push` key (`PushServiceConfigSchema`); the served transport today is `transport: 'direct'` only, one block per vendor, at least one (`apns`: `teamId`, `keyId`, `topic`, `environment`, `credentialRef`; `fcm`: `projectId`, `credentialRef`); `credentialRef` names each secret and never holds it, and an inline `privateKey` / `serviceAccount` / PEM / JSON value is refused; the relay transport (also what `{}` or an absent key reads as) is refused at boot until its sender ships (#29920, open). - `skills/objectstack-platform/SKILL.md`: the `requires` table's service row points `push` at that section (one pointer, no new line). - `skills/objectstack-platform/evals/config-plugins-ops.json` case 12 (#29969's): the expected output teaches the direct shape and the relay's status; `must_contain` gains `transport: 'direct'` and `credentialRef`, `must_not_contain` gains `serviceAccount:`. Case shape unchanged. - `.changeset/30092-platform-skill-push-block.md`: `'@objectstack/skills': patch`. Facts read off `origin/main` `254f2ec1`: `packages/spec/src/system/push-config.zod.ts` (the schema, its `strictObject` guidance maps for inline secrets, `PushRelayTransportConfigSchema` marked `[PLANNED — not yet served]`), `packages/services/service-push/src/capability-arg.ts` (`resolvePushCapabilityArg` throws when `config.transport !== 'direct'`, absent and `{}` included) and `src/credential.ts` (open-tier resolver = environment variable named by the reference). ### Token ratchet — paid by same-file deletion, no ceiling moved | File | before | after | ceiling | |:--|--:|--:|--:| | `skills/objectstack-platform/SKILL.md` | 6010 | 6009 | 6011 | | `skills/objectstack-platform/references/operations.md` | 3119 | 3116 | 3119 | | bundle total (whole shipped tree) | 226670 | 226666 | — | Lines: `SKILL.md` 413 → 412; `operations.md` 199 → 212; all `SKILL.md` files summed 4523 → 4522. Deleted restatement that paid for it: - `operations.md`: the `os validate` row's list of the three gates (the Verify-your-work section right below lists them); the "Use `os validate` as the fast inner-loop check…" sentence (the code block's comments say it); "run the **same** structural + semantic gates" → "Both run the same gates"; the `os lint` aside's example list; two Common-ops-pitfalls rows (`Port N is already in use`, better-auth `Invalid origin` 403) that restate the Ports & networking section's production row and sync list. - `SKILL.md`: the auth-callback example list in "`auth` implies `email`". ## Tests Docs and eval fixture only; no code behaviour changes, so no ablation. ## Acceptance notes - Kept to the file surface: `references/plugin-development.md` (held by PR #30726) and `packages/**` untouched. ## Verification Local runs on head `a5d42984` (after `pnpm install` and a `turbo run build` of the `@objectstack/spec` / `lint` / `formula` / `objectql` closures under `os-verify-lock`). `node scripts/pm/dispatch-gates.mjs --ran` → `✓ dispatch-gates --ran: 46 derived famil(ies) accounted for — 45 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).` `pnpm lint` exit 0. NOT MEASURED: `check:dual-build-cjs-loads`, reason: PREREQUISITE NOT MET (needs every package's `dist/`, i.e. a whole-tree build); a skills/docs diff moves no build output, so it is left to CI. <details><summary>Exit codes (45 + 1 + lint)</summary> ```text # head a5d4298 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-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-skills-token-ratchet.mjs :: exit 0 node scripts/check-skills-token-ratchet.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/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:changeset-gate-self-tests :: exit 0 pnpm check:ci-filter-parity :: 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 3 pnpm check:gitlink-declared :: exit 0 pnpm check:merge-driver :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:published-files :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:role-word :: 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 pnpm lint :: exit 0 pnpm check:lean-entry-closure :: exit 0 ``` </details> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 97e4249 commit 1f92c72

4 files changed

Lines changed: 37 additions & 14 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-platform teaches the stack's `push` block in `references/operations.md` (Mobile push): `transport: 'direct'` with `apns` / `fcm` blocks is the transport served today, each secret is named by `credentialRef` and never written inline, and the hosted relay (also what `{}` reads as) is refused at boot until its sender ships; the `requires` table points `push` at it, and the push eval case asserts the shape
6+
7+
Clause-②: no
8+
Metadata-protocol: none

‎skills/objectstack-platform/SKILL.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ token through the `CAPABILITY_PROVIDERS` registry in
302302
| Token | Provider package |
303303
|:--|:--|
304304
| `automation` | `@objectstack/service-automation` — flows, and any declarative `connectors:` entry |
305-
| `analytics` `cache` `storage` `queue` `job` `messaging` `realtime` `settings` `sms` `push` | `@objectstack/service-` + the token |
305+
| `analytics` `cache` `storage` `queue` `job` `messaging` `realtime` `settings` `sms` `push` | `@objectstack/service-` + the token ([the `push` block](./references/operations.md#mobile-push)) |
306306
| `marketplace` | `@objectstack/service-package` |
307307
| `audit` `email` `sharing` `approvals` `webhooks` | `@objectstack/plugin-` + the token |
308308
| `pinyin-search` | `@objectstack/plugin-pinyin-search` |
@@ -331,8 +331,7 @@ Five rules that change what you write:
331331
- **Declaring is a demand.** A capability YOU declared whose provider package is
332332
absent is a hard boot error; one the platform auto-injects for you stays
333333
best-effort (warn and continue).
334-
- **`auth` implies `email`.** Auth callbacks (password reset, email
335-
verification, magic link, invitation) need the mail service, so the CLI
334+
- **`auth` implies `email`.** Auth callbacks need the mail service, so the CLI
336335
appends `email` whenever `auth` is required.
337336
- **Keep `automation` whenever `plugins:` lists a connector** — connector
338337
executors register their provider factories with it, and without it they have

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,20 @@
201201
{
202202
"id": 12,
203203
"prompt": "Turn on push notifications for our `os serve` deployment: our own iOS and Android apps, signed with our APNs key and our Firebase project.",
204-
"expected_output": "`requires: ['push']` on the stack root (the token mounts `@objectstack/service-push`) plus a top-level `push:` key on the stack definition for the transport; no host code, no `PushServicePlugin` in `plugins:`. The APNs signing key and the Firebase service-account JSON stay in the secrets layer (an environment variable in the open tier) and the config names them by reference (`credentialRef`): no `.p8` key, `privateKey` or service-account JSON written inline.",
204+
"expected_output": "`requires: ['push']` on the stack root (the token mounts `@objectstack/service-push`) plus a top-level `push:` key on the stack definition; no host code, no `PushServicePlugin` in `plugins:`. Their own signed apps take `transport: 'direct'`, the transport served today, with an `apns` block (`teamId`, `keyId`, `topic`, `environment`, `credentialRef`) and an `fcm` block (`projectId`, `credentialRef`). The APNs signing key and the Firebase service-account JSON stay in the secrets layer (an environment variable in the open tier) and each `credentialRef` names one: no `.p8` key, `privateKey`, `serviceAccount` or service-account JSON written inline. Not the hosted relay, which the boot refuses until its sender ships.",
205205
"files": [],
206206
"assertions": {
207207
"must_contain": [
208208
"requires",
209209
"'push'",
210-
"push:"
210+
"push:",
211+
"transport: 'direct'",
212+
"credentialRef"
211213
],
212214
"must_not_contain": [
213215
"BEGIN PRIVATE KEY",
214216
"privateKey:",
217+
"serviceAccount:",
215218
"\"private_key\""
216219
]
217220
}

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

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ to be re-run when commands are added.
1010
| `os init` | Scaffold a new project (alternative to `npx create-objectstack`) |
1111
| `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. |
1212
| `os dev --ui` | Also mount the bundled Console portal at `/_console/` (there is no separate `os studio` command) |
13-
| `os validate` | Validate `objectstack.config.ts` — Zod protocol schema, CEL/predicate validation (`record.<field>` existence), and widget-binding integrity. Same gates as `os build`, no artifact emitted. See [Verify your work](#verify-your-work). |
13+
| `os validate` | Validate `objectstack.config.ts`: the `os build` gates, no artifact emitted — see [Verify your work](#verify-your-work) |
1414
| `os lint` | Style/convention lint on metadata files |
1515
| `os info` | Print a metadata summary of the config (objects, apps, and other collections; `--json`) |
1616
| `os doctor` | Diagnose common setup issues |
@@ -39,7 +39,7 @@ os validate # Zod schema + CEL predicates + widget bindings — no artifact
3939
os build # the same three gates, plus emits dist/objectstack.json
4040
```
4141

42-
`os validate` and `os build` run the **same** structural + semantic gates:
42+
Both run the same gates:
4343

4444
1. **Zod protocol schema** — the stack conforms to `@objectstack/spec`.
4545
2. **CEL / predicate validation (ADR-0032)** — every `visible` / `disabled` /
@@ -49,13 +49,12 @@ os build # the same three gates, plus emits dist/objectstack.json
4949
3. **Widget-binding integrity (ADR-0021)** — every dashboard widget's
5050
`dataset` / `dimensions` / `values` resolves to a declared dataset/field.
5151

52-
Both exit non-zero with a located, corrective message. Use `os validate` as the
53-
fast inner-loop check after editing metadata and `os build` when you need
54-
`dist/`. In a scaffolded project these are `npm run validate` / `npm run build`.
52+
Both exit non-zero with a located, corrective message. In a scaffolded project
53+
these are `npm run validate` / `npm run build`.
5554

5655
**Rule of thumb: never report a metadata change as done until `os validate`
57-
passes.** (`os lint` is a *separate* style/convention pass — naming, labels,
58-
namespace prefixes — and does not replace `os validate`.)
56+
passes.** (`os lint` is a *separate* style/convention pass and does not
57+
replace `os validate`.)
5958

6059
## Ports & networking
6160

@@ -112,6 +111,22 @@ Port resolution is the same for `os dev` and `os start` (both spawn `os serve`):
112111
> endpoint that `os package install` targets, ship the **Installed Apps** page and
113112
> marketplace Setup nav as plugin metadata.
114113
114+
## Mobile push
115+
116+
`requires: ['push']` reads the top-level `push` key (`PushServiceConfigSchema`).
117+
Served today: `transport: 'direct'` only, one block per vendor, at least one:
118+
119+
```ts
120+
push: { transport: 'direct',
121+
apns: { teamId, keyId, topic: 'com.acme.app', environment: 'production', credentialRef: 'APNS_P8_KEY' },
122+
fcm: { projectId, credentialRef: 'FCM_SA_JSON' } },
123+
```
124+
125+
`credentialRef` names each secret (the `.p8` key, the service-account JSON; an
126+
env-var name in the open tier), never holds it: `privateKey`, `serviceAccount` or
127+
a PEM/JSON value is refused. The relay transport (also what `{}` or no key reads
128+
as) is refused at boot until its sender ships.
129+
115130
## Shipping a plugin
116131

117132
Authoring stops at `kernel.use(plugin)`; these three make it distributable.
@@ -189,8 +204,6 @@ describe('stack boot', () => {
189204
| Symptom | Likely cause |
190205
|:--------|:-------------|
191206
| `os dev` hangs at "Loading metadata…" | Circular import in `objectstack.config.ts` — run `os validate` |
192-
| `os start` exits with "Port N is already in use" | Intended: production never auto-shifts ports. Free the port or set `PORT=<n>` — see [Ports & networking](#ports--networking) |
193-
| better-auth `Invalid origin` 403 after a port/host change | Port or hostname out of sync with `OS_AUTH_URL` / `OS_TRUSTED_ORIGINS` — see [Ports & networking](#ports--networking) |
194207
| Migrations apply locally but not in cloud | `env` scoping on the dataset excludes the target environment |
195208
| Adapter 404s on auto-generated routes | `enable.apiEnabled: false` on the object, or missing `os build` |
196209
| LiteKernel test passes, ObjectKernel boot fails | Test missed a plugin the CLI auto-registers — compare your test's `use()` list against the `os dev` boot log |

0 commit comments

Comments
 (0)