Skip to content

fix(spec,drivers): the three drivers type-check — and most of their 292 errors were the types, not the tests (#4311) - #4407

Merged
os-zhuang merged 5 commits into
mainfrom
claude/framework-type-check-missing-1dhwcz
Jul 31, 2026
Merged

fix(spec,drivers): the three drivers type-check — and most of their 292 errors were the types, not the tests (#4311)#4407
os-zhuang merged 5 commits into
mainfrom
claude/framework-type-check-missing-1dhwcz

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Burns down the largest block of #4311's code-tier ledger: driver-sql (241), driver-sqlite-wasm (27) and driver-memory (23). All three now declare typecheck and leave the DEBT ledger — 60/77 packages covered, 17 ledgered (was 57/20).

#4311 filed these as one playbook: author-tier literals handed to a parsed-tier parameter. That is what 127 of the 292 were. 165 were the types being wrong, and only a tsc that had never run could have kept them invisible.

What the errors actually were

bypassTenantAudit was never on DriverOptionsSchema — 118

SqlDriver.auditMissingTenant reads it. Its own warning text tells callers to pass it ("set bypassTenantAudit:true to silence"). ObjectQLEngine sets it for system-context calls (engine.ts:1069). service-settings and service-datasource send it on global-scope writes. packages/verify documents it. Only the schema had never heard of it — so every driver read went through (options as any) and every caller through an untyped options object, and nothing on either side type-checked.

Declared now, with the limit stated in the schema: it silences a diagnostic, it never changes which rows a write touches.

The same as any covered timezone, tenantId, tenantIds and preserveAudit — all four long since declared. All seven casts are gone, so the next undeclared option fails the build instead of hiding behind one.

findOne(object, id) was on no contract — 41

An undeclared typeof query === 'string' | 'number' branch in SqlDriver, used by nothing outside this package's own tests, and answered differently by the other two drivers:

driver findOne('task', 't1') does
SqlDriver id lookup (the undeclared branch)
MemoryDriver spreads the string → {0:'t',1:'1'}
MongoDriver reads query.whereundefinedan arbitrary row

It also bypassed find(), skipping field selection, temporal coercion, and the deterministic-order tie-breaker IDataDriver.find explicitly promises. Removed; call sites spell the lookup as { object, where: { id } }.

initObjects did not declare the tenancy it consumes — 4

Each object flows into computeAndRecordTenantField, which reads obj.tenancy to pick the tenant column and set the sticky opt-out. registerExternalObject had it right all along.

AnalyticsQuery had no author tier — 20

timezone is .default('UTC') and Cube.public is .default(false), so the parsed types require both while authors write neither. Added AnalyticsQueryInput beside AnalyticsQuery (the existing QueryInput/QueryAST pattern) and routed the literals through the schema, so the parse itself proves the default lands. defineCube — already in spec for exactly this — does the same job for the cube.

InMemoryDriver.create declared no return type — 3

TS inferred the literal it builds, so every column of the created row except id/created_at/updated_at vanished from the caller's view.

The second commit: 111 as any casts

Onboarding a package is supposed to make its typecheck mean something. These three declared it while 111 driver-call arguments were cast to any. Stripping them produced 66 fresh errors, every one real:

  • 65 were a missing object — the same defect, in the sites the checker had not been allowed to read.
  • 1 was a silent runtime bug. sql-driver-aggregate-temporal-output.test.ts asked for orderBy: [['id', 'asc']]. The driver reads item.field; a tuple has none; if (item.field) was false and the sort never reached SQL. The helper is called viaFind and exists to read a column in order — in the one file whose subject is output ordering. The tuple form appears nowhere else in the repo.

43 casts were needed by nothing at all. Exactly 2 are load-bearing and stay: sql-driver-filter-no-silent-drop and memory-filter-ast-vocabulary both feed where shapes isFilterAST() refuses, on purpose, to prove the driver throws rather than dropping the condition. Those now read where as FilterCondition next to a comment explaining why — one honest cast is easier to see when a hundred idle ones aren't standing next to it.

Six call sites the checker could not see

findOne('users', alice.id as any) — the cast is what let an off-contract call survive a contract narrowing. They surfaced as runtime failures the moment the branch was removed, which is the point of the whole issue.

Filed, not fixed: #4406

QueryAST.object is required at the driver boundary and read by no driver — parameter 1 always wins, and nothing rejects a call where the two disagree. Adding it to query literals was the single largest edit here (127 keys, all inert at the call site). Reshaping IDataDriver across four drivers is a design change, not a rider on a type-debt burn-down.

Verification

  • turbo run typecheck122/122 (three new targets)
  • pnpm test132/132 tasks; 1069 tests green across the three drivers
  • pnpm lint, the 12 lint.yml check gates, coverage ratchet self-test 18/18
  • packages/spec/authorable-surface.json regenerated by the build (one added key)

Generated by Claude Code

claude added 2 commits July 31, 2026 11:34
…d 165 of their 292 errors were the types, not the tests (#4311)

`driver-sql` (241), `driver-sqlite-wasm` (27) and `driver-memory` (23) were
filed as one playbook: author-tier literals handed to a parsed-tier parameter.
That is what 127 of them were. The other 165 were the types being wrong, and
only a tsc that had never run could have kept them invisible:

- **`bypassTenantAudit` was never on `DriverOptionsSchema`** (118 errors).
  `SqlDriver.auditMissingTenant` reads it, its own warning text tells callers
  to pass it, `ObjectQLEngine` sets it for system-context calls, and both
  `service-settings` and `service-datasource` send it on global-scope writes.
  Declared now, with the limit stated: it silences a diagnostic, it never
  changes which rows a write touches. The driver read it — and `timezone`,
  `tenantId`, `tenantIds`, `preserveAudit`, all four long since declared —
  through `(options as any)`; those seven casts are gone, so the next
  undeclared option fails the build instead of hiding behind one.

- **`findOne(object, id)` was on no contract** (41). An undeclared
  `typeof query === 'string'` branch in `SqlDriver`, used by nothing outside
  this package's own tests, and answered differently by the other two drivers:
  `MemoryDriver` spreads the string (`{0:'t',1:'1'}`), `MongoDriver` reads
  `query.where` (undefined → an arbitrary row). It also bypassed `find()`, so
  it skipped field selection, temporal coercion and the deterministic-order
  tie-breaker `IDataDriver.find` promises. Removed; the call sites spell the
  lookup as `{ object, where: { id } }`.

- **`initObjects` did not declare the `tenancy` it consumes** (4) — each
  object flows into `computeAndRecordTenantField`, which reads `obj.tenancy`
  to pick the tenant column and set the sticky opt-out.
  `registerExternalObject` had it right all along.

- **`AnalyticsQuery` has no author tier** (19+1). `timezone` is
  `.default('UTC')` and `public` is `.default(false)`, so the parsed types
  require both; the tests wrote author-tier literals. Added
  `AnalyticsQueryInput` beside `AnalyticsQuery` (the `QueryInput`/`QueryAST`
  pattern) and routed the literals through the schema, so the parse itself is
  the proof the default lands — `defineCube` does the same job for the cube.

- **`InMemoryDriver.create` declared no return type** (3), so TS inferred the
  literal it builds and every other column of the created row vanished from
  the caller's view.

The remaining 127 are the filed playbook: `object` added to the query literal
(the driver reads the name from parameter 1 — the AST field is required and
redundant there, filed separately), and `count`/`find` calls likewise.

Six call sites the type checker could not see at all wrote
`findOne('users', alice.id as any)`; the cast is what let an off-contract call
survive a narrowing. They surfaced as runtime failures the moment the branch
was removed, which is the point.

All three packages now declare `typecheck` and leave the DEBT ledger: 60/77
packages covered, 17 ledgered. 1069 tests green across the three.

Claude-Session: https://claude.ai/code/session_011m13wbaZziPveBdtQthrXd

Co-authored-by: Claude <noreply@anthropic.com>
…were opting the driver call sites out of the check they had just been given (#4311)

Onboarding a package is supposed to make its `typecheck` mean something. These
three declared it while 111 driver-call arguments were cast to `any` — the
gate went green over call sites the type checker was not allowed to read.

Stripping the casts produced 66 fresh errors, every one real:

- 65 were a missing `object` on the query literal, the same defect the first
  commit fixed 127 times in the sites that were visible.
- 1 was a silent runtime bug. `sql-driver-aggregate-temporal-output.test.ts`
  asked for `orderBy: [['id', 'asc']]`; the driver reads `item.field`, a tuple
  has none, so `if (item.field)` was false and the sort never reached SQL. The
  helper is named `viaFind` and exists to read a column in order — it had been
  reading whatever order the rows came back in, in the one file whose subject
  is output ordering. The tuple form appears nowhere else in the repo.

43 of the casts were needed by nothing at all. Exactly 2 are load-bearing and
stay: `sql-driver-filter-no-silent-drop` and `memory-filter-ast-vocabulary`
both feed `where` shapes `isFilterAST()` refuses, deliberately, to prove the
driver throws rather than dropping the condition — those now say
`where as FilterCondition` next to a comment, so the one honest cast in the
package is not camouflaged by a hundred idle ones.

Verified: `turbo run typecheck` 122/122 (three new), `pnpm test` 132/132,
eslint, the 12 lint.yml check gates, coverage ratchet self-test 18/18.

Claude-Session: https://claude.ai/code/session_011m13wbaZziPveBdtQthrXd

Co-authored-by: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 12:54pm

Request Review

)

Two conflicts, both in `driver-sql`, both where #4363's paged-read work landed
on the same lines as this branch's contract fixes.

`sql-driver.ts` — `findOne`. main refactored the shared read path into
`findRows(object, query, options, singleRowLookup)`; this branch removed the
undeclared bare-id branch above it. Kept both: the bare-id branch stays gone and
the surviving call is main's `findRows(..., true)`, so a single-row lookup keeps
the `singleRowLookup` ORDER BY decision #4363 introduced. The method's doc
comment named `find()` as the path the removed branch skipped; it now names
`findRows()` and what that path actually does.

`sql-driver-pagination-conformance.test.ts` — took main's version whole (it adds
the `PAGINATION_UNORDERED_CASES` suite) and re-applied this branch's two
transforms to it: 18 `as any` casts on driver-call arguments dropped, and the
15 missing `object` keys they had been hiding added back.

One of those 15 was a codemod misfire caught in review, not by tsc: `sqlOfFind`
passes a whole query through, and the rewrite read its `query` parameter as an
id (`where: { id: query }`) — type-clean, semantically nonsense, and it would
have made every assertion in that block test the wrong statement. The helper now
takes `Omit<QueryAST, 'object'>` and spreads it. Audited every generated
`where: { id: … }` in the branch for the same shape; that was the only one.

Verified on the merge result: `turbo run typecheck` 122/122, `pnpm test`
132/132, eslint, coverage ratchet 60/77 with self-test 18/18, release-notes gate.
Adds the changeset this branch was missing.

Claude-Session: https://claude.ai/code/session_011m13wbaZziPveBdtQthrXd

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file protocol:data tests tooling size/l labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec.

111 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/driver-memory)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/driver-memory)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/driver-memory, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-memory, @objectstack/driver-sql, @objectstack/driver-sqlite-wasm, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

claude added 2 commits July 31, 2026 12:42
…udit` (#4311)

`content/docs/references/data/driver.mdx` is generated from the Zod schema, so
declaring a new `DriverOptions` key leaves it stale until `gen:docs` runs.
`@objectstack/spec check:docs` caught it — the generated table is what a driver
author reads to learn which options exist, and it had been complete for every
key except the one this branch adds.

`pnpm --filter @objectstack/spec gen:schema && gen:docs`; the diff is the one
row. 252 generated files back in sync.

Claude-Session: https://claude.ai/code/session_011m13wbaZziPveBdtQthrXd

Co-authored-by: Claude <noreply@anthropic.com>
…4311)

`check:api-surface` reports every public export against a committed snapshot,
so a new type — additive, 0 breaking — still has to be acknowledged rather than
appearing unannounced in a consumer's build. One line.

Also ran the other fifteen `@objectstack/spec` gates against this branch rather
than waiting to meet them one CI round at a time: docs, generated, skill-refs,
skill-docs, exported-any, authorable-surface, spec-changes, upgrade-guide,
liveness, empty-state, variant-docs, strictness-ledger, react-blocks,
react-conformance, skill-examples — plus `check:i18n` / `check:i18n-coverage`
and `check:generated --reconcile-only`, which are the remaining steps of the
CI job that failed. All green; only this snapshot needed regenerating.

The pattern behind both this and the previous commit: `pnpm build` does not run
the generators, so a schema edit type-checks and tests clean locally while two
committed artifacts derived from it sit stale. Worth knowing before the next
spec change — the generators are `gen:*` in `packages/spec`, and their gates
are the `check:*` beside them.

Claude-Session: https://claude.ai/code/session_011m13wbaZziPveBdtQthrXd

Co-authored-by: Claude <noreply@anthropic.com>
@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 13:15
@os-zhuang
os-zhuang merged commit 4384921 into main Jul 31, 2026
19 checks passed
@os-zhuang
os-zhuang deleted the claude/framework-type-check-missing-1dhwcz branch July 31, 2026 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:data size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P3] IDataDriver: query.object is required at the driver boundary and read by no driver — parameter 1 silently wins

2 participants