Repository navigation
Commit 49700e4
Fixes #23787
Clause-②: no
## What changed
Ruling C′ item 2 on #23128 (record 6142279866, the maintainer's 「同意」)
retired the imperative `http.server` mount as an app-author surface: an
app declares `apis:`, computes in a flow, and registers code as a stack
function or a connector; the `http.server` service itself stays the
platform plugins' own door. This PR is the skill half of that item plus
the ADR-0088 row the filer added. The spec half is PR #23785 (#23216);
none of its files are touched here, and the skill wording follows its
retire texts where they say the same thing.
- `skills/objectstack-api/SKILL.md`
- Table "Choosing between `apis:` and a code handler": the `http.server
mount (plugin code)` row is replaced by **A flow that runs your code** —
point a `type: 'flow'` endpoint at a flow whose `script` node names a
stack function (`defineStack({ functions })`), or whose
`connector_action` node calls a declared connector (outbound). The
`apis:` row is unchanged.
- The paragraph under the table now names the inbound `type: 'api'` flow
hook (a pointer to objectstack-automation → Inbound webhook (`api`)
triggers, which already teaches `hookId` / `secret`), states that an app
never mounts on `http.server` (same reasons as PR #23785's retire texts:
no caller identity, auth gate, rate limit, namespace carve-out or
OpenAPI entry), and names the three cases with no app surface today
instead of inventing a door.
- Section "Code routes: mounting on `http.server`" (26 lines including
its leading separator) is deleted, with the deprecated-alias,
`kernel:listening` and Hono-sealing notes.
- `skills/objectstack-api/evals/endpoints-auth-routes.json`, eval id 3.
The Stripe prompt is **kept** (it is the realistic lure for the retired
door) and the eval now expects the skill's statement of the gap plus
what holds for code: `expected_output` rewritten; `must_contain` =
`x-objectstack-signature`, `functions`; `must_not_contain` =
`http.server`, `kernel:ready`, `getService`. Chosen over re-targeting at
a served case because the eval's job is to catch an authoring AI
reaching for the retired door on exactly this prompt; evals 1 and 2
already cover the served `apis:` cases.
- `docs/adr/0088-metadata-kind-admission-and-retirement.md`: one
`**Amended**` entry on the Status line (2026-10-16, #23787, citing
#23128 ruling C′ item 2, record 6142279866), and the `router` row points
at it. The `router` KIND's retirement is unchanged; the amendment
narrows only the delivered form "imperative `http.server` mounts" to the
platform plugins. No other ADR edit.
- `.changeset/23787-api-skill-retired-route-door.md`:
`@objectstack/skills` patch with `Clause-②: no`. Measured: `skills/**`
ships through `packages/skills` (`files[]` = `dist`, `README.md`,
`CHANGELOG.md`; `scripts/sync-catalog.mjs` copies `skills/**` into
`dist/skills/`), so the text edit publishes. `docs/adr/**` ships in no
package's `files[]` (every tracked `package.json` scanned at b485122:
zero `adr`/`docs` entries), so the ADR edit owes nothing.
## Measurements behind the new text (base b485122)
- `apis:` types: `packages/spec/src/api/endpoint.zod.ts:174` — only
`object_operation` and `flow` execute; `script` / `proxy` are refused at
publish (`endpoint-publish-gate.ts:370-371`). The describe text already
sends script logic to "a flow whose script node runs your registered
function" and outbound calls to "a flow using a declared connector".
- Stack function from a flow: the `script` node's `config.function`
resolves through `engine.resolveFunction()` to `defineStack({ functions
})`
(`packages/services/service-automation/src/builtin/screen-nodes.ts:31-33,
409-419`; `engine.ts:4266-4275`;
`packages/spec/src/stack.zod.ts:992-1047`). A `type: 'flow'` endpoint
runs that flow (`packages/runtime/src/endpoint-executor.ts:253-262,
484-585`).
- Connectors: all five at this base (`connector-imap`, `-mcp`,
`-openapi`, `-rest`, `-slack`) are outbound only — they
`registerConnector` / `registerConnectorProvider` with the automation
service so a flow's `connector_action` node can dispatch to them; imap
is a read-only session that registers no `connector_action` handler.
Zero hits for `http.server`, `IHttpServer` or `.post(` under
`packages/connectors/*/src`.
- Inbound hook (PM reading b4, confirmed):
`packages/triggers/trigger-api/src/api-trigger.ts` arms `POST
/api/v1/automation/hooks/:flowName/:hookId` for a `type: 'api'` flow;
the start node's `config.secret` is required (`engine.ts:10807-10825`);
it verifies only ObjectStack's `x-objectstack-signature` in the
timestamped form through core's `verifyHttpSignature`
(`api-trigger.ts:142-170, 346-348`), requires a JSON object body (400
otherwise) and answers 202 queued. It is app-authorable (a flow is app
metadata; `skills/objectstack-automation/SKILL.md:340-354` teaches it),
so the skill names it — for a sender that signs with ObjectStack's own
scheme.
- Streaming: `endpoint-executor.ts` answers `{ status, body: { success:
true, data } }` for both executing types (`:291`, `:567-585`); there is
no streaming path.
- Real-app precedent: `examples/app-showcase` mounts no route handler of
its own; its recalc is a declared flow endpoint (`src/coverage.ts:133`,
`objectstack.config.ts:259`).
## Three cases with no app surface today — open questions for the
maintainer
The seat puts these on the card; they are stated in the skill as gaps,
not routed to the retired door.
1. A third-party callback verified by its own signature over the raw
body (the eval's Stripe case): trigger-api answers 401
`INVALID_SIGNATURE` to any post without a valid
`x-objectstack-signature` (`api-trigger.ts:321-348`), and a flow —
whether started by the hook or by a `type: 'flow'` endpoint — receives
the parsed JSON body as its trigger record, never the raw bytes or the
request headers (`api-trigger.ts:357-366`; `endpoint-executor.ts:567`
passes `ctx.body` to `buildAutomationContext`). A stack function
therefore cannot verify `Stripe-Signature`.
2. A streaming response: declared endpoints answer one JSON envelope
(`endpoint-executor.ts:291, 585`).
3. A protocol the platform does not speak: no declared surface exists by
definition, and `http.server` is an HTTP door in any case.
## `skills/**` readings
| File | Lines before → after | Tokens before → after (ceiling) |
|:--|:--|:--|
| `skills/objectstack-api/SKILL.md` | 434 → 415 (−19) | 4754 → 4642
(6314; headroom 1560 → 1672) |
| `skills/objectstack-api/evals/endpoints-auth-routes.json` | 55 → 55
(0) | 1042 → 1039 (1042; headroom 0 → 3) |
| Whole package (sum of every `skills/*/SKILL.md`) | 4408 → 4389 (−19) |
bundle total (whole shipped tree) 155016 → 154901 |
`node scripts/check-skills-token-ratchet.mjs` verdict lines:
- before, at b485122: `✓ check-skills-token-ratchet:
skills/objectstack-api/SKILL.md is 4754 tokens (ceiling 6314; headroom
1560).` · `✓ check-skills-token-ratchet:
skills/objectstack-api/evals/endpoints-auth-routes.json is 1042 tokens
(ceiling 1042; headroom 0).`
- after, at b4ea190: `✓ check-skills-token-ratchet:
skills/objectstack-api/SKILL.md is 4642 tokens (ceiling 6314; headroom
1672).` · `✓ check-skills-token-ratchet:
skills/objectstack-api/evals/endpoints-auth-routes.json is 1039 tokens
(ceiling 1042; headroom 3).`
No `CEILINGS` row moves. The eval's growth is paid inside the file by
content, not re-wrapping (its line count is unchanged; the byte count
fell 4165 → 4156).
## Verification (local, head b4ea190)
Derived after the final commit with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` (no paths): 49 commands —
the PM's 37 at 46ca793 plus 12 changeset-family gates the new
`.changeset/*.md` pulls in. Every command was run with its exit code
captured before any pipe and reconciled: `dispatch-gates --ran` → "49
derived famil(ies) accounted for — 45 run, 4 NOT-MEASURED (4 DERIVED
from a recorded exit 3)", exit 0, 0 UNRUN.
- 45 × exit 0, among them `check-skills-token-ratchet` (+
`--self-test`), `check:skill-docs`, `check:adr-anchors`,
`check-adr-links` (+ `--self-test`), `check-adr-symbol-anchors`,
`check:skill-identifier-liveness`, `check:skill-frame-sync`,
`check:skill-compatibility`, `check:role-word`, `check:doc-authoring`,
`check:pm-skill-id-lint`, `check:nul-bytes`, `check:published-files`,
`check-empty-changeset`, `check-changeset-no-major`,
`check-adr-0087-registration`, and `check:doc-formula-expressions`
(first exit 3 for an unbuilt `@objectstack/lint`; rebuilt with `pnpm
exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint --concurrency=2` under `os-verify-lock.sh`,
`VERDICT command-exit 0`, 88 s held; rerun exit 0).
- 4 NOT MEASURED locally (exit 3, PREREQUISITE NOT MET):
`check:dts-closure`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure`, `check:sourcemap-no-sources-content`. They
read the whole repository's `dist/`; a fresh worktree has none, and this
diff touches no package, so the whole-repo build is not owed here. They
enter the derivation only through ci.yml's Build Core job filter on
`skills/**`, and CI measures them after its full build.
- No package source changed, so no package test suite or typecheck is
owed; no tested behaviour changed, so no ablation.
## Acceptance notes
- Observation, not filed (class none; carrier: none):
`skills/objectstack-platform/rules/plugin-lifecycle.md:134` reads
`ctx.getService('http-server')`, the deprecated alias; the api-skill
text deleted here recorded that the alias is absent on the provider path
that registers no alias. Platform-plugin teaching outside this card's
file list.
- The remaining `kernel:ready` / `kernel:listening` mentions in
`skills/` (objectstack-platform, objectstack-i18n) teach plugin
lifecycle to platform-plugin authors, the primitive the ruling leaves
untouched. Out of scope.
- The spec's describe texts still spell the executing-type statement as
"17.x" (`endpoint.zod.ts:174-175`, `openapi-endpoints.ts:218`); the
skill keeps that spelling at its L154 for consistency with the spec. Not
this card.
## 维护者速读(草稿)
**改了什么**:`objectstack-api` 技能不再教应用作者把路由挂到 `http.server`
上。决策表的「需要代码」一行改为:声明 `type: 'flow'` 端点,代码以 stack function(`defineStack({
functions })`)由 flow 的 `script` 节点运行,或以 connector 由 `connector_action`
节点调用;新增一句指向 `type: 'api'` 入站 webhook;整节「Code routes」删除。eval 3
改为期待这套说法。ADR-0088 状态行加一条 Amended,`router` 行指向它。附 `@objectstack/skills`
patch changeset。
**为什么改**:#23128 裁决 C′ 第 2 条(记录 6142279866)已把 `http.server`
的命令式挂载从应用作者面退役;发布的技能文本和它的 eval 仍在教这扇门,写元数据的 AI 会照表执行。spec 半边在 PR
#23785,本 PR 是技能半边加 ADR 行。
**风险与代价(含回滚)**:只改文本,运行时零变化;技能行数与 token 都下降(SKILL.md 434→415 行、4754→4642
tokens;eval 1042→1039
tokens,天花板不动)。风险在于三类场景(第三方自带签名的回调、流式响应、平台不说的协议)今天确实没有应用可声明的落点,技能如实写明「没有」而不发明门;若维护者另有裁定,只需改那一句。回滚
= revert 本 PR 一个 commit。
**席位意见**:(留空)
**你要做的**:审批 Tier H(`skills/**` + `docs/adr/**`);并回答一个问题 —— 上述三类无落点场景(尤其
Stripe 这类自带签名、需原始请求体的回调)是否要给应用一个声明式入口,还是维持「平台插件自建门」的现状。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VqGhCg9wdsBXPz54uAEWg7)_
Co-authored-by: objectstack-fleet[bot] <objectstack-fleet[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
1 parent f7a5ebe commit 49700e4
4 files changed
Lines changed: 26 additions & 36 deletions
File tree
- .changeset
- docs/adr
- skills/objectstack-api
- evals
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
124 | 131 | | |
125 | 132 | | |
126 | 133 | | |
| |||
198 | 205 | | |
199 | 206 | | |
200 | 207 | | |
201 | | - | |
202 | | - | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
218 | | - | |
219 | | - | |
220 | | - | |
221 | | - | |
222 | | - | |
223 | | - | |
224 | | - | |
225 | | - | |
226 | | - | |
227 | 208 | | |
228 | 209 | | |
229 | 210 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | | - | |
| 30 | + | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| |||
0 commit comments