Skip to content

Commit b146102

Browse files
os-billclaude
andauthored
docs(spec): the connector header no longer teaches retryConfig as the rate-limit remedy (#19040)
Fixes #18983 Clause-②: no ## What moves One sentence of the connector header TSDoc in `packages/spec/src/integration/connector.zod.ts`, plus the reference page `gen:docs` renders from it. **Prose only.** No schema, declaration, default or accept set moves, and the fate of these keys stays ADR-0049's to rule on rather than being prejudged here. The header ended its "no outbound rate limiting" paragraph by naming a remedy: *"What L3 does declare for a rate-limited upstream is `retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502, 503, 504]` includes `429` — and `health.circuitBreaker`."* PR #18979 retired that same claim from `packages/spec/docs/SYNC_ARCHITECTURE.md`; this file is where it was authored, and the generated page carried it downstream. The replacement carries the wording #18979 landed: both keys are **declared but currently unimplemented**, with a pointer to the liveness ledger, and explicitly neither *retired* nor *left to the host*. ## Why the sentence was false — re-measured on this branch, not inherited `packages/spec/liveness/connector.json`, read at `ed63e0d39c`: | ledger rows | status | verifiedAt | |:---|:---|:---| | `retryConfig.*` — all 8 sub-keys | `dead` | 2026-09-17 | | `health.circuitBreaker.*` — all 7 rows | `dead` | 2026-09-17 | | `health.healthCheck.*` — all 8 rows | `dead` | 2026-09-17 | | **`providerConfig` — the must-answer live control, same read** | **`live`** | 2026-09-17 | A `dead` column with no live row beside it would only show the instrument answering one way, so the control is part of the reading. The "not left to the host" half is re-verified here too, at source rather than cited: `ConnectorProviderContext` (`packages/spec/src/integration/connector-provider.ts:57`) declares exactly `name`, `label`, `description`, `icon`, `type`, `providerConfig`, `auth` and `loadPackageFile` — eight members, none of them `retryConfig` or `health`. A provider factory is never handed either key, so it has no way to honour one. ## Regeneration leg `pnpm --filter @objectstack/spec gen:docs` moved exactly one tracked file, `content/docs/references/integration/connector.mdx` (sha256 `640ea9d5…` to `ef0d11fc…`), and `check:docs` is green on the merged tree. The page is not hand-edited. One prediction on the card did not hold, reported as measured: the page's `retryableStatusCodes` occurrence count stays **4**, not lower. One of the four is inside the historical sentence this change quotes; the other three are the generated field tables for `RetryConfigSchema`, which this change does not touch. ## Zero-hit reading, with its radius The corrected sentence spans three comment lines, so a per-line `grep` reads `0` for it while it is plainly there — that reading is blind, not clean. The sweep here strips comment prefixes and collapses newline-bearing whitespace before matching. - **Claim.** The assertive adjacency `upstream gateway.** What L3 does declare` occurs **0** times in the tree at `ed63e0d39c` (10,270 files scanned). - **Radius.** Content layer, over the working tree only; extensions `.ts .tsx .mts .mjs .js .md .mdx .json`; `node_modules`, `dist`, `.git`, `.turbo` and `.cache` pruned. - **A known target that must be outside it.** The blob at `96cf32b075` still holds that exact string. Extracted to a file *inside* the radius the same instrument reads it (2 hits), so the probe is live; scanning the tree it reads 0, because git objects are outside the radius by construction. ## Published-surface reading — per file, which is why this is not `skip-changeset` | file in this diff | on a published `files[]`? | how measured | |:---|:---|:---| | `packages/spec/src/integration/connector.zod.ts` | **yes** | `@objectstack/spec` ships `src/**/*.zod.ts`; `npm pack --dry-run` lists the path among the tarball's 2,039 files | | `content/docs/references/integration/connector.mdx` | no | 0 of the 70 published workspace packages can reach `content/docs/**`; same instrument does see `@objectstack/spec`'s `src/**` entry, so it is not blind | | `.changeset/18983-connector-header-rate-limit-remedy.md` | no | changeset input, consumed at release | The edited file is itself shipped, so the corrected text reaches consumers: this takes a **`patch`** changeset, not `skip-changeset`. The header TSDoc does not reach `dist/`, so `src/**/*.zod.ts` is its only published carrier. ## Verification All of the below ran at `ed63e0d39c`, after `origin/main` was merged in (`packages/spec` had moved on main, so §10's rebuild-and-recheck applies). - **Gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived **100** commands from a non-stale tree. Each ran with its exit code written to disk before any pipe, then reconciled with `--ran`: **100 derived, 99 run green, 0 findings, 1 NOT MEASURED, 0 unrun.** - **NOT MEASURED (1).** `pnpm check:dual-build-cjs-loads` exits **3** — `PREREQUISITE NOT MET`, its own distinct code for "nothing was measured". It reads built output for 26 packages still without a `dist/` (apps, connectors), which needs a whole-workspace build; CI's required `Lint & Repo Gates` job builds everything and runs it. Five other gates refused the same way on first pass and were cleared by building the closures they named, then re-run green: `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples`, `check:docs-transcript-drift`, `check:lean-entry-closure`. - **Tests.** `pnpm --filter @objectstack/spec test` — **489 files, 14,229 tests passed**. - **Typecheck.** `pnpm --filter @objectstack/spec typecheck` green: `tsc --noEmit`, `check:scripts-typecheck`, and `check:test-typecheck` (54 files / 259 errors / 144 pinned signatures held in the shrink-only ledger, unchanged). - **`check:generated`** — all 16 generated artifacts up to date, `check:docs` among them. - **Lint, full population, no narrowing.** `eslint . --no-inline-config` over the whole repo: **6,861 files, 0 errors, 0 warnings**, exit 0. The config enables no type-aware linting for any file (its own comment at `eslint.config.mjs:326` records this with a positive control), so this run is per-file parsing throughout. ## Acceptance notes — measured, deliberately not changed here - The per-field `.describe()` strings on the same dead keys still read as present-indicative behaviour: `'HTTP status codes to retry'`, `'Enable circuit breaker'`, `'Failures before opening circuit'`, `'Add jitter to retry delays'`, and about twenty more across `RetryConfigSchema`, `HealthCheckConfigSchema` and `CircuitBreakerConfigSchema`. They render into this same generated page three times over and into the authorable-surface artifacts an authoring agent reads. Correcting them edits the declaration surface and moves `gen:schema` output, which is outside this card. - The triage seat's escalation condition — a **third** prose source still teaching this remedy — did **not** trigger. Sweeping the tree with the fold-free instrument for `retryConfig` paired with `429` or with `circuitBreaker` returns only: this file, its generated page, the already-corrected `SYNC_ARCHITECTURE.md` passages, the liveness ledger's own notes, a schema parse test, a generated defaults artifact, and an ADR naming-convention list. No third source asserts the remedy. --- _Generated by [Claude Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5ba2ec3 commit b146102

3 files changed

Lines changed: 74 additions & 6 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): the connector header no longer teaches `retryConfig` as the remedy for a rate-limited upstream (#18983)
6+
7+
`packages/spec/src/integration/connector.zod.ts` ships inside this package —
8+
`files[]` carries `src/**/*.zod.ts`, and the file is present in the published
9+
tarball — so its header TSDoc is text consumers read, and the generated
10+
reference page is rendered from it. That header ended its "no outbound rate
11+
limiting" paragraph with "what L3 does declare for a rate-limited upstream is
12+
`retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502, 503,
13+
504]` includes `429` — and `health.circuitBreaker`", which reads as a remedy.
14+
15+
It is not one. `packages/spec/liveness/connector.json` records all eight
16+
`retryConfig` sub-keys and every `health.circuitBreaker` sub-key as `dead`
17+
(verifiedAt 2026-09-17), and outside `packages/spec` nothing reads either: no
18+
retry loop consumes the strategy, the backoff, the jitter or that status-code
19+
list, so the `429` in it never causes a retry, and no breaker ever opens. An
20+
author who followed that sentence wrote configuration that parses, stores, and
21+
is then silently ignored.
22+
23+
The sentence now carries the wording PR #18979 landed for the same claim in
24+
`packages/spec/docs/SYNC_ARCHITECTURE.md`: both keys are **declared but
25+
currently unimplemented**, with a pointer to the liveness ledger, and they are
26+
explicitly neither retired — both are still declared and still parse, so an
27+
author writing them sees no error — nor left to the host, since
28+
`ConnectorProviderContext` carries exactly `name`, `label`, `description`,
29+
`icon`, `type`, `providerConfig`, `auth` and `loadPackageFile`, and a provider
30+
factory is therefore never handed either key.
31+
32+
**Prose only — zero behaviour change.** No schema, declaration, default or
33+
accept set moves, and the keys' fate stays ADR-0049's to rule on rather than
34+
being prejudged here. The generated reference page
35+
`content/docs/references/integration/connector.mdx` follows from `gen:docs`; it
36+
is not published by any package in this workspace.

‎content/docs/references/integration/connector.mdx‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,25 @@ rate-limiting engine ever existed**. The platform's only token bucket (runtime
3838
the calls a connector makes *out*. Do **not** substitute `shared`'s
3939
`RateLimitConfig` — that is the inbound limiter and would cap the wrong direction.
4040
**Until an outbound throttle exists, rate-limit at the connector provider or
41-
upstream gateway.** What L3 does declare for a rate-limited upstream is
42-
`retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502, 503,
43-
504]` includes `429` — and `health.circuitBreaker`. The full removal reasoning is
41+
upstream gateway.** **And do not reach for `retryConfig` instead.** This
42+
paragraph used to end "what L3 does declare for a rate-limited upstream is
43+
`retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502,
44+
503, 504]` includes `429` — and `health.circuitBreaker`", which reads as a
45+
remedy. It is not one: both keys are **declared but currently
46+
unimplemented**. `packages/spec/liveness/connector.json` records every
47+
`retryConfig` sub-key and every `health.circuitBreaker` sub-key as `dead`,
48+
and outside `packages/spec` nothing reads either — no retry loop consumes a
49+
strategy, a backoff, a jitter or that status-code list, so the `429` in it
50+
never causes a retry, and no breaker ever opens. They are **not retired**:
51+
both are still declared and still parse, so an author can write them and see
52+
no error. They are **not left to the host** either —
53+
`ConnectorProviderContext` (`integration/connector-provider.ts`) carries
54+
exactly `name`, `label`, `description`, `icon`, `type`, `providerConfig`,
55+
`auth` and `loadPackageFile`, so a provider factory is never handed either
56+
key and has no way to honour it. ADR-0049 owes these keys a decision
57+
(retire / implement / declare as a host contract); until it rules, the
58+
advice above is the whole advice — retry and throttle **at the connector
59+
provider or upstream gateway**. The full removal reasoning is
4460
recorded at the removal site: the "REMOVED: outbound rate limiting" block in
4561
`integration/connector.zod.ts`, and `packages/spec/docs/SYNC_ARCHITECTURE.md`.
4662

‎packages/spec/src/integration/connector.zod.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,25 @@ import { retiredKey } from '../shared/retired-key';
4141
* the calls a connector makes *out*. Do **not** substitute `shared`'s
4242
* `RateLimitConfig` — that is the inbound limiter and would cap the wrong direction.
4343
* **Until an outbound throttle exists, rate-limit at the connector provider or
44-
* upstream gateway.** What L3 does declare for a rate-limited upstream is
45-
* `retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502, 503,
46-
* 504]` includes `429` — and `health.circuitBreaker`. The full removal reasoning is
44+
* upstream gateway.** **And do not reach for `retryConfig` instead.** This
45+
* paragraph used to end "what L3 does declare for a rate-limited upstream is
46+
* `retryConfig` — whose `retryableStatusCodes` default `[408, 429, 500, 502,
47+
* 503, 504]` includes `429` — and `health.circuitBreaker`", which reads as a
48+
* remedy. It is not one: both keys are **declared but currently
49+
* unimplemented**. `packages/spec/liveness/connector.json` records every
50+
* `retryConfig` sub-key and every `health.circuitBreaker` sub-key as `dead`,
51+
* and outside `packages/spec` nothing reads either — no retry loop consumes a
52+
* strategy, a backoff, a jitter or that status-code list, so the `429` in it
53+
* never causes a retry, and no breaker ever opens. They are **not retired**:
54+
* both are still declared and still parse, so an author can write them and see
55+
* no error. They are **not left to the host** either —
56+
* `ConnectorProviderContext` (`integration/connector-provider.ts`) carries
57+
* exactly `name`, `label`, `description`, `icon`, `type`, `providerConfig`,
58+
* `auth` and `loadPackageFile`, so a provider factory is never handed either
59+
* key and has no way to honour it. ADR-0049 owes these keys a decision
60+
* (retire / implement / declare as a host contract); until it rules, the
61+
* advice above is the whole advice — retry and throttle **at the connector
62+
* provider or upstream gateway**. The full removal reasoning is
4763
* recorded at the removal site: the "REMOVED: outbound rate limiting" block in
4864
* `integration/connector.zod.ts`, and `packages/spec/docs/SYNC_ARCHITECTURE.md`.
4965
*

0 commit comments

Comments
 (0)