Skip to content

fix(service-datasource): one live introspection per datasource per validation sweep - #11371

Merged
os-sam merged 2 commits into
mainfrom
claude/issue-10962-validate-object-introspection-memo
Aug 23, 2026
Merged

fix(service-datasource): one live introspection per datasource per validation sweep#11371
os-sam merged 2 commits into
mainfrom
claude/issue-10962-validate-object-introspection-memo

Conversation

@os-sam

@os-samos-sam commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Fixes#10962

What

validateAll / validateDatasource performed one live introspect(datasource) round-trip per federated object, all concurrently — M objects on one datasource dialled the same remote M times per sweep. The sweep now threads a per-call introspection memo through the validation body:

  • sweepScopedIntrospect() returns a reader that memoises the in-flight introspection promise by datasource name for the lifetime of one validateEach call. The memo is a local of that call — it dies when the call returns.
  • validateObject's body moved to a private validateObjectUsing(objectName, readSchema); the public method passes config.introspect directly (a direct call is always a live read), the sweep passes the memoised reader. Public signatures are unchanged.
  • Memoising the promise (not the resolved value) makes the concurrent Promise.all sweep share one read; a rejected read is shared the same way — one connection attempt, M failure rows.

Why per-call, not per-instance (the design constraint from triage): a Map on the service instance would also collapse M reads into 1 — and would serve last sweep's schema to every later sweep for the life of the process. The memo's lifetime is one call, so a long-lived service always answers a new sweep from the remote's schema as it is now.

Proof — the counting pins

New block per-sweep introspection memo [#10962] in external-datasource-service.test.ts (counting fake records every introspect call):

Ablation at 890f8e7cfd (mutation confirmed on disk by anchored greps; predicted signature written first): bypassing the memo turns exactly the 4 counting pins red (4 failed | 36 passed) while every correctness test — including the #11241 refusal-path pin (nothing persisted, nothing introspected) — stays green; restore proven byte-identical via git hash-object, all green. No rebuild needed: the suite imports ../external-datasource-service.js source-relative.

Merge with main at 92809ceb23 — joint resolution with #11166

#11166 (unreachable diff kind, merged 14:17Z) rewrote the same validateEach hunks. Resolution keeps both sides whole: the memoised reader (this branch) plus the unreachable kind in the per-object catch (main), both docblock additions kept. The new counting pin for an unreachable remote now asserts kind: 'unreachable' (a throw out of introspect is never an invented missing_table); the second-sweep and direct-call pins keep asserting missing_table because their schemas are successfully read and lack the table — exactly the structural line #11166 draws.

The one packages/spec line

contracts/external-datasource-service.ts:157 documented validateAll as "parallelised per datasource" while the implementation parallelised per object. The docstring now states the actual behaviour: parallel per object, one live schema read per datasource per call. Comment prose only — no accept/reject change, no surface widened.

Verification at 92809ceb23 (merged head)

Scope notes

Clause-②: yes — by the PATH limb (the diff touches packages/spec/src/contracts/external-datasource-service.ts); content limb no (comment prose only). This PR stays draft and waits on the review chain.

🤖 Generated with Claude Code

https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4


Generated by Claude Code

…lidation sweep
validateAll/validateDatasource used to call config.introspect(datasource)
once per federated OBJECT, all concurrently — M objects on one datasource
dialled the same remote M times per sweep. The sweep now threads a
per-call memo (sweepScopedIntrospect) through the validation body: the
in-flight introspection promise is memoised by datasource for the
lifetime of ONE validateEach call and discarded when the call returns,
so a long-lived service never serves a stale schema to a later sweep.
Direct validateObject calls keep reading live on every call.
Pins: counting fakes assert one read per datasource per sweep (both
sweep spellings), a second sweep reads live again and sees a remote
change (per-call, not per-instance), direct calls stay live, and one
unreachable remote costs one connection attempt while every object on
it still gets its failure row. The IExternalDatasourceService.validateAll
docstring stops promising "parallelised per datasource" for an
implementation that parallelised per object.
Fixes#10962
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
…on-memo
Joint resolution with #11166 (unreachable diff kind, merged 14:17Z):
validateEach keeps this branch's memoised reader (validateObjectUsing +
sweepScopedIntrospect) AND main's 'unreachable' kind in the per-object
catch; both docblock additions kept. The new counting pin for an
unreachable remote now asserts kind 'unreachable' (a throw out of
introspect is never an invented missing_table); the second-sweep and
direct-call pins keep asserting missing_table because their schemas are
successfully READ and lack the table - exactly the structural line
#11166 draws.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

6 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ✅

What this run could not see
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 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 735f5c7099d627cb31881ba6f797953b5db74420packageMentionDocs.

Which tree this was computed on

This run read content/docs from a31d125b1b4ca78d87effeacebb755c4b163bd1c — the merge of head 92809ceb23798d56628af6ab6a156b710718b51a into base 735f5c7099d627cb31881ba6f797953b5db74420, 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 a31d125b1b4ca78d87effeacebb755c4b163bd1c && git checkout a31d125b1b4ca78d87effeacebb755c4b163bd1c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 735f5c7099d627cb31881ba6f797953b5db74420 92809ceb23798d56628af6ab6a156b710718b51a && git checkout -B drift-repro 735f5c7099d627cb31881ba6f797953b5db74420 && git merge --no-ff 92809ceb23798d56628af6ab6a156b710718b51a
node scripts/docs-audit/affected-docs.mjs --json 735f5c7099d627cb31881ba6f797953b5db74420

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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 23, 2026
@os-sam
os-sam marked this pull request as ready for review August 23, 2026 16:12
@os-sam
os-sam added this pull request to the merge queueAug 23, 2026
Merged via the queue into main with commit 29d0676Aug 23, 2026
54 of 56 checks passed
@os-sam
os-sam deleted the claude/issue-10962-validate-object-introspection-memo branch August 23, 2026 16:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validateObject re-introspects the datasource once per object, so validating M federated objects on one datasource performs M live remote schema reads

2 participants

@os-sam@claude