Feature: Query Adapter Seam#91
Merged
Merged
Conversation
… hardcodes AD4M
Correction to the earlier framing: `irToAd4mQuery` emitted only the neutral flat `$query` dialect
(no AD4M predicates) — it was misnamed, and the genuine AD4M coupling (the `ModelClass` ORM) is
already injected via `$getModel`. So the fix isn't "move a file" — it's stop the renderer from
hardcoding the IR→backend composition and inject a real adapter that owns the AD4M-specific pieces.
- Rename `irToAd4mQuery` → `irToFlatQuery` (schema-shared) — it's the neutral inverse of
`compileQuery`, consumed by any `ModelClass` ORM (AD4M's, the in-memory harness's).
- New `QueryAdapter` port (dataSource.ts): `{ capabilities, plan(ir), lower(ir) }`. Added to
`RendererDataBindings` as `$queryAdapter`. The renderer routes `compileQuery → plan → lower`
through the injected adapter and knows nothing about any backend.
- `ad4mQueryAdapter` (app-framework) composes the neutral pieces with AD4M's profile: `plan` is
`planQuery(ir, ad4mCapabilities)` PLUS the two conditional degradations no capability boolean can
express — OR/AND/NOT in `where` disables AD4M's sort/pagination pushdown, and a projection/
relation-path sort needs a `limit`. This finally makes `planQuery` live (it was dead code).
- The in-memory harness gets its own AD4M-free `QueryAdapter`, so its flag-on test exercises the
same renderer path the AD4M adapter does.
- Drop the `adamStore.currentPerspective` fallback in the renderer (both query paths) now that
`$currentDataset` is injected — no AD4M store reference left in schema-solid's data path. Migrated
the queryToken test fixtures to inject `$currentDataset`.
- Simplify the `$me` binding to `$me: adamStore.me` (mirroring `$currentDataset:
adamStore.currentPerspective`) instead of a hand-rolled identity-assembly function. The seam type
stays neutral (`() => unknown`, not AD4M's `Agent`); templates read `$me.did`.
Increment 1 of 2: the adapter is wired and gaps are correctly classified, but a gap still falls back
to the direct backend path (logged). Increment 2 runs compute-up (executeQueryIR) for the gaps.
schema-shared 443 · schema-solid 34 · harness 7 · app-framework 58 green; tsc + lint clean.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
…trality
Removes the last AD4M-specific vocabulary from the query/template layer.
**Drill-down → neutral `scope`.** The one template with a raw AD4M predicate
(`parent: { id, predicate: 'ad4m://has_child' }`) now authors `scope: { anchor, via, anchorId }`.
The pipeline: `scope` added to the authoring grammar (QueryToken + zQuery) → `compileQuery` passes it
to the IR's existing `Scope` → the AD4M adapter's `lower` resolves `via` on `anchor` to the relation's
predicate (from the perspective's model manifest, `ModelManifestProperty.predicate`) and emits AD4M's
`parent: { id, predicate }` — the Tier-2 "adapter-rewrite". This also sidesteps AD4M's own broken
relation-name resolver (`resolveParentPredicate`) by handing it the predicate form directly, so **no
AD4M-repo change and no second backend needed**. `ad4mQueryAdapter` becomes a factory closing over the
perspective's model entries (`createAd4mQueryAdapter(getModels)`).
**Removed the vestigial AD4M fields from the internal `FlatQuery` dialect** — the earlier vocab sweep
neutralized the authoring surface + templates but deliberately left `FlatQuery` as the flat "ORM shape":
- `parent` — superseded by `scope`; the adapter still emits it internally (AD4M's contract).
- `perspective` — dead (`compileQuery` dropped it; the dataset handle is injected).
- `model` → `entity` — aligns the flat dialect with the neutral surface end-to-end
(compiler + renderer + adapter + harness + tests).
Swept the codebase: no `model:`/`perspective:`/`parent:` query keys remain (the surviving `perspective`
is the mutation-API option, a separate deferred write-path concern).
schema-shared 443 · schema-solid 34 · harness 7 · app-framework 60 green; tsc + lint clean; schemas
validate (0 errors); no raw AD4M predicate in any template query. ai-context regen follows.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Update the drill-down docs so AI-authored templates emit the neutral `scope: { anchor, via, anchorId }`
instead of the removed `parent: { id, relation/predicate }`; regenerate CLAUDE.md, copilot-instructions,
cursor rules, and schemaContext.ts.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
✅ Deploy Preview for coasys-we ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Inject the backend query adapter + finish query-plane neutrality (
feat/query-adapter-seam→dev)Summary
Two AD4M couplings remained in the query/render plane after the neutral-vocab work, and this branch
removes both. First, the renderer hardcoded the IR→backend lowering (
compileQuery → irToAd4mQueryimported directly in
SchemaRenderer), so schema-solid was silently AD4M-bound. Second, one templatestill embedded a raw AD4M protocol identifier in a query (
parent: { id, predicate: 'ad4m://has_child' }).This branch injects a real
QueryAdapterport that the host provides — the renderer routes everyquery through it and imports no backend — and neutralizes the drill-down to a backend-agnostic
scopethat the adapter resolves. Along the way it finishes the job the vocab sweep deliberately scoped to the
authoring surface: the internal
FlatQuerydialect is now neutral too (model→entity;parent/perspectiveremoved). After this, nomodel/perspective/parent/ad4m://remains anywhere in thequery layer — authored or internal.
A correction to the earlier framing is baked in:
irToAd4mQueryturned out to emit only the neutral flat$querydialect (no AD4M predicates), so it was misnamed — it's renamedirToFlatQueryand stays in@we/schema-shared. Nothing AD4M-specific "moved"; what's new in app-framework is the adapter objectthat composes the neutral pieces with AD4M's capability profile.
The existing AD4M app is unaffected: the IR path runs behind
seed.features.useQueryIR(now on), nativequeries are verified identical, and gaps fall back to the direct path.
Changes
The
QueryAdapterport + injectiondataSource.ts— newQueryAdapterinterface ({ capabilities, plan(ir), lower(ir) }) +$queryAdapteron
RendererDataBindings.SchemaRendererroutescompileQuery → plan → lowerthrough the injectedadapter; it no longer imports
irToAd4mQuery/ad4mCapabilities.ad4mAdapter.ts—createAd4mQueryAdapter(getModels)composesad4mCapabilities+planQuery+ theneutral
irToFlatQuery, and folds in AD4M's two conditional degradations no capability boolean canexpress (OR/AND/NOT disables the sort/pagination pushdown; a projection/relation-path sort needs a
limit). This makesplanQuerylive — it was dead code before.TemplateProviderinjects the adapter; the in-memory harness ships its own AD4M-freeQueryAdapter,so its flag-on test exercises the same renderer path.
irToAd4mQuery→irToFlatQuery(neutral; stays in schema-shared).Neutral drill-down (
scope) — Tier-2 adapter-rewritescope: { anchor, via, anchorId }added toQueryToken+zQuery;compileQuerypasses it to the IR's existing
Scope.ad4mQueryAdapter.lowerresolvesscope.viaonscope.anchorto the relation's RDF predicate (fromthe perspective's model manifest,
ModelManifestProperty.predicate) and emits AD4M'sparent: { id, predicate }. This sidesteps AD4M's brokenresolveParentPredicateby handing it the predicate formdirectly — so no
@coasys/ad4mchange and no second backend needed. The adapter is a factory closingover
getModelsfor exactly this lookup.FluxConversationsNestedListmigrated from the raw predicate toscope— noad4m://in the template.FlatQueryfully neutralmodel→entity(compiler + renderer + adapter + harness + tests).parentremoved (superseded byscope; the adapter still emits it internally as AD4M's contract).perspectiveremoved (dead —compileQuerydropped it; the dataset handle is injected).Seam-binding cleanups
$me: adamStore.me(mirroring$currentDataset), seam typed neutrally (() => unknown, not AD4M'sAgent); the hand-rolled identity-assembly function is gone.adamStore.currentPerspectivefallback removed from both renderer query paths (both now read theinjected
$currentDataset);queryTokentest fixtures migrated accordingly.AI-context — the drill-down docs teach
scopeinstead ofparent; regenerated CLAUDE.md /copilot-instructions / cursor rules /
schemaContext.ts.Tests — new
ad4mAdapter.test.ts(the two conditional degradations +scope→predicate resolution +surfaced-error path);
compileQuery/corpus updated forscopeandentity;queryTokenfixtures on$currentDataset.Known follow-ups (out of scope)
scopedrill-down (Flux nested conversations, flag on). The spike proved thepredicate is in the manifest, but the resolution runs against a synced dynamic Flux model, so only live
testing confirms it end-to-end. On failure the adapter throws a clear error (surfaced, not silent).
genuine capability gaps; the current gap-fallback is an AD4M-phase punt, not a general disposition (a
second backend forces compute-up or fail-loud).
(stores as Record<string, unknown>).$X); typingit against the declared
RendererDataBindings/QueryAdapteris a candidate next increment.adamStore$store/$actionbindings (store/shell decoupling, Phase 6) and the mutation-APIperspective/parentoptions (the write path) are the remaining AD4M coupling — both consciously deferred.Test plan
@we/schema-shared— 443 (queryCompiler incl.scope, dataSource port, engine, capabilities)@we/schema-solid— 34 (renderer over the injected adapter;$currentDatasetfixtures)QueryAdapter, flag on/off)@we/app-framework— 60 (+10 todo) —ad4mAdapterplan degradations +scoperesolution, corpustsc --noEmitclean — schema-shared, schema-solid, app-framework, harness, ai-contextpnpm --filter @we/schema-shared validate— 0 errors (template onscope)model:/perspective:/parent:query keys remain; no rawad4m://in any template queryNot committed — scratch PR description per repo convention.