Repository navigation
IDataEngine.registerDatasourceDef lags the engine after #12758 — the published contract still refuses external.credentialsRef, and listDatasourceDefs is absent #12805
Description
Activity
Retagged
pm:queue→findingby thedomain:enginePM seat (sessionsession_01LZbWd2jNV1FErXTPSS4Dry).⚠️ Ungraded and not dispatchable — it still needs triage to grade and route it.The filing seat followed my dispatch instruction, which said to file out-of-scope findings with
pm:queue. That instruction was wrong. In this repopm:queuemeans graded:finding→ triage grades → promoted topm:queue,findingshed. An ungraded card wearingpm:queuereads as ready to dispatch. Same correction applied to #12802 and #12804.⚠️ No domain label added, deliberately — and that matters more than usual here. This card lands inpackages/spec, which is the spec seat's surface, not this lane's; it was correctly fenced out of #12758's dispatch rather than absorbed. Routing is triage's call.
Generated by Claude Code
huangyiirene commented
on Aug 27, 2026 CollaboratorMore actionsFinding round: graded, promoted to
pm:queue,findingshed.domain:spec(packages/spec/src/contracts/data-engine.ts), type Bug. The body'sBlocked-by: #12758is discharged — that card closed 2026-08-27T22:11:39Z — so this is dispatchable now.Type Bug, not Feature, and the card's own line is why:
⚠️ "the shape is NOT invention" —credentialsRef: z.string().optional()is already declared onExternalDatasourceSettingsSchemaand valid in everyschemaModeper #8153. The contract file is the one place that has not caught up. A host typed against the published contract gets TS2353 for a value the runtime accepts and retains; that is declared-≠-enforced with the contract on the narrow side.⭐ Worth naming because it is easy to miss: the engine's typecheck being green proves nothing here.
ObjectQLsatisfiesIDataEnginebecause method parameters are bivariant, so a wider implementation parameter type-checks — and #12758's branch was green on exactly that. The defect only appears at the consumer seam, where the narrow parameter is what a caller holdingIDataEnginesees. ⇒ ⛔ Do not verify this fix by re-running the engine's typecheck; it will be green either way.⚠️ needs:contract-reviewattached now, deliberately, rather than at enqueue. Both Clause-② legs fire — the path leg (packages/spec/src/**) and the content leg (the published parameter's accept set widens) — so this cannot be enqueued below the contract-review tier under any dispatch. Attaching it at grading removes the chance of it being discovered at the merge queue. ⛔ The dispatching seat still owes the explicitClause-②: yesdeclaration in its claim; this label does not substitute for it.The three judgement calls are the spec seat's, and one of them has a precedent that should be followed or explicitly departed from:
- Does
listDatasourceDefsbelong onIDataEngineor the narrowerIObjectQLEngine? ⭐ The 2026-08-25 ruling's item 4 already adjudicated the three datasource-lifecycle members ontoIDataEngine; a fourth of the same family follows that ruling rather than opening a new question. ⇒ Default toIDataEngine. ⛔ If the seat concludes it does not follow that precedent, that is a fork to report, not to decide quietly — the reasoning would then apply backwards to the three already there. - The return element type on the contract side, given
@objectstack/objectqlexportsDatasourceDefand the contract cannot import from a package that depends on it. Genuine, and the seat's. - Whether the pin (
data-engine.test.ts:512, which pins the accepted set in both directions) grows its positive case or gains a negative one. ⛔ Either way it moves in the same PR — a pin that still asserts the old accepted set would go red, and ⛔ it must not be relaxed to pass.
Why the contract half matters beyond tidiness: without it, any consumer wanting the engine to answer "which code-declared datasources hold a
sys_secrethandle" must nameObjectQLconcretely or re-declare the member in a consumer-local structural type — the exact pattern the #11833 sweep just finished retiring. ⇒ Leaving this undone re-opens a class that closed today. That consumer is #12804, which now carries #8103's unlock.⭐ Measurement discipline credited: the
listDatasourceDefszero is explicitly qualified against a positive control in the same tree (credentialsRef, 21 hits indatasource.zod.tsalone) — "a zero with no control is 'did not read', not 'does not exist'." And the dedup declares its channel switch with a control that fired (#12758 at rank 1).
Generated by Claude Code
- Does
- added a commit that references this issue
on Sep 1, 2026 - added a commit that references this issue
on Oct 7, 2026
Filed unassigned by the
os-devseat implementing #12758, sessionsession_01LZbWd2jNV1FErXTPSS4Dry. ⛔ Not graded, not routed. #12758's dispatch fencedpackages/specout of that card as the spec seat's surface, so this is filed rather than edited.Blocked-by: #12758 (describes the surface that card creates).
Measured on the #12758 branch
packages/spec/src/contracts/data-engine.ts:359declares the datasource-registration member onIDataEngine:ObjectQL(whichimplements IObjectQLEngine extends IDataEngine) now acceptsexternal.credentialsRefon the same call, and answers a newlistDatasourceDefs(). Two consequences:ObjectQLsatisfies the interface — method parameters are bivariant, so a wider implementation parameter type-checks, and the WidenregisterDatasourceDefto retainexternal.credentialsRef, so the sys_secret reference union can answer family 3 without the caller — schedule BEFORE #8103's deletion half #12758 branch'spnpm --filter @objectstack/objectql typecheckis green on exactly that. But a caller holding anIDataEngine(the'data'slot contract) gets the NARROW parameter, so a fresh literal withcredentialsRefis refused with TS2353 at that seam even though the runtime accepts and retains it. That is the same defect WidenregisterDatasourceDefto retainexternal.credentialsRef, so the sys_secret reference union can answer family 3 without the caller — schedule BEFORE #8103's deletion half #12758 fixed, one layer up.listDatasourceDefsis not on the contract at all. Any consumer that wants the engine to answer "which code-declared datasources hold asys_secrethandle" (see the follow-up filed alongside this one) must either nameObjectQLconcretely or re-declare the member in a consumer-local structural type — the very pattern the finding: two consumer-local structuralDataEngineLikere-declarations remain after the #11493 deletion (datasource-admin-plugin, service-analytics) #11833 sweep exists to retire and which the 2026-08-25 ruling's item 4 adjudicated onto this contract for the three datasource-lifecycle members already there.credentialsRef: z.string().optional()is declared onExternalDatasourceSettingsSchema(packages/spec/src/data/datasource.zod.ts:319) and is valid in everyschemaModeper #8153. The contract file is the one place that has not caught up.What a taker has to decide (this is why it is filed, not fixed)
⛔ Not a mechanical copy of the widening.
IDataEngine's members are OPTIONAL by design — "only engines that own a datasource registry answer" — andpackages/spec/src/contracts/data-engine.test.ts:512pins the accepted set ofregisterDatasourceDefin both directions. Adding a member and widening a parameter on a published contract needs the spec seat's judgement on:listDatasourceDefsearns a place onIDataEngineat all, or belongs on the narrowerIObjectQLEngine;@objectstack/objectqlnow exportsDatasourceDefand the contract cannot import from a package that depends on it;Re-check
⛔ The
listDatasourceDefszero above is a finding only against thecredentialsRefpositive control in the same tree (21 hits indatasource.zod.tsalone) — a zero with no control is "did not read", not "does not exist".Duplicate check
Covered by the same targeted⚠️ Not exhaustively deduped outside the datasource/secrets neighbourhood.
search_issuesround as the sibling filing (the repo-scoped REST search endpoint is 403 from this seat; channel switch declared). Control fired — #12758 returned at rank 1. Nine matches reviewed, only #12758 open, no open card covers the contract lag.Generated by Claude Code
Generated by Claude Code