docs(spec): SYNC_ARCHITECTURE stops teaching retryConfig as the rate-limit remedy - #18979
Conversation
…e-limit remedy Four passages in `packages/spec/docs/SYNC_ARCHITECTURE.md` taught authors that a rate-limited upstream is answered by `retryConfig` and printed its `retryableStatusCodes` defaults (429 included), while nothing reads the key. They now say what is measurably true today — declared but currently unimplemented — and point at `packages/spec/liveness/connector.json`. Measured on this tree, fold-proof read-shaped probe with a firing control in the same run: `.retryConfig` / `.connectionTimeoutMs` / `.requestTimeoutMs` have 0 read-shaped consumers outside `packages/spec`; the sibling key `providerConfig` has 15 across 9 files. `ConnectorProviderContext` carries none of the three, so a provider factory cannot honour them either. No declaration, schema or accept set is touched: the ADR-0049 ruling on these keys is deliberately not prejudged. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
扩面确认;并认下:我那道栅栏划得太宽派发席( 一、把
|
The `connector.zod.ts` L3 header still taught that `retryConfig` (with its `retryableStatusCodes` `429` default) and `health.circuitBreaker` are what L3 declares for a rate-limited upstream. Both keys parse and store, and nothing reads either: `packages/spec/liveness/connector.json` records every `retryConfig` sub-key and every `health.circuitBreaker` sub-key as `dead`. Replace that one sentence with the wording PR #18979 landed for the same claim in `packages/spec/docs/SYNC_ARCHITECTURE.md`: declared but currently unimplemented, pointing at the liveness ledger, and explicitly neither retired nor left to the host. Prose only; no schema, declaration or accept set moves. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18794
Clause-②: no
What changed
packages/spec/docs/SYNC_ARCHITECTURE.mdtaught authors that a rate-limited upstream is answered byretryConfig, printed itsretryableStatusCodesdefaults (429 included), and listed it as a reason to pick L3 — while nothing reads the key. Five passages in that one file now say what is measurably true today: declared but currently unimplemented, each pointing atpackages/spec/liveness/connector.json.This is option A from the card, and only A. No declaration, schema or accept set is touched, so the ADR-0049 ruling on these keys is deliberately not prejudged. The keys are not described as retired (they are still declared and still parse, so an author writing them still sees no error) and not as the host's job (falsified below).
106717c3aa):157blockquoteretryConfig":299example comment:312example timeoutsconnectionTimeoutMs/requestTimeoutMs:336Best PracticesretryConfighandles the429you get for exceeding a limit":360decision rowretryConfig,health.circuitBreaker"Measurements — re-taken on this branch, not inherited
Consumer probe, fold-proof predicate, firing control in the same run. Comment leaders are stripped first, then every whitespace run (newlines included) is collapsed and glued to the access punctuation, so a folded access cannot hide from it. Read-shaped access only (
x.KEY,x?.KEY,x["KEY"], destructure). 8763 tracked files, tree106717c3aa:Second control, reachability. A bare-identifier census in the same run proves the scan surface reaches the files where these keys actually live:
connectionTimeoutMsoccurs in 9 files outsidepackages/spec(the four connector packages,plugin.ts, a test) andrequestTimeoutMsin 8 — every one of them a WRITE of the literal into a def so it satisfies the post-parse type, never a read. So the zeros read as "no reader", not "the probe never looked".Host seam, re-read verbatim.
packages/spec/src/integration/connector-provider.ts:57declaresConnectorProviderContextwith exactlyname,label,description?,icon?,type,providerConfig,auth?,loadPackageFile?. None of the three keys is among them, so a provider factory is never handed them and has no way to honour them. "Left to the host" is false.Changeset, measured rather than assumed. The trigger is "can a consumer read a change", not "did bytes move".
npm pack --dry-run --json --ignore-scriptsonpackages/spec: 275 entries,docs/SYNC_ARCHITECTURE.mdabsent, and zerodocs/paths at all, while the positive controlsliveness/connector.jsonandsrc/integration/connector.zod.tsare both present.files[]isdist,json-schema,liveness,prompts,llms.txt,README.md,src/**/*.zod.ts,CHANGELOG.md,api-surface,spec-changes.json— nodocsentry. The edited file publishes to nobody, soskip-changeset.Verification
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 48 commands against commitc4c09b853e. All 48 were run, exit codes landed to disk first, then reconciled: "48 derived, 48 run, 0 NOT-MEASURED, 0 UNRUN" — a derived zero, every family carrying a recorded exit code. Four of them (check:dts-closure,check:dual-build-cjs-loads,check:lean-entry-closure,check:sourcemap-no-sources-content) first exited 3 = PREREQUISITE NOT MET, which is neither a pass nor a failure; afterpnpm build(73/73 tasks successful) all four re-ran green.pnpm --filter @objectstack/spec test— 488 test files, 14182 tests, all pass.pnpm --filter @objectstack/spec check:generated— "All 15 generated artifacts are up to date".pnpm --filter @objectstack/spec typecheck— clean.packages/spec/src/integration/connector-author-shape.test.tsextracts the file's typescript-fenced blocks and compiles them verbatim against the real schema, pinning the fence count at 2, the elision-sketch count at 0, and every block to compile clean. 14/14 pass with the edit in place.retryConfigkey toretryConfigBogushad to turn that gate RED on the block I touched. Observed: RED,TS2561 Object literal may only specify known properties— TypeScript's did-you-mean form of the excess-property error, since the bogus name is one edit from the real one — on the assertion "L3 example #0 must compile clean"; 1 failed, 13 passed. Mutation landing was proved on disk (anchor 1 to 0, blobbd3c6b895c2atoc02cf5c9c5e3) and the restore was proved independently of the tool's own claim: blob back tobd3c6b895c2aequals HEAD, andgit diff HEADempty. This is what shows the comments added inside the fence are inside the compiled region and compile clean, rather than sitting outside it.files:selector ineslint.config.mjstargets{ts,tsx,mts,cts,js,jsx,mjs,cjs}, and no markdown selector or processor exists. (2) The count is read from--format json: the one changed file yields"File ignored because no matching configuration was supplied."with 0 errors, so 0 files of the lint population are touched. (3) Invariance: the file is outside the lint population entirely, so this diff cannot move any untouched file's verdict.One widening the reviewer should confirm
The acceptance line for
:360reads "it must not present an unimplemented key as a selection criterion". That row pairedretryConfigwithhealth.circuitBreaker, and the:157blockquote pairs them too.health.circuitBreakeris unimplemented on the same evidence:packages/spec/liveness/connector.jsonrecords every one of its sub-keys asdead, and the same probe run shows 0 read-shaped consumers outsidepackages/specin anypackages/orexamples/file (its only hits are the generated reference page andcontent/docs/references/system/cache.mdx, which is the CACHE's own breaker, a different subject). Correcting only theretryConfighalf would have left the row still presenting an unimplemented key as a selection criterion. So both halves are corrected, citing the ledger's existing verdict rather than making a new one. Flagged because it is one key wider than the card's three.Acceptance notes
Noted, not filed. The first two are real sites carrying the same prescription, each behind a read-only fence this round:
content/docs/automation/flows.mdx(now:1595; the card said:1588) is held by open PR feat(spec,types,triggers)!:groupruns package-authored scheduled work without a declaration, owning each run's writes per record #18420 and is untouched here. Its passage is about reconciling retry-COUNT conventions between block kinds (maxAttemptsincludes the first attempt), not the "useretryConfigfor upstream rate limiting" prescription, so excluding it does not damage the card's thesis. Follow-up: sweep it once PR feat(spec,types,triggers)!:groupruns package-authored scheduled work without a declaration, owning each run's writes per record #18420 lands. Successor: the seat that picks up that sweep.packages/spec/src/integration/connector.zod.ts:44carries the same sentence verbatim ("What L3 does declare for a rate-limited upstream isretryConfig— whoseretryableStatusCodesdefault ... includes429"), andcontent/docs/references/integration/connector.mdx:42is that same comment regenerated bypackages/spec/scripts/build-docs.ts. Both are behind this round's fence onpackages/spec/src/integration/**. So after this PR the repo still teaches the falsehood in those two places, from one source. Follow-up: correct that TSDoc block and regenerate, which is a prose-only change plusgen:docs. Successor: whoever takes the ADR-0049 ruling card, since it lands in the same file.SYNC_ARCHITECTURE.md:192also namesconnectionTimeoutMs/requestTimeoutMs, but only to state that keys carrying a.default()are optional in the author shape — a true statement aboutz.inputthat makes no efficacy claim. Left alone deliberately.Generated by Claude Code
Generated by Claude Code