Skip to content

docs(spec): ConnectorConnectionState.status JSDoc drops "expired" from the no-refresh-token reconnect reason - #29238

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-6nm8cv
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-6nm8cv

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #28961

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:只改 ConnectorConnectionState.status 的一段 JSDoc 注释,去掉「过期(expired)」一词,外加一个 spec patch changeset。
  • 为什么改:没有 refresh token(或没有 client)的连接,在缓存未命中或请求遇到 401 时也会尝试刷新并被标为需要重连,此时 access token 可能仍然有效;旧注释把原因说成「token 过期」,与运行时不符。
  • 风险与回滚:零运行时风险,只是注释文字;回滚即还原这一个 commit。
  • 先看:packages/spec/src/contracts/automation-service.ts(第 1145–1148 行)。
  • 两分钟验证:在本分支运行 git grep -n "access token it has to renew with" -- packages,应看到 3 处:运行时 2 处(connector-connections.ts、sys-connector-connection.object.ts)和 spec 1 处,说法一致。
  • 需要你做的:无,席位落地。

What changes

  • packages/spec/src/contracts/automation-service.ts: the reconnect_required arm of the ConnectorConnectionState.status JSDoc said a connection is marked when it "keeps nothing to renew an expired access token with". A refresh also runs on a cache miss (a fresh replica, or a revision another replica moved) and on a dispatch's 401, so a connection with no refresh token, or no client to refresh with, is marked while its access token may still be valid. The arm now reads "keeps nothing to renew an access token it has to renew with", the wording the runtime already uses for the same fact in ConnectorConnectionStore.markReconnectRequired (packages/services/service-automation/src/connector-connections.ts) and in the sys_connector_connection.status description, and the sense of the reasons the runtime stores (no_refresh_token / no_client in oauth2-grant/grant-handle.ts: "an access token the connection has to renew cannot be renewed"). The runtime sentence from PR fix(service-automation): a connection marked reconnect required says the cause that actually happened (#28863) #28955 is on main, so no wording waits on it. No new claim is added.
  • .changeset/28961-connector-connection-status-doc.md: @objectstack/spec patch, with the two declaration lines.

Comment only: no schema, type, export or runtime string changes. No forced test edits.

Tests

Run in the card worktree at head 8d3ba747, each under scripts/pm/os-verify-lock.sh:

packages/spec build, test, typecheck, check:generated
  • pnpm --filter @objectstack/spec build (plus the @objectstack/lint closure for the formula gate): VERDICT command-exit 0.
  • pnpm --filter @objectstack/spec test: VERDICT command-exit 0, Test Files 790 passed (790), Tests 23370 passed, 1 todo (23371).
  • pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0.
  • pnpm --filter @objectstack/spec check:generated: exit 0, "All 14 generated artifacts are up to date" against the dist just built. A JSDoc on an interface field is not a .describe(), so content/docs/references/** does not move.

No consumer tests: the claim is Metadata-protocol: none and the public surface is byte-identical (check:api-surface green).

Acceptance notes

None.

Verification

dispatch-gates derived set at 8d3ba74: 73 derived, 71 run green, 2 NOT MEASURED
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 73 commands for the 2 paths; every one was run and recorded with its exit code; --ran reconciles: "73 derived famil(ies) accounted for, 71 run, 2 NOT-MEASURED".
  • 71 exit 0, including check:doc-authoring, check:api-surface, check:docs, check:changeset-no-major --base origin/main, check:empty-changeset --base origin/main, check:adr-0087-registration --base origin/main, check:nul-bytes, check:issue-citations, check:spec-docblock-symbol-anchors.
  • check-plugin-teardown-shape.mjs --self-test first exited 3 on the shallow clone (its pinned fixture commit was unreachable); after fetching that one commit it exits 0, 48 cases pass.
  • NOT MEASURED locally, exit 3 PREREQUISITE NOT MET: pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure. Both load every package's built dist/, which this card did not build (no package's code changes). CI's Lint & Repo Gates runs them.

Generated by Claude Code

…m the no-refresh-token reconnect reason

A refresh also runs on a cache miss and on a dispatch's 401, so a
connection with no refresh token, or no client, is marked while its access
token may still be valid. Match the runtime's own wording.

Claude-Session: https://claude.ai/code/session_01ADHkmQs2bVYuybG5pYYUg2
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/s label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see

Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 45e9b2f14c91f76683296cd33bb1b477cbaef1bb → packageMentionDocs.

Which tree this was computed on

This run read content/docs from be35f992fd5154809e15b8b423f8a7ee35d87c54 — the merge of head 8d3ba747cdcd6b703b09b4c07622d924c3a7eb3e into base 45e9b2f14c91f76683296cd33bb1b477cbaef1bb, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin be35f992fd5154809e15b8b423f8a7ee35d87c54 && git checkout be35f992fd5154809e15b8b423f8a7ee35d87c54
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 45e9b2f14c91f76683296cd33bb1b477cbaef1bb 8d3ba747cdcd6b703b09b4c07622d924c3a7eb3e && git checkout -B drift-repro 45e9b2f14c91f76683296cd33bb1b477cbaef1bb && git merge --no-ff 8d3ba747cdcd6b703b09b4c07622d924c3a7eb3e

node scripts/docs-audit/affected-docs.mjs --json 45e9b2f14c91f76683296cd33bb1b477cbaef1bb

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

Head-sha: 8d3ba747cdcd6b703b09b4c07622d924c3a7eb3e · read 2026-11-02T10:28Z

维护者速读

席位意见:可落地(纯注释,不需契约复审)。

席位核对 — ACCEPT · domain:spec#1 · head 8d3ba747

  1. Shape: draft, base main, Fixes #28961, Clause-②: no / Metadata-protocol: none at line start in the body and the changeset (spec patch). Two files, within claim 6573671795's surface. check-governed-merges: not governed.
  2. Diff: the JSDoc now reads "keeps nothing to renew an access token it has to renew with". That is the runtime's own wording (ConnectorConnectionStore.markReconnectRequired, the sys_connector_connection.status description) and the sense of the stored no_refresh_token / no_client reasons (fix(service-automation): a connection marked reconnect required says the cause that actually happened (#28863) #28955 is on main). No new claim is added.
  3. Readings (dev): spec 790 files / 23370 tests, typecheck and check:generated 14/14, all exit 0. dispatch-gates: 71 run green, 2 NOT MEASURED (dual-build-cjs-loads, lean-entry-closure need every package's dist; CI builds). Offline --event exit 0. Check-runs at this reading: 52 success, 9 skipped, 2 still running (Type Check UI, one Test Core shard), 0 failed.
  4. Landing: comment only, so no CONTRACT_REVIEW_TIER review. The seat readies the PR and arms auto-merge now; the required checks gate the merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit f125ce4 Nov 2, 2026
65 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-6nm8cv branch November 2, 2026 11:26
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8d3ba747cdcd6b703b09b4c07622d924c3a7eb3e
Local-runs: none
Scope: packages/spec/src/contracts/automation-service.ts + packages/services/service-automation/src/connector-connections.ts (markReconnectRequired, resolveRefreshToken, resolveRegisteredClient), packages/services/service-automation/src/sys-connector-connection.object.ts (status, status_reason), packages/services/service-automation/src/oauth2-grant/grant-handle.ts (RECONNECT_REQUIRED_CAUSES, reconnectRequiredReason, grantRefused, endpointOf, refreshOnce, get / refresh), packages/services/service-automation/src/plugin.ts (the MCP tool-discovery mark at the markReconnectRequired call) and .changeset/28961-connector-connection-status-doc.md

Read-only round: the net diff against main at the merge base 104cd1a5 (2 files, +13 / −3, the same two files the PR's file list names), card #28961's body and all three comments, PR #29238's body and file list, and the check-runs on 8d3ba747 (every required context success; the skipped rows are opt-in or path-filtered legs). The dispatching seat's ACCEPT record and landing note are not inputs. Every cited file was read at 8d3ba747, never on the working tree.

① Derived judgments

  1. The net change is one JSDoc arm rewrapped and a changeset; no type, export, .describe() or runtime string moves — right. The ConnectorConnectionState.status union is byte-identical ('not_connected' | 'connected' | 'reconnect_required'); only the comment lines above it change. A JSDoc on an interface field is not a .describe(), so content/docs/references/** is not owed and check:docs has nothing to regenerate; the TypeScript Type Check leg (home of check:generated and check:api-surface) is green on this head.
  2. Dropping "expired" is the correct reading of the runtime — right. In grant-handle.ts, get() refreshes whenever the cached token is absent, stale, or cached under a different revision than the row carries, and refresh() (the 401 retry of callWithBorrowedCredential) refreshes unconditionally; refreshOnce then reaches no_refresh_token the moment resolveRefreshToken answers undefined, and no_client the moment endpointOf does. Neither path reads expires_at. A fresh replica, a moved revision or a 401 therefore marks a no-refresh-token connection while its access token may still be valid, which is what the card states and what the runtime's own comment at RECONNECT_REQUIRED_CAUSES records ("Not 'expired': a refresh also runs on a cache miss ... and on a dispatch's 401").
  3. The three arms enumerate every path that writes reconnect_required — right. markReconnectRequired has exactly two callers in the tree: grantRefused in grant-handle.ts (reached with the three ReconnectRequiredCause values refused, no_refresh_token, no_client) and the MCP tool-discovery handler in plugin.ts (discoveryTokenRefusedReason, written only while the row is still connected under the same connectedAt). The JSDoc's first arm is refused, its second arm names both no_refresh_token and no_client in its parenthesis, its third arm is the discovery mark. Nothing is left out and nothing is invented.
  4. "no refresh token was issued" is exact, not an overclaim — right. resolveRefreshToken answers undefined only when record.hasRefreshToken is false; a stored token that cannot be read back (no crypto provider, no privileged dereference) throws ConnectorCredentialUnavailableError and is never turned into a mark, which the store's docblock states in so many words. "no client to refresh with" is likewise endpointOf answering undefined: no declared tokenUrl + clientId, and no registered client the row keeps.
  5. "statusReason says which" holds. markReconnectRequired writes status_reason: reason, toRecord carries it as statusReason, and the door's state is read from the record; a legacy row storing none is answered with the refused sentence by markedReason, which is the optional member's documented absence case.
  6. The three carriers of the same fact now read the same sentence — right. connector-connections.ts L325–329, sys-connector-connection.object.ts L98 and the spec JSDoc at L1143–1149 carry the one wording ("keeps nothing to renew an access token it has to renew with (no refresh token was issued, or no client to refresh with)"); the card's "Done when" asked for exactly this convergence after PR fix(service-automation): a connection marked reconnect required says the cause that actually happened (#28863) #28955, which is on main at the merge base.

② Semver level

.changeset/28961-connector-connection-status-doc.md declares '@objectstack/spec': patch with Clause-②: no and Metadata-protocol: none; the PR body carries the same two lines. The diff publishes a changed comment in the emitted declarations and nothing else: no authorable key, value or type moves, so none is the kind and patch is its level. Check Changeset and the PR-body guards are green on this head.

③ Boundary flags

Dev flags, from os-dev-report 6574504560 on the card (head 8d3ba747) and the PR body:

  • Deviation: the first commit ran with core.hooksPath overridden, so pre-commit did not run; amended unpushed with the repo's .githooks before the first push. Acceptable as declared: the pushed head is the only one that exists on the remote, it passed pre-commit and pre-push there, and every CI gate on it is green. Nothing to carry.
  • NOT MEASURED locally: check:dual-build-cjs-loads, check:lean-entry-closure (exit 3, prerequisite). Both ride Lint & Repo Gates, green on this head. Right to leave to CI for a comment-only change.
  • files_changed and line_budget match the net diff (2 files, 16 changed lines, no governed path).
  • File surface held: the runtime (packages/services/**) is untouched, as the claim required; the runtime sentence it aligns to was already on main (PR fix(service-automation): a connection marked reconnect required says the cause that actually happened (#28863) #28955), so no wording waits on another PR.
  • open_questions: [], out_of_scope_findings: [] — nothing escalated, and this round found nothing the dev should have escalated.

④ Design judgment

packages/spec/src/contracts/automation-service.ts only, every sentence the change writes against the runtime it describes. Attempts to break it:

  • Tried to find a mark the three arms miss: every markReconnectRequired caller and every reconnectRequiredReason cause is covered (① item 3). Tried to find a reconnect_required written by the connect or revoke doors: neither writes the status; revoke deletes the row (the door then answers not_connected), complete upserts connected.
  • Tried to read "no refresh token was issued" as also covering a read-back failure: it does not, by the store's own contract (① item 4).
  • Tried to make "it has to renew" false for a connection that never needs a refresh: a connection whose server issued no refresh token is still refreshed on a cache miss or a 401 (① item 2), so the "has to renew" clause is what makes the sentence true without "expired". The runtime stores the same sense in both no_refresh_token and no_client reasons.
  • Checked the rewrap moved no words other than the deletion of "expired" and the substitution "an access token it has to renew with": the three lines differ only by that edit and the wrap.

No must-fix, no should-fix. A note, not a finding: the JSDoc states the fact and the runtime's code comment states the mechanism (cache miss, moved revision, 401); a reader of the published contract gets the corrected fact without the mechanism, which is the right altitude for a status description and is what the card asked for.

Implemented-by: claude/project-thread-6nm8cv
Reviewed-by: session_01ADHkmPi25XXKx92Xmg4viS

VERDICT: PASS

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 tooling

Projects

None yet

1 participant