Repository navigation
Commit b59bf60
fix(service-automation): a dispatch to a held connector instance names its fix, not a retry (#28037)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DUGZNCw4uuwpe5WcHJJSY8f)_
domain:server#3 · os-elon-musk · session_016WV4cw8ZC8W9Jeq9E7gY3X
Fixes #28016
Clause-②: no
Metadata-protocol: none
Skills-docs: none — `git grep -n -i -e "retries automatically" -e
"retried with backoff" -- skills` finds nothing, and `git grep -n -i
degraded -- skills` finds one line
(`skills/objectstack-automation/references/integration-recipes.md:9`, an
unset ref leaves its instance degraded until set), which stays true. The
docs row this card owes is in this PR.
`docs/adr/0097-declarative-connector-instances.md:190` still quotes "the
platform retries automatically" for every husk; it is governed and
outside this card's surface (see Acceptance notes).
## 维护者速读
- 改了什么:连接器实例「被挂起」(重试时遇到配置错误,或上游拒绝了已存储的凭据)时,`connector_action`
派发的拒绝信息不再说「平台会自动重试」,而是写明怎么恢复:编辑声明,或重新设置凭据
`<ref>`(或重启)。真正在退避重试的实例仍说会自动重试。
- 为什么改:#28014 / #27237 之后这类实例根本不会被重试,原来那句话是假的,作者会一直等。
- 风险与回滚:只改拒绝文案和一条引擎 warn 日志;引擎的降级记录多一个可选的 `degradedHold` 字段,新增只读方法
`getConnectorDegradedHold`,`registerDegradedConnector`
多一个可选第五参数,原有调用不变。回滚即还原本 PR。
- 先看:`builtin/connector-nodes.ts`(拒绝文案)、`plugin.ts` 两处挂起点传入
hold、`connector-held-dispatch-refusal.test.ts`。
- 两分钟验证:`pnpm --filter @objectstack/service-automation exec vitest run
src/connector-held-dispatch-refusal.test.ts
src/connector-credential-door.test.ts`,预期 30/30 通过。
- 需要你做的:无,席位落地。
## What changes
- **The degraded record carries the hold.** `RegisteredConnector` gains
an optional `degradedHold: ConnectorHold`, `{ kind: 'declaration' }` or
`{ kind: 'credential', ref }`. `registerDegradedConnector` takes it as
an optional fifth argument (engine delegate and the body in
`plugin-extension-api.ts`), and
`AutomationEngine.getConnectorDegradedHold(name)` reads it. A later
degrade of the same name replaces the record, so an instance that goes
back on the backoff loses the hold.
- **`plugin.ts` passes it at the two hold sites.**
`failDegradedConnectorInstance` (a configuration fault met on a retry,
#27992) passes `declaration`. `holdConnectorForRejectedCredential`
passes `credential` with the row's `ref` for a stored value, and
`declaration` for an instance with no `credentialRef` (its credential is
written into the declaration, so only an edit changes what it sends). An
environment or vault value stays on the backoff and passes nothing.
- **The refusal (`builtin/connector-nodes.ts`).** Held: `Dispatch is
unavailable until its declaration is edited (or the server restarts);
the platform does not retry it.` or `… until credential '<ref>' is set
again (or the server restarts); …`. On the backoff: unchanged, `… the
platform retries automatically.` A reason that already ends with a full
stop no longer gets a second one (`…does not list.. Dispatch` before).
- **The engine's `Connector registered DEGRADED` warn** says the
materializer does not retry a held instance, and until what. The unheld
wording is byte-identical (pinned in `degraded-register-cause.test.ts`).
- **Docs.** `content/docs/automation/connectors.mdx`'s failure-modes
table gains the row for a configuration fault met on a retry.
- **Changeset.** `@objectstack/service-automation` patch.
## Tests
- New `src/connector-held-dispatch-refusal.test.ts` (4): a configuration
fault met on the backoff retry; a credential written into the
declaration refused by the upstream; control: an unreachable upstream
keeps "retries automatically"; the engine record carries the hold, its
warn says so, and an unheld degrade clears it.
- `src/connector-credential-door.test.ts` (+3, real ObjectQL over
SQLite): a stored value refused at materialization (mcp shape) and on a
call (rest / openapi shape) says to set credential `<ref>` again;
control: an environment value refused on a call keeps "retries
automatically".
## Serial: open PR #27951 (`engine.ts`)
#27951 (seat 4) is still open. The two `engine.ts` regions do not
overlap. This PR's hunks: `@@ -667,8 +667,27 @@`
(`RegisteredConnector`), `@@ -3982,12 +4001,16 @@` and `@@ -3995,7
+4018,7 @@` (the `registerDegradedConnector` delegate), `@@ -4142,6
+4165,17 @@` (the new reader beside `getConnectorDegradedReason`).
#27951's hunks against `main`: `@@ -6373,6 …`, `@@ -6398,8 …`, `@@
-6416,7 …`, `@@ -6594,38 …` (resume paths). Over 2,000 lines apart.
## Acceptance notes
- `docs/adr/0097-declarative-connector-instances.md:190` says a dispatch
to a husk fails with "the platform retries automatically". After this PR
that holds only for an instance on the backoff. The ADR is governed and
outside this card's surface, so it is left as is.
- `ConnectorDescriptor` (`GET /api/v1/automation/connectors`) does not
carry the hold; adding it is a `packages/spec` change, outside this
card. The listing's `degradedReason` for a refused credential already
says it is not retried.
- The additions to the engine's surface (`getConnectorDegradedHold`, the
optional `degradedHold` field and fifth argument, the `ConnectorHold`
type exported from `engine.ts`, not from the package entry) are additive
and optional; no accept set narrows, hence `Clause-②: no` as ordered.
## Verification
All readings at head `824e0f5d`.
<details><summary>Red on main, green with the fix, ablations,
restore</summary>
- On `main`'s `engine.ts`, `plugin-extension-api.ts`, `plugin.ts`,
`builtin/connector-nodes.ts` with the new tests: `Tests 4 failed | 25
passed (29)`. Each held case received `… Dispatch is unavailable until
that is resolved; the platform retries automatically.` (the
configuration case as `…does not list.. Dispatch …`). Both controls
passed.
- With the fix: the five related files
(`connector-held-dispatch-refusal`, `connector-credential-door`,
`connector-retry-configuration-fault`, `connector-materialization`,
`degraded-register-cause`) `Tests 93 passed (93)`.
- Ablation through `scripts/ablation-replace.mjs`, fix committed first,
each restored with blob == HEAD and `git diff HEAD` empty:
1. `builtin/connector-nodes.ts`, `const hold =
engine.getConnectorDegradedHold(cfg.connectorId);` to `undefined`:
anchor 1 to 0, blob 2491245 to 98ae59d65b55, `Tests 4 failed | 26
passed (30)` (the four held dispatch cases; controls green).
2. `plugin.ts`, the credential-hold argument to `undefined`: blob
6a0a751 to 11d034a84acc, `Tests 3 failed | 27 passed (30)`
(declared credential, stored credential x2).
3. `plugin.ts`, `failDegradedConnectorInstance`'s `{ kind: 'declaration'
}` to `undefined`: `Tests 1 failed | 3 passed (4)` (the configuration
case).
</details>
<details><summary>Package suite, typecheck, lint</summary>
- `pnpm --filter @objectstack/service-automation exec vitest run`: `Test
Files 224 passed (224)`, `Tests 2848 passed | 1 expected fail (2849)`.
- `pnpm --filter @objectstack/service-automation run typecheck`: exit 0.
- `pnpm lint`: exit 0.
</details>
<details><summary>dispatch-gates families (92 derived, 92 run, 0
NOT-MEASURED)</summary>
`check-plugin-teardown-shape --self-test` after fetching its pinned
commit; `check:skill-examples` and `check:dual-build-cjs-loads` exited 3
on the closure build and 0 after a whole-tree `turbo run build` (exit
0). `dispatch-gates --ran`: 92 derived families accounted for, 92 run, 0
NOT-MEASURED.
```text
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-adr-links.mjs :: exit 0
node scripts/check-adr-links.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-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.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-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.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-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.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/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: 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/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: 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:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:durability-log-level :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm lint :: exit 0
```
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4cw8ZC8W9Jeq9E7gY3X)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_016WV4cw8ZC8W9Jeq9E7gY3X
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5aee206 commit b59bf60
8 files changed
Lines changed: 334 additions & 14 deletions
File tree
- .changeset
- content/docs/automation
- packages/services/service-automation/src
- builtin
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
454 | 454 | | |
455 | 455 | | |
456 | 456 | | |
| 457 | + | |
457 | 458 | | |
458 | 459 | | |
459 | 460 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
70 | 73 | | |
71 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
72 | 78 | | |
73 | 79 | | |
74 | 80 | | |
75 | | - | |
76 | | - | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
77 | 85 | | |
78 | 86 | | |
79 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
722 | 722 | | |
723 | 723 | | |
724 | 724 | | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 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 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
667 | 667 | | |
668 | 668 | | |
669 | 669 | | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
670 | 675 | | |
671 | 676 | | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
672 | 691 | | |
673 | 692 | | |
674 | 693 | | |
| |||
3982 | 4001 | | |
3983 | 4002 | | |
3984 | 4003 | | |
| 4004 | + | |
| 4005 | + | |
| 4006 | + | |
3985 | 4007 | | |
3986 | 4008 | | |
3987 | 4009 | | |
3988 | 4010 | | |
3989 | 4011 | | |
3990 | 4012 | | |
| 4013 | + | |
3991 | 4014 | | |
3992 | 4015 | | |
3993 | 4016 | | |
3994 | 4017 | | |
3995 | 4018 | | |
3996 | 4019 | | |
3997 | 4020 | | |
3998 | | - | |
| 4021 | + | |
3999 | 4022 | | |
4000 | 4023 | | |
4001 | 4024 | | |
| |||
4142 | 4165 | | |
4143 | 4166 | | |
4144 | 4167 | | |
| 4168 | + | |
| 4169 | + | |
| 4170 | + | |
| 4171 | + | |
| 4172 | + | |
| 4173 | + | |
| 4174 | + | |
| 4175 | + | |
| 4176 | + | |
| 4177 | + | |
| 4178 | + | |
4145 | 4179 | | |
4146 | 4180 | | |
4147 | 4181 | | |
| |||
0 commit comments