diff --git a/.changeset/18975-connector-retry-config-and-request-timeout.md b/.changeset/18975-connector-retry-config-and-request-timeout.md index 9ee705cd49..69123d851a 100644 --- a/.changeset/18975-connector-retry-config-and-request-timeout.md +++ b/.changeset/18975-connector-retry-config-and-request-timeout.md @@ -28,7 +28,7 @@ declared policy onto those options — one execution site, not one per connector package. **How the authored value gets there.** `ConnectorProviderContext` gains -`retryConfig`, `connectionTimeoutMs` and `requestTimeoutMs`, read-only and +`retryConfig` and `requestTimeoutMs`, read-only and resolved from the entry (the automation service parses `retryConfig` so a factory reads real values instead of re-deriving the schema's defaults), so a custom provider that does its own I/O can honour them. The built-in HTTP @@ -56,19 +56,20 @@ policy could never reach. They now go through the same wrapper as `connector-rest` and `connector-slack`, which gives them the 30s per-attempt timeout and bounded retry those two already had. -**⚠️ `connectionTimeoutMs` is NOT enforced, deliberately, and is the one thing +**⚠️ `connectionTimeoutMs` is NOT made live, deliberately, and is the one thing the ruling assumed that measurement refused.** A connector's call is a WHATWG `fetch`, whose only cancellation surface is one `AbortSignal` over the whole operation; nothing in that interface observes the connection phase separately. Bounding time-to-response with it would kill a slow-but-connected upstream the author meant to allow with a large `requestTimeoutMs` — breaking the very -promise the key makes. So it is carried onto `ConnectorProviderContext` (a -custom provider on a transport that *can* separate the phases may honour it) -and left unenforced by the platform, with the reason recorded at the mapping and -in `packages/spec/liveness/connector.json`, which keeps that one row `dead`. It -is owed a second, narrower ADR-0049 decision: retire it, or re-describe it as -something the platform can enforce. +promise the key makes. So this change leaves it unenforced, with the reason +recorded at the mapping and in `packages/spec/liveness/connector.json`, whose +row for it stays `dead`. That left it owed a second, narrower ADR-0049 +decision, and this same release takes it: `connector.connectionTimeoutMs` is +**retired**, and its own entry in this release says what to write instead. The +key never reaches `ConnectorProviderContext` in any release. -Nine of the ten ledger rows flip `dead` → `live` with the consumer site named. -No declaration moves: the connector schema keeps every key, every bound and -every default it had. +Nine of the ten ledger rows flip `dead` → `live` with the consumer site named; +the tenth is `connectionTimeoutMs`, above. This change itself moves no +declaration: it leaves every key, every bound and every default on the +connector schema as it found them. diff --git a/.changeset/19580-retire-connector-connection-timeout-ms.md b/.changeset/19580-retire-connector-connection-timeout-ms.md index 8252183817..da6a3bf441 100644 --- a/.changeset/19580-retire-connector-connection-timeout-ms.md +++ b/.changeset/19580-retire-connector-connection-timeout-ms.md @@ -25,13 +25,20 @@ The key was bounded (`min(1000).max(300000)`), defaulted (`30000`), | removed | what to write instead | | --- | --- | | `connector.connectionTimeoutMs` (on `Connector` and on `DeclarativeConnectorEntry`, so `stack.connectors[]` and `PUT /meta/connector/:name`) | `requestTimeoutMs` — the deadline the platform keeps, applied as `resilientFetch`'s per-attempt timeout. For a connect-only bound, configure it at a connector provider or upstream gateway on a transport that can separate the phases. | -| `ConnectorProviderContext.connectionTimeoutMs` (handed to every `ConnectorProviderFactory`) | `ctx.requestTimeoutMs`, or the factory's own `providerConfig` where the provider owns the vocabulary. | +| `ConnectorProviderContext.connectionTimeoutMs` (handed to every `ConnectorProviderFactory` — added after `@objectstack/spec@17.4.0` and never in a release, see below) | `ctx.requestTimeoutMs`, or the factory's own `providerConfig` where the provider owns the vocabulary. | | The `ZodObject` combinators on `ConnectorSchema` and `DeclarativeConnectorEntrySchema` — `.extend()`, `.omit()`, `.pick()`, `.partial()`, `.merge()`, `.strict()`, `.keyof()`, `.safeExtend()` | Both exports are now `z.preprocess` **pipes** (the residue stage below), so those methods no longer exist on them. **Build on the object and re-wrap:** `acceptRetiredDefaultResidue(, { connectionTimeoutMs: 30000 })`, the `EffectiveObjectPermissionSchema` route. ⚠️ `.superRefine()` still *exists* on a pipe but returns a schema with no read-through `shape`, so refine before wrapping, not after. Parsing, `z.input` / `z.infer`, and the read-through `.shape` are unchanged. | -**The one-line fix: delete the key** — and, for a custom provider factory, stop -reading `ctx.connectionTimeoutMs`. `os migrate meta --from 17` lists the +**The one-line fix: delete the key.** `os migrate meta --from 17` lists the mechanical edits for existing sources; apply them by hand. +The three interface members withdrawn with it were **never in a release**: +`ConnectorProviderContext.connectionTimeoutMs`, +`RestConnectorOptions.connectionTimeoutMs` and +`OpenApiConnectorConfig.connectionTimeoutMs` all entered with `b929e0a662`, +after the `@objectstack/*@17.4.0` tag, and leave in this same release. A factory +or caller built against a released version never saw them; only code written +against an unreleased `main` in between can read them, and it stops. + ⚠️ Runtime behaviour is **unchanged for every shipped provider**, because none ever applied the value: a connector that authored `connectionTimeoutMs: 1000` made exactly the same calls, with exactly the same deadlines, as one that did @@ -41,7 +48,8 @@ not. What does change is observable and intended: the def served by ### ⭐ This is NOT the zero-mention retirement shape Measured with `git grep -n connectionTimeoutMs SHA -- . ':!packages/spec'` at -`origin/main`: **thirteen** non-test source occurrences over seven files in five +`e07843b5a6`, the tree this retirement landed on: **thirteen** non-test source +occurrences over seven files in five packages — **six reads** (`openapi-connector.ts:242`, `openapi-provider.ts:193`, `rest-connector.ts:134`, `rest-provider.ts:64`, `plugin.ts:307`, `plugin.ts:1589`), **four type declarations**, and **three** surviving hardcoded @@ -73,7 +81,8 @@ ruling that made the siblings live forbids.) both channels — `tsc` (input type `never`) and the parse, which raises the prescription itself. `DeclarativeConnectorEntrySchema` carries it too — both published carriers wrap the same private `ConnectorBaseSchema` — so - `stack.connectors[]` and the `/meta/connector` door refuse it too. + `stack.connectors[]` and the `/meta/connector` door refuse it too: every value + but the retired default `30000`, which the residue stage below strips first. - **A D2 conversion, `connector-connection-timeout-ms-removed`** — one strip per `connectors[]` entry, a pure lossless delete. ⭐ The ruling left whether one was owed to be **measured** ("a D2 conversion only if a stored connector row can @@ -85,9 +94,11 @@ ruling that made the siblings live forbids.) written on 17.x therefore replay clean. - **A D3 semantic entry, `connector-provider-context-connection-timeout-ms-retired`**, for the withdrawn - `ConnectorProviderContext` member. A provider factory is code: there is no - authored source and no `sys_metadata` row for a conversion to rewrite, so the - removal reaches a factory author as a `tsc` error and as that entry. + `ConnectorProviderContext` member (never in a release, above). A provider + factory is code: there is no authored source and no `sys_metadata` row for a + conversion to rewrite, so the removal reaches a factory author who read it — + possible only against an unreleased `main` — as a `tsc` error and as that + entry. - **No def leaves.** The key was a bare `z.number()`, never a `ConfigSchema` shape, so `RETIRED_DEFS_BY_MAJOR[18]` gains nothing — and `api-surface/` and `json-schema.manifest/` are byte-identical, which is the correct reading for a @@ -103,19 +114,26 @@ ruling that made the siblings live forbids.) carries both readings with their trees rather than one undated claim. - **An `acceptRetiredDefaultResidue` stage** (#12840), `{ connectionTimeoutMs: 30000 }` on both carriers. The key was `.optional().default(30000)`, so a 17.x parse - materialized it into **every** connector — measured across two builds: the base - build emits it for an entry that authored only `name`/`label`/`type`, and the - tombstoned build refuses that exact object at `connectors.0.connectionTimeoutMs`. + materialized it into **every** connector — measured on both sides of the + retirement: the released + `@objectstack/spec@17.4.0` emits `connectionTimeoutMs: 30000` for an entry that + authored only `name`/`label`/`type`, and the tombstone **without the stage** + refuses that exact object at `connectionTimeoutMs`. With the stage, as it + ships, that object is **accepted and the key stripped** before the tombstone + reads it — on `ConnectorSchema`, `DeclarativeConnectorEntrySchema`, the + `/meta/connector` schema and `stack.connectors[]` alike. The D2 does **not** discharge the obligation, and the precedent shows it: `ObjectPermission:allowPurge` carries a D2 **and** the residue stage, for its own reason (a released toolchain materialized its default into every built artifact's entries). The reason *here* is a different one — this schema has a second door: `AutomationEngine.registerConnector` parses `ConnectorSchema` for a def a plugin or provider factory builds **in code**, where no conversion - ever runs, and all four shipped connector packages put the materialized value - straight into that def literal. So the emitted `30000` - is accepted-and-stripped while `15000` keeps the tombstone's refusal, and - nothing is un-retired: `z.input` stays `never` and the `[RETIRED]` row stays. + ever runs, and in 17.4.0 all four shipped connector packages put that `30000` + straight into the def literal. So the emitted `30000` is accepted-and-stripped, + while every other value (`15000`, `1000`, the string `"30000"`) keeps the + tombstone's refusal — at `connectionTimeoutMs`, or at + `connectors.0.connectionTimeoutMs` inside a stack — and nothing is un-retired: + `z.input` stays `never` and the `[RETIRED]` row stays. - **No deprecation window** (maintainer 2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进」), and no staged retirement. @@ -126,8 +144,7 @@ occurrences of the name at objectui `87af769e`, against a lit control on the sam command and scope, so no sibling fix or pin bump rides with this. `Clause-②: yes (narrowing)` — a published authorable key is removed on two -carriers and a published interface member leaves `ConnectorProviderContext`, so -the accept set a consumer writes against narrows. Nothing is widened and nothing -is renamed. Contract-review tier. +carriers, so the accept set a consumer writes against narrows. Nothing is +widened and nothing is renamed. Contract-review tier.