feat(plugin): an optional planFindPage door — an index that can plan a find answers it in one call
Some checks failed
CI / Node 22 (push) Successful in 12m34s
CI / Node 24 (push) Successful in 12m21s
CI / Integration + conformance (Node 22) (push) Failing after 17m4s
CI / Bun (latest) (push) Successful in 12m19s

The provider's read doors each serve one stage, so a find that consults three of
them crosses into the index three times and marshals a result set at every
crossing: a filter matching a hundred thousand rows builds a hundred thousand id
strings to return a page of twenty-five. An index able to decide the stage order
itself can answer the page in one call and build ids only for the page.

planFindPage is optional and additive, in the shape filterIdsWithin and
getIdSetForFilter already set. The hook sits above the branch selection, because
the branches are what decide stage order per call site and an index that plans
has to be asked before that choice is made. Absent — as it is on this engine's
own index — every find is served by the stage doors exactly as before, which is
what keeps this engine the ordering oracle for any index that implements one.

The contract the door must keep, written where an implementer will read it:
identical rows in identical order to what the stage doors would produce; the
graph-first law (neighbours are the candidate universe, the filter runs over
those ids, orderBy sorts the whole set, the page is cut last); null returned
BEFORE any work rather than instead of an answer; and emptyAt naming the stage
that produced an empty page, so the serving law is applied to the right index —
an empty graph answer is re-verified against the adjacency before it is
believed, and a filter-empty is not.

Pinned in tests/integration/find-planner-door.test.ts: absent changes nothing;
present it is asked first with normalized params, the hidden ids and the graph
provider; its page is used and hydrated in its order; a declining door leaves
the result identical to the no-door path; and the two emptyAt branches verify
the adjacency, or correctly do not.
This commit is contained in:
David Snelling 2026-09-01 13:11:16 -07:00
parent d5147ed608
commit 4d5f823f47
4 changed files with 226 additions and 2 deletions

View file

@ -7344,6 +7344,47 @@ export class Brainy<T = any> implements BrainyInterface<T> {
await this.verifyMetadataLive()
}
// PLANNED FIND (optional provider door, `MetadataIndexProvider.planFindPage`).
//
// The stage doors below each serve one stage, so a find that consults
// three of them crosses into the index three times and marshals a result
// set at every crossing — a filter matching a hundred thousand rows
// builds a hundred thousand id strings to return a page of twenty-five.
// An index that can decide the stage order itself answers the page in one
// call and materializes ids only for the page.
//
// The hook sits ABOVE the branch selection because the branches are what
// decide stage order per call site; an index that plans has to be asked
// before that choice is made, not inside one of its arms.
//
// Optional and additive: a provider without the door, and any shape the
// door hands back, take exactly the path they always took. `null` is a
// routing decision the door must make BEFORE doing any work — never a
// partial answer. Every guard above still ran (readiness, the migration
// gate, the where-clause validation, the metadata cold-read guard), and
// the serving law is applied here on the way out: an empty answer is
// re-verified against the index that produced it before it is believed.
const planningIndex = this.metadataIndex as unknown as MetadataIndexProvider
if (typeof planningIndex.planFindPage === 'function') {
const planned = await planningIndex.planFindPage(params, [...hiddenIds], this.graphIndex)
if (planned !== null && planned !== undefined) {
if (planned.ids.length === 0) {
// A cold adjacency can report a size yet hold no edges, so an empty
// graph answer is not truth until the adjacency verifies live. A
// genuinely edgeless anchor verifies and the empty result stands.
if (planned.emptyAt === 'graph') await this.verifyGraphAdjacencyLive()
return []
}
const plannedEntities = await this.batchGet(planned.ids)
const plannedResults: Result<T>[] = []
for (const id of planned.ids) {
const entity = plannedEntities.get(id)
if (entity) plannedResults.push(this.createResult(id, 1.0, entity))
}
return plannedResults
}
}
// Handle metadata-only queries (no vector search needed)
if (!hasVectorSearchCriteria && !hasGraphCriteria && hasFilterCriteria) {
// Build filter for metadata index