Skip to content

docs(adr): ADR-0097 "Upstream availability" states the mcp credential-rejection carve-out (fatal configuration fault, never retried) - #26985

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-9k1i9y
Oct 28, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-9k1i9y

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26809

Clause-②: no
Metadata-protocol: none

维护者速读

What changes

One file, docs/adr/0097-declarative-connector-instances.md:

  • A dated, append-only amendment note under "Upstream availability: degrade, don't die", directly after the paragraph that says the mcp provider classifies connect / tools/list failures as unavailable. The note states the one exception past transport validation: a credential the MCP server rejects (HTTP 401 / 403 on connect, handshake or tools/list) is a configuration fault, thrown plain — fatal at boot, a skipped entry on reload, never CONNECTOR_UPSTREAM_UNAVAILABLE, never retried — on the fatal side of the section's own split beside an unresolvable credentialRef and the openapi provider's non-retryable 4xx on its spec fetch. It says why (retrying cannot make a wrong, missing or under-scoped credential valid; a degraded instance retried with backoff reads as "server down") and how the rejection is told apart (the MCP SDK's own StreamableHTTPError 401 / 403 or UnauthorizedError in the cause chain; a JSON-RPC error merely carrying code 401 stays operational). The original sentence is not rewritten (AGENTS.md Prime Directive 13).
  • A second entry on the header's existing **Amended by** line pointing at that note, so a reader of the header learns where the record was amended, as it already does for ADR-0143.

Every claim in the note is read off origin/main, not invented: the classification and its reason from packages/connectors/connector-mcp/src/mcp-provider.ts#credentialRejectionStatus and #createMcpProviderFactory (the catch branch and its docblock), the "skipped on reload" half from this ADR's own "Runtime reconcile" section, and the real-server pin from packages/connectors/connector-mcp/src/mcp-http-discovery-faults.test.ts (credential rejection — real Streamable-HTTP server: 401 and 403 throw plain and are sent once; a declarative instance answered 401 fails kernel.bootstrap() with no degraded reason and no retry).

Anchors are path#symbol as the ADR symbol-anchor gate requires (no file:line). The note cites #26709 item 3(b) and PR #26798 as the card asks.

Tests

No code changes, no tests added or edited. No forced test edits.

Acceptance notes

Verification

Head f15b7824 (worktree objectstack-issue-26809, base origin/main 7bb8084, merge base 7bb8084).

Derived gates — node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (21 commands) and --ran reconciliation
21 command(s) derived (13 by path, 8 whole-tree); exit codes captured per command before any pipe (runner: bash -c CMD < /dev/null; EXIT recorded):
  20 exit 0 on the first pass; 1 exit 3 (PREREQUISITE NOT MET, nothing measured):
    pnpm --filter @objectstack/lint run check:doc-formula-expressions  — needs @objectstack/formula + @objectstack/lint dist
  fix applied: os-verify-lock: VERDICT command-exit 0 · held the lock 91s (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint, 4 tasks)
  rerun: pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
  check:adr-symbol-anchors  ✅ 2724 anchors across 145 records resolve — 592 symbol (566 declaration, 26 literal), 2085 file-level, 47 cross-repo, 6 exempt, 31 continuation. 0 line anchors survive
  check:adr-index           adr-index --report: OK — docs/adr/README.md is current, every record parsed, self-test 46 assertions green
  check:adr-anchors         OK (91 anchored file(s), every governing ADR still referenced; 139 decision number(s) …)
  check:adr-links, check:doc-authoring, check:nul-bytes, check:pm-governed-merges, check:pm-prior-rulings, check:closed-ceremony-text, check:gitlink-declared, check:ci-filter-parity, check:cross-package-test-inputs, check:driver-memory-census, check:refd-timer-probe, check:watch-hint-literal, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, plan-test-core-shards --self-test: exit 0
--ran (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt):
  ✓ dispatch-gates --ran: 21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED (a DERIVED zero — all 21 recorded an exit code and none of them is 3).
Tier (dispatch-gates --tier)
Model tier — MANDATORY: claude-fable-5-1 (derived from the file surface, not recalled).
  - docs/adr/0097-declarative-connector-instances.md ⇢ 'docs/adr/**' … no one-line exemption
Changed lines — 21 (+20 / -1; generated files INCLUDED) vs the human-merge threshold 3000: under.

Not built, not tested locally: the diff touches no package (local verification scope ① / ② are empty); pnpm lint and every whole-tree family are CI's — see this PR's check-runs.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (no code change)
  • 2. Every name I added uses the casing for its kind. (none added)
  • 3. No formatter ran; pnpm lint is CI's (no lintable file changed).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. Nothing publishes: no changeset, skip-changeset; both declaration lines are above.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the required Chinese business view).

Card thread: session_01ADHkmSPesP5MpCBMrkkQw5, dispatched by domain:skills#1 (session_01ADHkmSrRBdu9MdC6fc33Uu).

🤖 Generated with Claude Code

https://claude.ai/code/session_01ADHkmSPesP5MpCBMrkkQw5


Generated by Claude Code

…ial-rejection carve-out

Append-only amendment note under "Upstream availability": a credential the
MCP server rejects (HTTP 401 / 403 on connect, handshake or tools/list) is a
fatal configuration fault, never upstream-unavailable and never retried, as
the mcp provider has classified it since the connector load-faults change.
The sentence itself is not rewritten; the header's Amended-by line gains the
matching entry.

Claude-Session: https://claude.ai/code/session_01ADHkmSPesP5MpCBMrkkQw5
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 28, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:skills#1 · session_01ADHkmSrRBdu9MdC6fc33Uu · head f15b7824 · 2026-10-28T00:10Z

  1. 形态 — Tier H (docs/adr/**): docs/adr/0097-declarative-connector-instances.md +20/−1 — a dated, append-only amendment note right after the mcp paragraph of "Upstream availability": a credential the MCP server rejects (401 / 403 on connect, handshake or tools/list) is a configuration fault, thrown plain, fatal at boot, never CONNECTOR_UPSTREAM_UNAVAILABLE, never retried, beside the section's own unresolvable-credentialRef and openapi 4xx cases; the header's Amended by line gains a second entry (the −1). The original sentence is untouched. No changeset.
  2. 前提实测 — PR fix(connectors)!: refuse a non-3.x OpenAPI document at load; mcp tools/list follows nextCursor and a 401/403 is a fatal configuration fault #26798 merged; on main connector-mcp/src/mcp-provider.ts declares credentialRejectionStatus (L162) and createMcpProviderFactory, the two path#symbol anchors the note cites; ADR-0097:187 still carries the unqualified sentence.
  3. 门禁 check-runs — dev ran the 21 derived commands, all exit 0 after building the formula / lint closure under the lock (--ran 21/21, 0 NOT-MEASURED); check:adr-symbol-anchors, check:adr-index, check:adr-anchors green. CI on the head is read again before landing.
  4. 接受的偏差 — the Status line is left alone (open question 0: check:adr-index reads its earliest keyword; the header's existing Amended by line carries the entry instead) — accepted.
  5. 范围外去向 — none. Contract review at CONTRACT_REVIEW_TIER, effort ≥ xhigh: requested from the director seat.
  6. 落地条款 — governed, Tier H: stays draft until a GOVERNED_APPROVERS approval; then it lands through the queue.

@os-zhuang
os-zhuang marked this pull request as ready for review October 28, 2026 00:11
@os-zhuang
os-zhuang enabled auto-merge October 28, 2026 00:11
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f15b78245ab2f2669e3057b6e2430ca613b20ebd
Local-runs: none
Scope: docs/adr/0097-declarative-connector-instances.md + packages/connectors/connector-mcp/src/mcp-provider.ts, packages/connectors/connector-mcp/src/mcp-http-discovery-faults.test.ts, scripts/adr-index.mjs, scripts/check-adr-symbol-anchors.mjs

Director seat (summon #52, ledger objectstack#12708), summoned by the domain:skills#1 seat; effort ≥ xhigh per the claim. Read-only at the head above: the diff, card #26809 and its thread (triage 6449606074, claim 6456601006, dev report 6456938732), the seat's ACCEPT 6456973580, PR #26798 (merged 55b5042e, an ancestor of origin/main 49b50591), ADR-0097, the mcp provider, its real-server test and the two ADR gates on origin/main, the head's check runs, reviews and timeline. docs/adr/** is a governed-register row ⇒ the rules face, CONTRACT_REVIEW_TIER; Tier H. The seat records; it does not approve, ready, queue or merge.

① Derived judgments

  • The note states what main does, symbol for symbol. mcp-provider.ts#credentialRejectionStatus (:162) walks the error's cause chain and answers only for the MCP SDK's own HTTP-transport errors — a StreamableHTTPError with code 401 / 403, or an UnauthorizedError (read as 401) — so a JSON-RPC error that merely carries a numeric code 401 is not a rejection (its docblock :156–159 says exactly this). #createMcpProviderFactory (:210) throws that case plain in its catch (:239–248: "connect, handshake or tools/list"), and wraps every other failure as operational / retryable (:258–261). The "sent once, no retry" and "declarative instance answered 401 fails kernel.bootstrap()" pins are mcp-http-discovery-faults.test.ts:137–191 (credential rejection — real Streamable-HTTP server, an it.each over 401 / 403, and the boot case rejecting with was refused by its MCP server (HTTP 401)). "A skipped entry on reload" is this ADR's own Runtime reconcile section (main L152–154: on reload a bad entry is logged and skipped; boot keeps fail-loudly). Nothing in the note is invented.
  • It sits on the fatal side the section already draws. main L161 (unresolvable credentialRef is a configuration fault) and L193–197 (the openapi spec fetch: 408 / 429 / 5xx retryable, a non-retryable 4xx fatal — so a 401 is fatal there, as the note says). The carve-out follows the section's own split rather than adding a new one.
  • Append-only, in the file's two existing forms. The blockquote > **Amendment note (date, source).** is the shape this file already carries at L74 and L130 (and ADR-0092 / ADR-0099 use for non-ADR sources); it is placed directly after the mcp paragraph it qualifies and before the openapi paragraph. The sentence it qualifies is untouched (classifies connect still has exactly one hit at the head). The header's **Amended by**: line gains a second entry; that is the file's header convention (ADR-0005 carries several entries on that line), and the **Status**: line is left as Accepted (2026-07-15). Prime Directive 13 holds.
  • Anchors are in the gate's grammar. path#symbol for both symbols, the second as a same-line bare #symbol continuation, which check-adr-symbol-anchors.mjs admits (its self-test pins "src/thing.ts#realSymbol then #sys_thing"); the test file is a file-level anchor. No file:line. Lint · docs & code guards (home of the anchor gate) and Lint · repo & tooling gates (check:adr-index) are success on the head.
  • skip-changeset is right. docs/adr/** publishes nothing; Clause-②: no / Metadata-protocol: none match a record-only change. One file, +20 / −1, inside the claim's surface.

② Semver level

skip-changeset. Correct.

③ Boundary flags

  • Governed path (docs/adr/**, Tier H). At this record the PR is approved by os-zhuang (GOVERNED_APPROVERS) at 2026-10-28T00:11:15Z on this head and marked ready for review by the same hand at 00:11:18Z — not by the dev, whose report and the seat's ACCEPT both predate it and describe a draft. The maintainer's approval is the landing condition; this record changes nothing about it. This seat's account is not a GOVERNED_APPROVERS member; this record is not an approval.
  • The Status line: the card delta asked for a Status-line Amended entry only if the file's convention has one; this file's convention is the header Amended by line, and scripts/adr-index.mjs#statusKeywordOf reads the earliest keyword in the Status value, so leaving Accepted alone is the safe reading. Appending an Amended entry after Accepted would not have re-keyed the record either, so the dev's stated reason is over-cautious, not wrong; nothing to change.
  • Header entry separator: ADR-0005 separates its Amended by entries with commas; here the second entry starts a new sentence after the ADR-0143 entry's full stop. adr-index does not parse that line for references, so this is a style nit, not a gate matter. No should-fix.
  • Check runs on f15b7824 at this record: 21 success, 18 expected skips (docs-only), 4 in_progress (Test Core 1/8 and three Type Check jobs). Nothing red; a red there re-opens this record. main has not moved on ADR-0097 since the base.
  • Dev deviations: none. Scope matches the claim; the skills/** non-mirror reading (no skill page teaches the openapi / mcp provider) carries over from PR fix(connectors)!: refuse a non-3.x OpenAPI document at load; mcp tools/list follows nextCursor and a 401/403 is a fatal configuration fault #26798's body and is not re-measured here.

④ Design judgment

  • The note is the narrowest edit that stops the record misleading an operator about a bad token: it keeps the landed sentence, states the exception with the symbols that implement it and the reason the fatal side is right (a retry cannot make a credential valid; a backoff loop reads as "server down"), and names the test that pins it. Holds.

Implemented-by: claude/project-thread-9k1i9y
Reviewed-by: session_01ADHkmQr732toX135tyo5zs

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants