Skip to content

feat(spec): type IObjectQLEngine.getSchema and declare IDataEngine.syncObjectSchema (#12481, #12482) - #12986

Merged
os-trump merged 4 commits into
mainfrom
claude/issue-12481-objectql-engine-member-adoption
Aug 28, 2026
Merged

feat(spec): type IObjectQLEngine.getSchema and declare IDataEngine.syncObjectSchema (#12481, #12482)#12986
os-trump merged 4 commits into
mainfrom
claude/issue-12481-objectql-engine-member-adoption

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Fixes#12481
Fixes#12482

Family dispatch (fold): two member cards, one commit per member, both ruled by inheritance from the 2026-08-25 #11833 ruling as executed by #12248 (triage comments 5421199436 / 5421181965). Premise verification found no semantic divergence on either member — no fork to report. Exclusion list honoured: every other unknown-typed or undeclared engine member (including the rest of #12010's "not verified" rows) is out of this fold.

Member 1 — #12481: IObjectQLEngine.getSchema typed (commit d047d5b)

Premise verified on origin/main at fc8a33935 (branch base):

  • Contract said getSchema(objectName: string): unknown (packages/spec/src/contracts/objectql-engine.ts, the schema-access block).
  • The implementation has always answered ServiceObject | undefined (packages/objectql/src/engine.ts:6525), and getObject is literally its alias — return this.getSchema(name) at engine.ts:12847 — so the mother ruling's fork-3 reason (the type lives in spec at data/object.zod.ts; the "engine-local type" rationale is void) transfers whole. Alias relation confirmed on the current ref: no fork.

Change: the member now returns ServiceObject | undefined (authored state, ADR-0122, exactly as getObject). Named pin block added to contracts/objectql-engine.test.ts: exact-type pin (mutual extends), alias non-drift pin (getSchema and getObject answer one type), and a write-guard-slice substitutability pin (the plugin-security repair depends on it).

Reverse verification (direction as observed): reverting the contract member to the base state turned pnpm --filter @objectstack/spec typecheck red with 5 type errors named in src/contracts/objectql-engine.test.ts — the gate's own output names the pin file, which also proves the pins sit inside the measured tsc program. Restore proven: HEAD blob hash equals on-disk blob hash, git diff HEAD empty. Note for reviewers: the vitest leg alone stays GREEN on the mutated tree (vitest transpiles without type checking), so the guarding gate for these type-level pins is the package typecheck — which CI runs.

Member 2 — #12482: IDataEngine.syncObjectSchema declared (commit 5979769)

Premise re-verified on the same base — all three cited sites live:

  • Producer: packages/objectql/src/engine.ts:12974 — async, idempotent, ADR-0015 §18 (federated read-metadata registration without DDL; managed objects get create/alter on demand).
  • service-datasource: ConnectionEngineLike declares the member (datasource-connection-service.ts:104) and the live call runs per bound external object after its driver connects (line 620).
  • service-messaging: messaging-service-plugin.ts:353-355 recovered the member through an as unknown as one-member structural slice, its own comment recording that the member lived on the concrete ObjectQL engine, not the contract.

Placement measurement (which contract file): declared on IDataEngine in contracts/data-engine.ts, directly after the #12248 datasource-lifecycle trio. Evidence: both measured consumers reach the member through IDataEngine-typed values — ConnectionEngineLike documents itself as the surface of the ObjectQL data-engine slot (the same population as the #12248 trio, which #12248 placed in data-engine.ts), and messaging's provisionSystemTables takes engine: IDataEngine directly. Declaring it on IObjectQLEngine instead would leave the messaging call site still needing a cast — the outcome the ruling forbids. The member is datasource-lifecycle-shaped (driven per bound external object after connect) even though implemented on the ObjectQL engine; the #12248 precedent places exactly that shape on IDataEngine as optional members. Named pin block added to contracts/data-engine.test.ts: optionality, exact signature (string parameter, async answering nothing), substitutability against both verbatim consumer-local shapes, and a refusal pin for a synchronous implementation.

⚠️Cross-wave same-file serial:contracts/data-engine.ts is also touched by the gated draft PR #12949 (EngineDatasourceDef + listDatasourceDefs). Landing order: #12949 first; this PR merges main after it lands and regenerates. This branch is based on origin/main, never on #12949's branch; #12949's diff was read via its PR ref, and this member is added after clearDatasourceUnavailable, textually clear of #12949's edit region.

Reverse verification: reverting data-engine.ts to base turned spec typecheck red — data-engine.test.ts reported 6 errors where the debt ledger records 1 pre-existing (the 5 new are the pin block). Restore proven by blob-hash equality and empty git diff HEAD.

Consumer-compat verification (acceptance, both cards)

Direction stated: the sweep is DOWNSTREAM of spec (the prefix filter form selects consumers); each named consumer package was typechecked individually on the merged head. All named cast sites verified:

SiteVerdict
plugin-security security-plugin.ts:1685 (contract-typed ql)cast dropped (below); package typecheck green; full suite 87 files / 1585 tests green
plugin-security probe sites (5142 / 5718 / 5833 / 6456 / 6801)read via this.ql typed any — unaffected; green
service-messaging sql-http-outbox.ts:274reads getSchema through its own IDataEngine-based local structural declaration (line 47), not the changed contract — unaffected; its cast still narrows a local unknown and stays
metadata-core record-organization.ts:79 / 229structural probes over an unknown engine parameter (deliberate test-double tolerance) — stay; typecheck green
runtime action-execution.ts:1596ql is any in that scope — unaffected; typecheck green
service-datasource ConnectionEngineLike + live call (line 620)contract member stays assignable to the local optional-member shape (pinned); typecheck green
service-messaging messaging-service-plugin.ts:355cast dropped (below); typecheck green; full suite 29 files / 295 tests green

No consumer broke — consistent with the mother ruling's implementor/consumer source-compatibility precedent.

Mechanical cast drops, each provably redundant, before/after:

  1. packages/plugins/plugin-security/src/security-plugin.ts:1685 — BEFORE: the getSchema answer (then unknown) was cast with as to the guard's EngineOwnedSchemaLike-or-undefined slice; AFTER: passed directly — the contract answer is assignable to that slice (pinned in objectql-engine.test.ts), and the package tsc is green with the cast removed; the now-unused type import was dropped in the same edit. The runtime typeof probe stays (double tolerance).
  2. packages/services/service-messaging/src/messaging-service-plugin.ts:355 — BEFORE: the engine was cast via as unknown as to a one-member structural slice to reach syncObjectSchema; AFTER: engine.syncObjectSchema read directly off the contract (assignability pinned in data-engine.test.ts; package tsc green). The runtime typeof probe stays.

Gates

Derived with node scripts/pm/dispatch-gates.mjs (no hand-fed paths; derivation header names this repo at commit 5d7b7caba — re-derived after each of the two same-day main merges, family set unchanged). Gate results quote the gates' own verdict lines; exit codes captured before any pipe.

Green at final head 5d7b7caba: contract pin tests 37/37 (both files), spec typecheck, check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:objectql-double-limit, check:nul-bytes, check:dev-prereqs, check-adr-0087-registration.

Green at 82a25cd8c with a declared narrowing for the delta to 5d7b7caba (that delta contains only scripts/pm/, .claude/, content/docs/ files — no package source, no changeset — so these measurements stand): full workspace build (turbo 70/70), spec full suite 440 files / 11653 tests, check:generated "All 14 generated artifacts are up to date" (api-surface unchanged — no new named export from either member), check:type-check-coverage OK (65/78 + ledger) and its --re-measure ratchet "none above its recorded number", all six consumer typechecks (objectql · metadata-core · runtime · service-datasource · service-messaging · plugin-security, each invocation echoed), check:i18n, check:i18n-stale-fill, check:cross-package-test-inputs, check:test-source-alias, check:type-source-resolution, check:spec-parsed-alias, check:slot-lookup, check:published-files, check:page-declaration-shape, check:merge-driver, check:doc-authoring, check:changeset-gate-self-tests, check:objectui-changeset, check:undeclared-dep-imports, check-ci-filter-parity, check-comment-mask-adoption, check-plugin-teardown-shape, check-changeset-no-major, check-empty-changeset, docs-audit affected-docs, release-rehearsal self-test, spec check:empty-state / check:variant-docs / check:liveness / check:strictness-ledger / check:docs / check:skill-refs / check:authorable-surface.

NOT MEASURED (neither green nor red): check:pm-half-states — exits 3 PREREQUISITE NOT MET from this container (the environment token is the proxy placeholder, not a GitHub credential; the gate's own text: "Nothing was swept … it is no reading at all"). CI runs it with its own credential.

Changesets

Two, one per member: @objectstack/spec minor + @objectstack/plugin-security patch (#12481); @objectstack/spec minor + @objectstack/service-messaging patch (#12482). FROM/TO prose per member in each body. check-adr-0087-registration green — no declared-breaking changeset (the #12248/#12949 additive precedent).

Clause-② holds on both members: opened as draft — the review chain owns enqueue; this seat does not flip ready or arm anything.

Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4

Generated by Claude Code


Generated by Claude Code

…ed (#12481)
One member over from #12248 (the #11833 ruling's fork 3, applied by
inheritance): ObjectQL.getObject is literally getSchema's alias, the class
has always answered ServiceObject | undefined, and ServiceObject lives in
spec — so the contract's engine-local-type rationale for unknown did not
apply here either. Pin block mirrors the getObject pins (exact type, alias
non-drift, write-guard slice substitutability). plugin-security's
engine-owned write guard drops its now-redundant
'as EngineOwnedSchemaLike | undefined' cast (typecheck-proven).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
The member #12010's inventory left 'not verified', verified and adopted
under the 2026-08-25 #11833 ruling's item-4 precedent as executed by
#12248: implemented on ObjectQL (on-demand single-object schema sync,
ADR-0015 s18 federated read-metadata registration included), consumed
cross-package by service-datasource (ConnectionEngineLike, per bound
external object after connect) and service-messaging (system-table
provisioning), both through consumer-local structural recovery until now.
Declared optional next to the datasource-lifecycle trio; pin block mirrors
theirs (optionality, exact signature, consumer-substitutability, async
refusal). service-messaging drops its now-redundant 'as unknown as'
recovery (typecheck-proven).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-security, @objectstack/service-messaging, @objectstack/spec, touching 5 documentable anchor(s).

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx(via IDataEngine (symbol))
  • content/docs/ai/knowledge-rag.mdx(via IDataEngine (symbol))
  • content/docs/concepts/architecture.mdx(via getSchema (symbol))
  • content/docs/kernel/contracts/data-engine.mdx(via IDataEngine (symbol))
  • content/docs/kernel/contracts/index.mdx(via IDataEngine (symbol))
  • content/docs/kernel/index.mdx(via IDataEngine (symbol))
  • content/docs/kernel/runtime-services/data-service.mdx(via IDataEngine (symbol))
  • content/docs/kernel/services-checklist.mdx(via IDataEngine (symbol))
  • content/docs/kernel/services.mdx(via IDataEngine (symbol))
  • content/docs/protocol/knowledge.mdx(via IDataEngine (symbol))
  • content/docs/protocol/objectql/query-syntax.mdx(via IDataEngine (symbol))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx(via IDataEngine (symbol))
  • content/docs/releases/v17.mdx(via IDataEngine (symbol), IObjectQLEngine (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 129 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 2b4178aa53ca62089f43e2cfae0b7838cf340dd1packageMentionDocs.

Which tree this was computed on

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

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2b4178aa53ca62089f43e2cfae0b7838cf340dd1 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 28, 2026
@os-trump
os-trump marked this pull request as ready for review August 28, 2026 13:53
@os-trump
os-trump enabled auto-merge August 28, 2026 13:53
@os-trump
os-trump added this pull request to the merge queueAug 28, 2026
Merged via the queue into main with commit 52954c0Aug 28, 2026
41 checks passed
@os-trump
os-trump deleted the claude/issue-12481-objectql-engine-member-adoption branch August 28, 2026 14:32
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

2 participants

@os-trump@claude