Found while fixing the introspection seam for #10676. Same seam, different key — filed separately so it does not ride that PR.
What
packages/spec/src/contracts/schema-diff-service.ts declares:
exportinterfaceIntrospectedSchema{tables: Record<string,IntrospectedTable>;dialect?: ...;introspectedAt: string;// required}SqlDriver.introspectSchema() returns { tables } and nothing else. plugin.ts hands that value straight to ExternalDatasourceService, which is typed against the spec contract — so the service reads two keys the producer never sets.
Measured
Object.keys() of a real SqlDriver.introspectSchema() result, live in-memory SQLite at 368e7a06f:
dialect and introspectedAt are both undefined.
Consequences
Two, both silent:
- Type mapping runs with no dialect.
ExternalDatasourceService.generateObjectDraft and refreshCatalog both call suggestFieldTypeForSqlType(col.type, schema.dialect as SqlDialect). With dialect undefined every remote column is mapped by the dialect-agnostic fallback, so any dialect-specific mapping the function implements is unreachable on the federation path — exactly the path that needs it. - The persisted catalog records no dialect.
refreshCatalog writes dialect: schema.dialect into the external_catalog metadata record that Studio's schema browser and the boot gate read back.
introspectedAt being absent while the contract declares it required is the sharper half: consumers are entitled to read it without a guard.
Relationship to #10676
Same root shape as the isPrimary / primaryKey collision that card is about: SqlDriver implements the IntrospectedSchema in packages/objectql/src/util.ts (which declares neither key), while everything downstream of plugin.ts is typed against the packages/spec one. #10676's fix repairs the primary-key column of that table; this is another column of it.
Whether the repair belongs in the driver (emit the spec shape) or in the spec (reconcile the two IntrospectedSchema declarations) is the same routing question #10676 raises for the column spelling, and the two should probably be decided together.
Not measured here
Only the SQLite arm was executed. introspectSchema builds its return value in one place for every dialect, so the omission is dialect-independent by construction — but Postgres/MySQL were read, not run.
Filed unassigned as a finding awaiting first-touch grading. Discovered from #10676.
Found while fixing the introspection seam for #10676. Same seam, different key — filed separately so it does not ride that PR.
What
packages/spec/src/contracts/schema-diff-service.tsdeclares:SqlDriver.introspectSchema()returns{ tables }and nothing else.plugin.tshands that value straight toExternalDatasourceService, which is typed against the spec contract — so the service reads two keys the producer never sets.Measured
Object.keys()of a realSqlDriver.introspectSchema()result, live in-memory SQLite at368e7a06f:dialectandintrospectedAtare bothundefined.Consequences
Two, both silent:
ExternalDatasourceService.generateObjectDraftandrefreshCatalogboth callsuggestFieldTypeForSqlType(col.type, schema.dialect as SqlDialect). Withdialectundefined every remote column is mapped by the dialect-agnostic fallback, so any dialect-specific mapping the function implements is unreachable on the federation path — exactly the path that needs it.refreshCatalogwritesdialect: schema.dialectinto theexternal_catalogmetadata record that Studio's schema browser and the boot gate read back.introspectedAtbeing absent while the contract declares it required is the sharper half: consumers are entitled to read it without a guard.Relationship to #10676
Same root shape as the
isPrimary/primaryKeycollision that card is about:SqlDriverimplements theIntrospectedSchemainpackages/objectql/src/util.ts(which declares neither key), while everything downstream ofplugin.tsis typed against thepackages/specone. #10676's fix repairs the primary-key column of that table; this is another column of it.Whether the repair belongs in the driver (emit the spec shape) or in the spec (reconcile the two
IntrospectedSchemadeclarations) is the same routing question #10676 raises for the column spelling, and the two should probably be decided together.Not measured here
Only the SQLite arm was executed.
introspectSchemabuilds its return value in one place for every dialect, so the omission is dialect-independent by construction — but Postgres/MySQL were read, not run.Filed unassigned as a finding awaiting first-touch grading. Discovered from #10676.