From 1bce9ede3ce4f8661cbeb8eaa046d95131d69a7e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 12:35:11 +0000 Subject: [PATCH 1/4] docs(skills/automation): teach clientSource 'installer' in the OAuth recipe Recipe 6 now shows the installer-supplied client beside the declared client, with when to use which: the administrator registers a client against the redirectUri the status read answers, connects with { clientId, clientSecret }, and the connection reads client: { source: 'installer', clientId, hasSecret }. Facts from content/docs/automation/connectors.mdx and the merged connector-auth schema. Two restatements are deleted first; the file's token ceiling moves by the measured remainder (2488 -> 2598) under the standing raise path recorded in the row comment. Claude-Session: https://claude.ai/code/session_01J8VVAnmBmMPE4eUyJhJRfx Co-authored-by: Claude --- scripts/check-skills-token-ratchet.mjs | 20 ++++++++++++++++++- .../references/integration-recipes.md | 9 +++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/check-skills-token-ratchet.mjs b/scripts/check-skills-token-ratchet.mjs index 5e8109225b4..7e50dcd8172 100644 --- a/scripts/check-skills-token-ratchet.mjs +++ b/scripts/check-skills-token-ratchet.mjs @@ -957,7 +957,25 @@ export const CEILINGS = new Map([ // and untranslated: 「A:先削重复,余额按实测提高」 — the standing path of // ruling record 6513306068 on #27847. // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(9949 / 4) - ['skills/objectstack-automation/references/integration-recipes.md', 2488], + // 9926 -> 10391 (card #29255; the file had shrunk 23 bytes under the row + // since, headroom 6 tokens): recipe 6 teaches `clientSource: 'installer'` + // beside the declared client — when to use which, the administrator's + // `{ clientId, clientSecret }` connect body against the status read's + // `redirectUri`, and the connection's `client: { source, clientId, + // hasSecret }` read — from content/docs/automation/connectors.mdx and the + // merged connector-auth schema. Paid down first, two restatements each with + // a surviving home: the authorization_code comment's "A user grants access + // once" (the paragraph under the block says an administrator connects it, + // once per organization), and "`client_credentials` and `app-credential` + // dispatch once the ref is set." (the file's opening paragraph says an unset + // ref leaves its instance degraded until set). Arithmetic: 9926 -> 10391 + // (+465 net: -100 cut, +565 added) -> ceil(10391 / 4) = 2598; the ceiling + // moves +110, pinned AT the measurement. Maintainer ruling 2026-10-27 on the + // seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」, + // tap A verbatim and untranslated: 「A:先削重复,余额按实测提高」 — the + // standing path of ruling record 6513306068 on #27847. + // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(10391 / 4) + ['skills/objectstack-automation/references/integration-recipes.md', 2598], // 5569 -> 5587 (card #26298, audit #26184 row A2): the approval config table // gains `excludeSubmitter` (refused beside `onEmptyApprovers: 'auto_approve'`), // +105 tokens. Paid down first: Best Practices 1 and 4 restated the Branching diff --git a/skills/objectstack-automation/references/integration-recipes.md b/skills/objectstack-automation/references/integration-recipes.md index 9e48f90b02b..ea96a04927a 100644 --- a/skills/objectstack-automation/references/integration-recipes.md +++ b/skills/objectstack-automation/references/integration-recipes.md @@ -159,20 +159,25 @@ Recipe 1's shape with a grant-bearing `auth`, served on `rest`, `openapi` and token is fetched per dispatch; no metadata key holds it. ```ts -// A user grants access once (PKCE always on); the ref is the client secret, omitted for a public client. +// PKCE always on; the ref is the client secret, omitted for a public client. auth: { type: 'oauth2', grant: 'authorization_code', authorizationUrl: 'https://account.docusign.com/oauth/auth', tokenUrl: 'https://account.docusign.com/oauth/token', scopes: ['signature'], clientId: 'objectstack-prod', credentialRef: 'DOCUSIGN_CLIENT_SECRET' } +// Or each deployment's own client: clientSource: 'installer' in place of clientId + credentialRef. // Server to server: grant: 'client_credentials' with tokenUrl, clientId, credentialRef (`resource` optional). // Vendor app id + app secret (`vendor`: `feishu` / `dingtalk` / `wecom`): auth: { type: 'app-credential', vendor: 'feishu', appId: 'cli_a1b2', credentialRef: 'FEISHU_APP_SECRET' } ``` -`client_credentials` and `app-credential` dispatch once the ref is set. `authorization_code` is degraded "not connected" until an administrator holding `manage_connector_connections` connects it, once per organization, from a signed-in session (an agent or API key is refused): `POST /api/v1/automation/connectors/:name/connection` answers `{ authorizationUrl }`; the vendor redirects to `GET …/connectors/oauth/callback`. An `authorization_code` instance on `mcp` may omit the `https://` endpoints and `clientId`, discovered at connect. +Name `clientId` for an app its publisher deploys; `clientSource: 'installer'` for one installed +on deployments the publisher never sees: the administrator registers a client with the vendor +against the `redirectUri` a `GET` on that path answers, then connects with `{ clientId, clientSecret }` +(no secret for a public client; a reconnect naming none keeps it). The connection keeps it +encrypted; a read shows `client: { source: 'installer', clientId, hasSecret }`. Still never park a short-lived token behind a `bearer` ref. From fd3cf40c354c21d1422a6e1e45de1cde19af7335 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 12:37:49 +0000 Subject: [PATCH 2/4] docs(skills/automation): name the status read's path in the installer sentence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "a GET on that path" could read as the callback path named just before; spell out GET …/:name/connection. The ceiling row follows the measurement (10391 -> 10402 bytes, 2598 -> 2601 tokens). Claude-Session: https://claude.ai/code/session_01J8VVAnmBmMPE4eUyJhJRfx Co-authored-by: Claude --- scripts/check-skills-token-ratchet.mjs | 12 ++++++------ .../references/integration-recipes.md | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/check-skills-token-ratchet.mjs b/scripts/check-skills-token-ratchet.mjs index 7e50dcd8172..0a62a968685 100644 --- a/scripts/check-skills-token-ratchet.mjs +++ b/scripts/check-skills-token-ratchet.mjs @@ -957,7 +957,7 @@ export const CEILINGS = new Map([ // and untranslated: 「A:先削重复,余额按实测提高」 — the standing path of // ruling record 6513306068 on #27847. // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(9949 / 4) - // 9926 -> 10391 (card #29255; the file had shrunk 23 bytes under the row + // 9926 -> 10402 (card #29255; the file had shrunk 23 bytes under the row // since, headroom 6 tokens): recipe 6 teaches `clientSource: 'installer'` // beside the declared client — when to use which, the administrator's // `{ clientId, clientSecret }` connect body against the status read's @@ -968,14 +968,14 @@ export const CEILINGS = new Map([ // once" (the paragraph under the block says an administrator connects it, // once per organization), and "`client_credentials` and `app-credential` // dispatch once the ref is set." (the file's opening paragraph says an unset - // ref leaves its instance degraded until set). Arithmetic: 9926 -> 10391 - // (+465 net: -100 cut, +565 added) -> ceil(10391 / 4) = 2598; the ceiling - // moves +110, pinned AT the measurement. Maintainer ruling 2026-10-27 on the + // ref leaves its instance degraded until set). Arithmetic: 9926 -> 10402 + // (+476 net: -100 cut, +576 added) -> ceil(10402 / 4) = 2601; the ceiling + // moves +113, pinned AT the measurement. Maintainer ruling 2026-10-27 on the // seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」, // tap A verbatim and untranslated: 「A:先削重复,余额按实测提高」 — the // standing path of ruling record 6513306068 on #27847. - // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(10391 / 4) - ['skills/objectstack-automation/references/integration-recipes.md', 2598], + // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(10402 / 4) + ['skills/objectstack-automation/references/integration-recipes.md', 2601], // 5569 -> 5587 (card #26298, audit #26184 row A2): the approval config table // gains `excludeSubmitter` (refused beside `onEmptyApprovers: 'auto_approve'`), // +105 tokens. Paid down first: Best Practices 1 and 4 restated the Branching diff --git a/skills/objectstack-automation/references/integration-recipes.md b/skills/objectstack-automation/references/integration-recipes.md index ea96a04927a..daf8b55bdec 100644 --- a/skills/objectstack-automation/references/integration-recipes.md +++ b/skills/objectstack-automation/references/integration-recipes.md @@ -177,7 +177,7 @@ the vendor redirects to `GET …/connectors/oauth/callback`. An `authorization_c instance on `mcp` may omit the `https://` endpoints and `clientId`, discovered at connect. Name `clientId` for an app its publisher deploys; `clientSource: 'installer'` for one installed on deployments the publisher never sees: the administrator registers a client with the vendor -against the `redirectUri` a `GET` on that path answers, then connects with `{ clientId, clientSecret }` +against the `redirectUri` that `GET …/:name/connection` answers, then connects with `{ clientId, clientSecret }` (no secret for a public client; a reconnect naming none keeps it). The connection keeps it encrypted; a read shows `client: { source: 'installer', clientId, hasSecret }`. Still never park a short-lived token behind a `bearer` ref. From 7c65d0da3ef0bcc072c1b7d300f4329446565cb5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 14:28:34 +0000 Subject: [PATCH 3/4] docs(skills/automation): reconnect clause names the client; add the skills changeset Contract review round: "a reconnect naming none keeps it" could read as "omit the secret and the kept secret stays", but a reconnect that names any client replaces the stored one whole. It now says "naming no client". The bytes are paid inside the new text: the code comment's "each deployment's own client" restated the when-to-use sentence below the block. The ceiling row follows the measurement (10377 bytes, 2595). skills/** ships in @objectstack/skills, so the change carries a patch changeset with both declaration lines instead of the skip label. Claude-Session: https://claude.ai/code/session_01J8VVAnmBmMPE4eUyJhJRfx Co-authored-by: Claude --- ...255-automation-skill-installer-oauth-client.md | 10 ++++++++++ scripts/check-skills-token-ratchet.mjs | 15 +++++++++------ .../references/integration-recipes.md | 4 ++-- 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 .changeset/29255-automation-skill-installer-oauth-client.md diff --git a/.changeset/29255-automation-skill-installer-oauth-client.md b/.changeset/29255-automation-skill-installer-oauth-client.md new file mode 100644 index 00000000000..dd942483594 --- /dev/null +++ b/.changeset/29255-automation-skill-installer-oauth-client.md @@ -0,0 +1,10 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): the automation skill's OAuth recipe teaches the installer-supplied client + +Clause-②: no +Metadata-protocol: none + +Recipe 6 of `objectstack-automation/references/integration-recipes.md` now shows `clientSource: 'installer'` beside a declared `clientId` + `credentialRef`, with when to use which: a declared client for an app its publisher deploys, the installer form for one installed on deployments the publisher never sees. The administrator registers a client with the vendor against the `redirectUri` the connection's status read answers, connects with `{ clientId, clientSecret }` (no secret for a public client; a reconnect naming no client keeps the stored one), and the connection reads back `client: { source: 'installer', clientId, hasSecret }`, never the secret. diff --git a/scripts/check-skills-token-ratchet.mjs b/scripts/check-skills-token-ratchet.mjs index 0a62a968685..2d2a9b19146 100644 --- a/scripts/check-skills-token-ratchet.mjs +++ b/scripts/check-skills-token-ratchet.mjs @@ -957,7 +957,7 @@ export const CEILINGS = new Map([ // and untranslated: 「A:先削重复,余额按实测提高」 — the standing path of // ruling record 6513306068 on #27847. // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(9949 / 4) - // 9926 -> 10402 (card #29255; the file had shrunk 23 bytes under the row + // 9926 -> 10377 (card #29255; the file had shrunk 23 bytes under the row // since, headroom 6 tokens): recipe 6 teaches `clientSource: 'installer'` // beside the declared client — when to use which, the administrator's // `{ clientId, clientSecret }` connect body against the status read's @@ -968,14 +968,17 @@ export const CEILINGS = new Map([ // once" (the paragraph under the block says an administrator connects it, // once per organization), and "`client_credentials` and `app-credential` // dispatch once the ref is set." (the file's opening paragraph says an unset - // ref leaves its instance degraded until set). Arithmetic: 9926 -> 10402 - // (+476 net: -100 cut, +576 added) -> ceil(10402 / 4) = 2601; the ceiling - // moves +113, pinned AT the measurement. Maintainer ruling 2026-10-27 on the + // ref leaves its instance degraded until set). The contract-review round + // reworded the reconnect clause ("naming no client") and paid for it inside + // the new text: the code comment's "each deployment's own client" restated + // the when-to-use sentence below the block. Arithmetic: 9926 -> 10377 + // (+451 net: -100 cut, +551 added) -> ceil(10377 / 4) = 2595; the ceiling + // moves +107, pinned AT the measurement. Maintainer ruling 2026-10-27 on the // seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」, // tap A verbatim and untranslated: 「A:先削重复,余额按实测提高」 — the // standing path of ruling record 6513306068 on #27847. - // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(10402 / 4) - ['skills/objectstack-automation/references/integration-recipes.md', 2601], + // ceil($(wc -c < skills/objectstack-automation/references/integration-recipes.md) / 4) == ceil(10377 / 4) + ['skills/objectstack-automation/references/integration-recipes.md', 2595], // 5569 -> 5587 (card #26298, audit #26184 row A2): the approval config table // gains `excludeSubmitter` (refused beside `onEmptyApprovers: 'auto_approve'`), // +105 tokens. Paid down first: Best Practices 1 and 4 restated the Branching diff --git a/skills/objectstack-automation/references/integration-recipes.md b/skills/objectstack-automation/references/integration-recipes.md index daf8b55bdec..a95df3173b0 100644 --- a/skills/objectstack-automation/references/integration-recipes.md +++ b/skills/objectstack-automation/references/integration-recipes.md @@ -163,7 +163,7 @@ token is fetched per dispatch; no metadata key holds it. auth: { type: 'oauth2', grant: 'authorization_code', authorizationUrl: 'https://account.docusign.com/oauth/auth', tokenUrl: 'https://account.docusign.com/oauth/token', scopes: ['signature'], clientId: 'objectstack-prod', credentialRef: 'DOCUSIGN_CLIENT_SECRET' } -// Or each deployment's own client: clientSource: 'installer' in place of clientId + credentialRef. +// Or clientSource: 'installer' in place of clientId + credentialRef. // Server to server: grant: 'client_credentials' with tokenUrl, clientId, credentialRef (`resource` optional). // Vendor app id + app secret (`vendor`: `feishu` / `dingtalk` / `wecom`): auth: { type: 'app-credential', vendor: 'feishu', appId: 'cli_a1b2', credentialRef: 'FEISHU_APP_SECRET' } @@ -178,6 +178,6 @@ instance on `mcp` may omit the `https://` endpoints and `clientId`, discovered a Name `clientId` for an app its publisher deploys; `clientSource: 'installer'` for one installed on deployments the publisher never sees: the administrator registers a client with the vendor against the `redirectUri` that `GET …/:name/connection` answers, then connects with `{ clientId, clientSecret }` -(no secret for a public client; a reconnect naming none keeps it). The connection keeps it +(no secret for a public client; a reconnect naming no client keeps it). The connection keeps it encrypted; a read shows `client: { source: 'installer', clientId, hasSecret }`. Still never park a short-lived token behind a `bearer` ref. From f77be0641c23c0ed6570396b5299301f2be76b20 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 14:30:04 +0000 Subject: [PATCH 4/4] docs(skills/automation): name the changeset file as the seat ordered Claude-Session: https://claude.ai/code/session_01J8VVAnmBmMPE4eUyJhJRfx Co-authored-by: Claude --- ...ler-oauth-client.md => 29255-installer-client-source-skill.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changeset/{29255-automation-skill-installer-oauth-client.md => 29255-installer-client-source-skill.md} (100%) diff --git a/.changeset/29255-automation-skill-installer-oauth-client.md b/.changeset/29255-installer-client-source-skill.md similarity index 100% rename from .changeset/29255-automation-skill-installer-oauth-client.md rename to .changeset/29255-installer-client-source-skill.md