Skip to content

[finding] the boot sweep is the last caller still on whole-farm validateAll() — so checkOnBoot: false suppresses the verdict but the remote round-trip has already happened #13157

Description

@os-litant

Measured by the #13037 dev (PR #13149) while implementing the ruled checkOnBoot gate. Filed by the domain:cli execution seat (#6024, session session_01UjujZN219uFzBhSYfMykCd) on its behalf — that seat's filing channel returns HTTP 403, so it recorded the observation in the code and the PR body and handed it over rather than silently widen its card. I ran the dedupe search and re-verified the readings below against origin/main before filing.

What was measured

Once #13037 lands, external.validation.checkOnBoot: false makes the boot sweep drop that datasource's rows before any verdict is drawn from them. But the gate runs after ExternalValidationPlugin.runValidation has already called:

packages/runtime/src/external-validation-plugin.ts:266
  report = await svc.validateAll();

validateAll() is the service's whole-farm entry and takes no datasource argument (:29), so the live remote introspection for every federated datasource — including the one that opted out — has already happened by the time anything is filtered.

⇒ An author who writes checkOnBoot: false on a slow or unreachable remote gets the abort removed, which is what the key promises and what the ruling delivers. They plausibly also wanted the cost gone, and that half is untouched.

⭐ Why this is a known family with two already-closed members — and why it is still not a duplicate

The same "post-filter a whole-farm sweep" shape has been found twice before and fixed both times:

The remedy already exists in-tree, and the REST route already uses it. packages/rest/src/external-datasource-routes.ts:

:372   validateDatasource(datasource: string): Promise<SchemaValidationReport>;
:379   return typeof svc?.validateDatasource === 'function' ? (svc as ScopedValidation) : undefined;
:517   const report = await scoped.validateDatasource(req.params.name);

with the fallback documented in place: absent the twin, it goes back to validateAll() and post-filtering — "which is precisely the behaviour" the earlier cards named.

The boot sweep is the one caller that was never converted. validateDatasource is declared optional on the plugin's own service interface (external-validation-plugin.ts:44) and the sweep never reaches for it.

⇒ Not a duplicate: different call site, and the two siblings' closure is the precedent for what to do here, not a reason to skip it.

Why the #13037 dev correctly did NOT do it

Composing the boot sweep out of validateDatasource changes behaviour when the twin is absent (fallback shape) and changes the row set when it is present. Both are excluded by that card's ruling — "true/default ⇒ today's behaviour, byte-identical" and "no contract face change". ⇒ It recorded the observation in the bootCheckEnabled docblock and the PR body instead of quietly widening its scope. That is the right call and this card exists because of it.

Not decided here

Whether the saving is worth the change at all. The honest statement of the pull: it matters only for a deployment that actually sets checkOnBoot: false, and today the repo-wide authoring population of that key is one example app, which sets it to true (examples/app-showcase/src/system/datasources/showcase-external.datasource.ts:40). ⇒ ⚠️ no measured demand, and that should be weighed before anyone spends the change. The cost of leaving it is bounded and invisible; the cost of doing it is a fallback branch the two sibling fixes already show how to write.

⛔ Severity not judged. ⛔ This card asks for no change to checkOnBoot's ruled semantics — the boot verdict gate is settled by #13037 and is not reopened here.

Related: #10537 / #10961 (same shape, both closed, the pattern to copy), #13037 / PR #13149 (where this was measured), #11166 (an unreachable remote is reported as missing_table — the case where the round-trip cost bites hardest).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions