Repository navigation
Commit 07450b5
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XUSbzmMRfCQ13hL4Jffkmqp)_
Fixes #28863
Clause-②: no
Metadata-protocol: none
Skills-docs: none — no file under `skills/**` teaches the connection's
`reconnect_required` causes (0 hits for `reconnect_required`, `reconnect
required` and `refused the grant`).
## 维护者速读
- **改了什么**:连接器连接变成「需要重新连接」有四种原因:授权服务器在刷新时拒绝授权;它没发 refresh
token;连接没存可用来刷新的 client;MCP 工具发现时上游拒绝了令牌。存下的
`status_reason`、`sys_connector_connection.status`
字段说明、`markReconnectRequired` 注释和 `connectors.mdx`
以前都只说第一种。现在三处文字列出全部四种;「没有 refresh token」和「没有
client」两条路径存下的原因各自说真正发生的事,不再先声称服务器拒绝,也不再说访问令牌「已过期」(缓存未命中或调度 401
也会触发刷新,令牌可能仍有效)。
- **为什么改**:管理员读到的原因说服务器拒绝了授权,可服务器根本没被问过(#28614 开发者发现,#28862 合同审查补充)。
- **风险与回滚**:低。只改文字和两条路径存储的原因句;拒绝路径的句子一字不变;状态值、错误码、写入都不变。回滚即 revert。
-
**先看**:`packages/services/service-automation/src/oauth2-grant/grant-handle.ts`
的 `reconnectRequiredReason` 与 `refreshOnce`;同目录 `grant-handle.test.ts`
末尾的 `[#28863]` 三条。
- **两分钟验证**:`pnpm --filter @objectstack/service-automation exec vitest
run src/oauth2-grant/grant-handle.test.ts`,预期 `Tests 22 passed
(22)`(main 上新加的 2 条红,对照两边都绿)。
- **需要你做的**:无,席位落地。spec 里同样的「expired」措辞(下面 Acceptance notes 点了行号)由席位转给
spec 车道。
## What changes
- **`reconnectRequiredReason(connector, organizationId, cause?)`**
(`oauth2-grant/grant-handle.ts`) takes an optional
`ReconnectRequiredCause`: `'refused'` (the default, so every existing
caller keeps the old sentence byte for byte), `'no_refresh_token'` or
`'no_client'`. The sentence's cause clause is the one that happened:
- refused: "its authorization server refused the grant (revoked, or
expired with no usable refresh)" (unchanged);
- no refresh token: "its authorization server issued no refresh token,
so an access token the connection has to renew cannot be renewed";
- no client: "its connection keeps no client to refresh with, so an
access token it has to renew cannot be renewed".
- **`refreshOnce`** passes each path's cause to `grantRefused`, so the
stored `status_reason` and the dispatch's 503 message are that one
sentence. Before, the two non-refusal paths stored the refusal sentence
with the real cause appended, and the no-refresh-token one said "an
expired access token".
- **Prose, all four causes, in PR #28862's words:** the `status` field
description in `sys-connector-connection.object.ts` (it lives in
`service-automation`, not `platform-objects` as the order's path
guessed), the `ConnectorConnectionStore#markReconnectRequired` docblock,
and `content/docs/automation/connectors.mdx` (the paragraph at L689 and
the boot-behaviour table row at L868). Each points at `status_reason` /
`statusReason` for which cause happened.
- **Changeset** `.changeset/28863-reconnect-required-cause.md`: `patch`
on `@objectstack/service-automation`, both declaration lines. The docs
page ships in no package.
- No new error code, no new status value, no new gate, no
`packages/spec/**`.
**Why `Clause-②: no`.** No accept set, status value, code or export
changes meaning. What changes is prose and the reason text two refresh
paths store and answer with (the 503 stays a 503 `SERVICE_UNAVAILABLE`).
## Tests
Three new cases at the end of
`packages/services/service-automation/src/oauth2-grant/grant-handle.test.ts`
(`[#28863]`), on the file's in-memory connection store, each reading the
row's stored `statusReason` and the dispatch refusal:
- **No refresh token:** the reason names the missing refresh token and
"an access token the connection has to renew", contains neither
`refused` nor `expired`, equals the 503 message, and no token request is
made.
- **No client** (an instance that declares no client and a connection
that keeps none): the reason names the missing client, contains neither
`refused` nor `expired`, equals the 503 message, and no token request is
made.
- **CONTROL, refused** (`invalid_grant`): the reason still names the
refusal, carries no token, and equals the 503 message.
<details><summary>Red on base, green on head</summary>
- **Base** (`grant-handle.ts` from `5e1b9b0a`, the new tests at head):
`Tests 2 failed | 20 passed (22)`. The two reds: `expected 'connector
instance 'crm' needs to b…' to contain 'issued no refresh token, so an
access…'` and the same for `'keeps no client to refresh with, so a…'`.
The refusal control is green both ways. Restored with `git checkout HEAD
-- grant-handle.ts`, `git diff HEAD` empty.
- **Head:** `Tests 22 passed (22)`.
</details>
## Acceptance notes
- **The spec half of item 5 is not in this PR** (ruling 4).
`packages/spec/src/contracts/automation-service.ts` L1145–1146, the
`ConnectorConnectionState.status` JSDoc from PR #28862: "the connection
keeps nothing to renew an / expired access token with". "expired" there
is narrower than the code, for the same reason as above. The runtime
wording here, "an access token it has to renew", is the proposed
replacement, so the two can agree.
- **Who matched the old text.** `grant-handle.test.ts` L322 matches
`issued no refresh token`, which the new sentence keeps, so it stays
green. `connector-connections.test.ts` L148–157 passes its own reason
string to the store. `connector-connection-door.test.ts` L576 matches
the plugin's `onGrantRefused` log line ("…refused the grant of
organization…"), which this PR does not touch. No other consumer reads
the reason text (searched objectstack, objectui and hotcrm for `no
usable refresh`, `refused the grant`, `expired access token` and `issued
no refresh token`).
- **Left as they are, the same class (noted, not filed):**
- the plugin's `onGrantRefused` warn line (`plugin.ts` ≈L3697) says "the
authorization server refused the grant" on all three refresh paths;
- the dispatch refusal for a row ALREADY marked (`select` in
`grant-handle.ts` ≈L238, `refreshOnce`'s first check, and the plugin's
hold-reason fallback ≈L3189) uses the default refused sentence whatever
cause the row stores. Answering with the row's own `statusReason` would
make those honest too.
## Verification
All readings at head `7cf0294a` (the change `790e473c`, merged once with
`origin/main`, a clean merge). Full `pnpm turbo run build
--concurrency=2`: 118/118.
<details><summary>Package suites, typecheck, lint</summary>
- `pnpm --filter @objectstack/service-automation test`: `Test Files 255
passed (255)`, `Tests 3284 passed (3284)`, lock VERDICT command-exit 0.
Its `typecheck` (tsc + test-typecheck): exit 0.
- `pnpm --filter @objectstack/platform-objects test`: `Test Files 108
passed (108)`, `Tests 1503 passed (1503)`, command-exit 0. Its
`typecheck`: exit 0. (No file of it changed; run as the order asks.)
- The repo's `pnpm lint` command scoped to
`packages/services/service-automation`: exit 0.
</details>
<details><summary>Derived gates (`dispatch-gates.mjs --commands`, 89
families)</summary>
- All 89 exit 0 at `7cf0294a`, the docs families included
(`check-affected-docs`, `check-drift-comment`, `check:docs-*`,
`check:corpus-claim-drift`); `dispatch-gates --ran`: `89 derived, 89
run, 0 NOT-MEASURED, 0 UNRUN`. Record:
`pm-orders/server/28863-gates-ran.txt`.
- `check:type-check-debt`: OK, `--re-measure: OK`.
- Repo-wide `pnpm lint` and the CI-only families are CI's; see the PR's
check-runs.
</details>
Dogfood battery: not owed (no `!`, no narrowing arm) and not run.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5mTYb1BTJNRqf3bdJ8)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent fed3a51 commit 07450b5
6 files changed
Lines changed: 116 additions & 20 deletions
File tree
- .changeset
- content/docs/automation
- packages/services/service-automation/src
- oauth2-grant
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
686 | 686 | | |
687 | 687 | | |
688 | 688 | | |
689 | | - | |
690 | | - | |
691 | | - | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
692 | 696 | | |
693 | 697 | | |
694 | 698 | | |
| |||
865 | 869 | | |
866 | 870 | | |
867 | 871 | | |
868 | | - | |
| 872 | + | |
869 | 873 | | |
870 | 874 | | |
871 | 875 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
329 | | - | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
330 | 334 | | |
331 | 335 | | |
332 | 336 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
413 | 413 | | |
414 | 414 | | |
415 | 415 | | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
127 | 156 | | |
128 | 157 | | |
129 | 158 | | |
| |||
266 | 295 | | |
267 | 296 | | |
268 | 297 | | |
269 | | - | |
270 | | - | |
| 298 | + | |
271 | 299 | | |
272 | 300 | | |
273 | 301 | | |
274 | | - | |
275 | | - | |
| 302 | + | |
276 | 303 | | |
277 | 304 | | |
278 | 305 | | |
279 | 306 | | |
280 | 307 | | |
281 | 308 | | |
282 | 309 | | |
283 | | - | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
284 | 313 | | |
285 | 314 | | |
286 | 315 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| |||
0 commit comments