Repository navigation
Commit 3eb6dfc
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoGmHhhmdLvicENGKahs4yY4)_
Fixes #28299
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:automation skill 的集成食谱把「OAuth 厂商:还没有服务器跑授权,标记
blocked」改成一条可用食谱(recipe 6):`oauth2`(`authorization_code` /
`client_credentials`)和 `app-credential`(飞书 / 钉钉 / 企业微信)三种 `auth`
写法、token 由服务器按次获取、`authorization_code` 由管理员按组织连接一次(`POST
…/connectors/:name/connection`、回调、状态、撤销)。eval 1 的评分标准同步;SKILL.md
的「loader 还不能吃的文档」一行去掉 OAuth;lint 的 `flow-credential-literal`
解释文案不再说「要等服务器跑授权」。
- 为什么改:#26702 S2 全部合并(#28399、#28419、#28437)后,内置 `rest` / `openapi` /
`mcp` 都会按次向凭据句柄取 token,skill 里那句「no server runs the grant yet」已经是假话,会让
AI 作者把能用的集成标成 blocked(分诊定 p2 的产品缺陷)。
- 风险与回滚:只改教学文本、eval 评分标准和一句 lint 解释;不改
schema、键或运行时。`integration-recipes.md` 的 token 上限按实测从 2236 抬到
2523(同文件先删了两处复述,走 6513306068 的既定路径);SKILL.md 上限按实测下调到 6454。回滚即 revert。
-
先看:`skills/objectstack-automation/references/integration-recipes.md`(recipe
6
与两处删减)、`skills/objectstack-automation/evals/integrations-declared.json`(eval
1)、`packages/lint/src/rule-explanations.ts`(一段文案)。
- 两分钟验证:`node scripts/check-skills-token-ratchet.mjs`,预期两行
`objectstack-automation` 都 `headroom 0` 且整体绿;对照
`content/docs/automation/connectors.mdx` 的 Grant-bearing kinds 一节核对
recipe 6 的每一句。
- 需要你做的:Tier H(`skills/**`)——席位做完 Fable 契约复审并写席位核对后,由你批准 / 入队合并。
## What changes
**`skills/objectstack-automation/references/integration-recipes.md`**
(the card's item 2)
- The "Not yet" OAuth bullet ("no server runs the grant yet: the
instance is held degraded … Blocked on the outbound-OAuth card") becomes
**recipe 6 · OAuth or vendor-app token — the server runs the grant**:
served on `rest`, `openapi` and `mcp` (`http` transport only; a `stdio`
server's credentials ride `transport.env`); the token is fetched per
dispatch, kept in memory, refreshed before expiry and once on a `401`;
the three `auth` shapes (`authorization_code` with `authorizationUrl` /
`tokenUrl` / `scopes` / `clientId` / `credentialRef`,
`client_credentials`, `app-credential` with the vendor's token
placement); `client_credentials` and `app-credential` dispatch once the
ref is set; an `authorization_code` instance is degraded "not connected"
until an administrator holding `manage_connector_connections`
(organization admins by default) connects it once per organization from
a signed-in session (`POST
/api/v1/automation/connectors/:name/connection` → `{ authorizationUrl
}`, the vendor's redirect to `GET …/connectors/oauth/callback`, `GET`
status, `DELETE` revoke); endpoints `https://`; on `mcp` the endpoints
and `clientId` may be left to discovery. The `bearer`-ref warning stays,
as the card asked. Every sentence is read from
`content/docs/automation/connectors.mdx` § Grant-bearing kinds on `main`
and the code it describes
(`packages/services/service-automation/src/plugin.ts`,
`oauth2-grant/app-credential.ts`, the three `connector-*-plugin.ts`
registrations with `{ credentialHandle: true }`,
`packages/runtime/src/route-ledger.ts`, `plugin-security` default
permission sets).
- "Not yet" keeps WeChat Pay alone (no `signed` scheme).
- **Token ceiling, paid first by two same-file restatement cuts, then
re-locked at the measurement** under the standing path (ruling record
6513306068; maintainer ruling 2026-10-27 「A:先削重复,余额按实测提高」):
1. recipe 3's three-line agent-exposure example (`refresh_low_stock`
with `ai: { exposed: true, … }`) — its home is `objectstack-ai` §
Actions as AI Tools, which the surviving one-line pointer names;
2. recipe 5's "Each `operationId` is a named action in the flow palette
and to agents" — the Holds bullet already says actions come from the
upstream's `operationId`s (the `provider: 'rest'` half of the sentence
stays).
Arithmetic (`ceil(bytes / 4)`): 8938 bytes on `main` (2235 tokens, 1
token under the 2236 row) → 10089 bytes (2523 tokens): −308 cut, +1459
added, +1151 net. Row 2236 → 2523, pinned at the measurement; the
comment above the row records it.
**`skills/objectstack-automation/SKILL.md`** — the loader-gap sentence
(≈L416) drops "OAuth" (the loader takes it now). "request signing"
stays: the `signed` kind serves three schemes, and WeChat Pay's is still
missing, so a document declaring that scheme is still a gap. Pure
shrink; the row is re-locked 6457 → 6454 at the measurement.
**`skills/objectstack-automation/evals/integrations-declared.json`** —
eval 1 (DocuSign): the rubric now expects the `oauth2`
`authorization_code` declaration, the built-in `openapi` provider
obtaining the token per dispatch, and the per-organization connect, and
names one remaining gap (the vendor-signed Connect webhook, polled).
`must_contain` gains `oauth2`, `authorization_code`, `/connection` and
drops `blocked`; `must_not_contain` gains `blocked on outbound OAuth`.
**`packages/lint/src/rule-explanations.ts`** (the card's cross-domain
exception, as #28226) — the `flow-credential-literal` explanation's
"carry a grant whose token the server obtains per dispatch, and until it
runs that grant such an instance is held degraded and calls no upstream"
becomes "… obtains per dispatch through the built-in `rest`, `openapi`
and `mcp` providers (an `authorization_code` instance once an
administrator connects it); on a provider that does not ask the
credential handle such an instance is held degraded and calls no
upstream". The four facts its test pins
(`lint-flow-credential-literals.test.ts`) still hold verbatim. `git grep
-n -E "runs that grant|takes the grant|grant yet" packages/lint
packages/services` finds no other string: the service's own reason text
(`credentialHandleNotAppliedReason`, "does not yet ask the per-dispatch
credential handle") is still true for a third-party provider.
**Changesets** — `.changeset/28299-automation-skill-oauth-recipe.md`
(`@objectstack/skills` patch) and
`.changeset/28299-lint-grant-steer-served.md` (`@objectstack/lint`
patch), both carrying the two declaration lines.
**Readings (`skills/**` diff)** — lines / tokens (`ceil(utf8 bytes /
4)`, stamp line excluded):
| file | before | after |
|:--|:--|:--|
| `references/integration-recipes.md` | 158 lines / 2235 tokens (ceiling
2236) | 178 lines / 2523 tokens (ceiling 2523) |
| `SKILL.md` | 450 lines / 6457 tokens (ceiling 6457; the gate read 6454
on `main` + this diff) | 450 lines / 6454 tokens (ceiling 6454) |
| whole package, all `SKILL.md` (sum) | 56506 tokens (`main` 1f1a369) |
56504 tokens |
| whole package, priced bundle total | 220019 tokens (`main` 1f1a369,
whole shipped tree) | 220384 tokens (+365: +287 recipes, −3 SKILL.md,
+81 eval 1, measured not ratcheted) |
## Why
#26702 S2 landed in full: #28399 (`7e9f8143`, the built-in `rest` and
`openapi` providers register with `credentialHandle: true`,
`app-credential` served), #28419 (`380a1078`, the connectors page),
#28437 (`70646157`, an OAuth-protected MCP server connects). The skill
still taught "no server runs the grant yet: mark the integration
blocked", which triage graded a p2 product defect (「写给 AI 的文档与 skills
说错一句,等于产品缺陷」). The card's item 1 (the interim "no built-in provider
takes the grant yet" wording) is overtaken by item 2.
## Tests
Head `5af07314`, under the shared verify lock (`os-verify-lock.sh`,
VERDICT lines read):
- `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2
src/rule-explanations.test.ts src/lint-flow-credential-literals.test.ts`
→ Test Files 2 passed, Tests 24 passed (the four pinned facts of the
explanation, including `held degraded and calls no upstream`, still
hold).
- `pnpm --filter @objectstack/lint test && pnpm --filter
@objectstack/lint typecheck` → Test Files 184 passed (184), Tests 7718
passed (7718); typecheck exit 0; `VERDICT command-exit 0`.
- `skills/objectstack-automation/evals/integrations-declared.json`
parses (`json.loads`); no root eval runner exists for `skills/**` (see
Acceptance notes).
- No ablation: no tested behaviour changes (text, a rubric and an
explanation paragraph).
## Verification
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived **86** families for this diff (7
paths vs merge base `1f1a369b`); every line run with `</dev/null`, exit
codes captured before any pipe; `--ran` reconciliation at head
`5af07314`: **86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN**, all exit 0.
List: `/mnt/project-files/d1-merge/out/28299-gates-ran-5af07314.list`
(project file).
<details><summary>Readings worth a line</summary>
- `node scripts/check-skills-token-ratchet.mjs` → exit 0:
`skills/objectstack-automation/SKILL.md` 6454 / 6454 (headroom 0),
`references/integration-recipes.md` 2523 / 2523 (headroom 0);
`--self-test` 97 cases pass. On `main` 1f1a369 the same gate read the
recipes file at 2235 / 2236.
- The ten dist-dependent families (`check:dual-build-cjs-loads`,
`check:dts-closure`, `check:lean-entry-closure`,
`check:published-files`, `check:sourcemap-no-sources-content`,
`check:test-source-alias`, spec `check:skill-docs` / `check:skill-refs`
/ `check:duration-unit-keys`, lint `check:doc-formula-expressions`) ran
after `pnpm build --filter='./packages/**'` (77 tasks, 9m25s under the
lock) → all exit 0.
- `node scripts/check-plugin-teardown-shape.mjs --self-test` exited 3 on
the first pass (shallow clone cannot read its pinned positive control);
after `git fetch origin 621a487` it
exits 0 (48 cases) — the recorded line is the rerun.
- `pnpm check:pm-dispatch-gates` fast tier 1937 cases in 441 s, exit 0.
- `pnpm check:pm-skill-id-lint`, `check:doc-authoring`,
`check:role-word`, `check:keyed-text-bounds`,
`check:closed-ceremony-text`, `check:issue-citations`,
`check:changeset-gate-self-tests`, `check:empty-changeset`,
`check:adr-0087-registration` → exit 0.
</details>
CI at report time: in_progress (the PR has just been opened).
## Acceptance notes
- `SKILL.md` keeps "request signing" in the loader-gap list:
`ConnectorSigningSchemeSchema` serves `aliyun-acs3-hmac-sha256`,
`tencentcloud-tc3-hmac-sha256` and `alipay-v3` only, and
`integration-recipes.md` still lists WeChat Pay under "Not yet", so the
words are still true for a document declaring any other scheme.
- No root eval runner exists for `skills/**` (the fixtures score the
skill for maintainers; `check:skill-eval-tokens` is the `ui/`
catalogue's gate). The eval file was validated as JSON and measured by
the token ratchet (eval fixtures are measured, not ratcheted).
- Recipe 6 does not name a Console Connect page: none exists on `main`
(the route ledger calls the connect "the Console's Connect / Reconnect
flow (ADR-0143 S4)", a later #26702 slice this card does not wait on).
The recipe teaches the three HTTP calls the ledger serves.
- Not changed, noted for the server lane:
`credentialHandleNotAppliedReason` in `service-automation` says a
provider "does not yet ask" the handle — true for a third-party
provider, so left as is.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No schema touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; the lint package's own lint is CI's run.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` not touched.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
repo's own Chinese form).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibvTqcjYUDnn7bEkHGx
---------
Co-authored-by: os-tesla <yinlianghui@steedos.com>
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1c61f48 commit 3eb6dfc
7 files changed
Lines changed: 87 additions & 19 deletions
File tree
- .changeset
- packages/lint/src
- scripts
- skills/objectstack-automation
- evals
- references
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4710 | 4710 | | |
4711 | 4711 | | |
4712 | 4712 | | |
4713 | | - | |
4714 | | - | |
| 4713 | + | |
| 4714 | + | |
| 4715 | + | |
| 4716 | + | |
4715 | 4717 | | |
4716 | 4718 | | |
4717 | 4719 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
486 | 486 | | |
487 | 487 | | |
488 | 488 | | |
489 | | - | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
490 | 494 | | |
491 | 495 | | |
492 | 496 | | |
| |||
931 | 935 | | |
932 | 936 | | |
933 | 937 | | |
934 | | - | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
935 | 961 | | |
936 | 962 | | |
937 | 963 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
413 | 413 | | |
414 | 414 | | |
415 | 415 | | |
416 | | - | |
| 416 | + | |
417 | 417 | | |
418 | 418 | | |
419 | 419 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
| 10 | + | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
102 | | - | |
103 | | - | |
104 | | - | |
| 102 | + | |
105 | 103 | | |
106 | 104 | | |
107 | 105 | | |
| |||
144 | 142 | | |
145 | 143 | | |
146 | 144 | | |
147 | | - | |
148 | 145 | | |
149 | 146 | | |
150 | | - | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
151 | 174 | | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | 175 | | |
158 | 176 | | |
0 commit comments