Skip to content

refactor(core): collection query executor owns filter/sort/paging with copy-on-output#211

Merged
tonyketcham merged 2 commits into
mainfrom
stack/tonyketcham/feat/unified-watch-effort-graph/collection-query-executor-owns-filter-sort-paging--f0e0928c
Jul 18, 2026
Merged

refactor(core): collection query executor owns filter/sort/paging with copy-on-output#211
tonyketcham merged 2 commits into
mainfrom
stack/tonyketcham/feat/unified-watch-effort-graph/collection-query-executor-owns-filter-sort-paging--f0e0928c

Conversation

@tonyketcham

Copy link
Copy Markdown
Collaborator

Evaluating a filter argument previously constructed a whole nested
FlatbreadProvider — full config init + schema build + a GraphQL
subquery — before sifting, because filters may target
resolver-computed fields (_content.html, preknownSchemaFragments).
That subquery never received the live contentGraph, so filters could
evaluate stale data. Separately, every findById/findMany/all resolver
cloneDeep'd its whole collection per call, and resolveSortBy sorted in
place on the clone — the "never return shared mutable graph nodes"
invariant lived implicitly in a blunt per-call clone.

New packages/core/src/query-executor/ module: (Collection snapshot,
Relation lookup, query args) → immutable result Records. It owns
traversal, filter, sort, paging, ID-normalized lookup, and
copy-on-output. Derived-field filters evaluate against a compiled
registry: preknown fragment resolvers and config override resolvers
run as plain functions over the snapshot; relations traverse via
normalized-ID lookup; sift runs over the materialized view. No nested
provider, no GraphQL subquery, no stale reads.

  • findById/findMany/all in schema.ts are now 1–2 line adapters over
    one executor built per schema generation; per-call cloneDeep retired
    (ADR-0003 pairs this with incremental reindex).
  • generators/arguments.ts and resolvers/arguments.ts deleted (shallow
    single-caller mirrors; failed the deletion test). Arg declarations
    moved byte-identically to query-executor/graphql-arguments.ts.
  • One deliberate behavior extension: filters traversing a list
    relation are now existential (previously non-functional — the old
    path returned undefined for array traversal). Everything else is
    test-locked: relational scalar filters, ID normalization, paging
    slice quirk, DESC-without-sortBy, findMany order/dedupe/error text,
    validation snapshot messages.

Test plan: 17 new AVA executor unit tests (filter incl. derived
fields and fragment-parent parity, sort stability + input
non-mutation, paging, lookup normalization, result immutability,
list-relation existential regression) plus unchanged provider and
validation-snapshot suites. Full suite green: pnpm verify (271 AVA +
52 vitest).

Co-authored-by: Cursor [email protected]

Depends-On: #210

…h copy-on-output

Evaluating a `filter` argument previously constructed a whole nested
FlatbreadProvider — full config init + schema build + a GraphQL
subquery — before sifting, because filters may target
resolver-computed fields (_content.html, preknownSchemaFragments).
That subquery never received the live contentGraph, so filters could
evaluate stale data. Separately, every findById/findMany/all resolver
cloneDeep'd its whole collection per call, and resolveSortBy sorted in
place on the clone — the "never return shared mutable graph nodes"
invariant lived implicitly in a blunt per-call clone.

New packages/core/src/query-executor/ module: (Collection snapshot,
Relation lookup, query args) → immutable result Records. It owns
traversal, filter, sort, paging, ID-normalized lookup, and
copy-on-output. Derived-field filters evaluate against a compiled
registry: preknown fragment resolvers and config override resolvers
run as plain functions over the snapshot; relations traverse via
normalized-ID lookup; sift runs over the materialized view. No nested
provider, no GraphQL subquery, no stale reads.

- findById/findMany/all in schema.ts are now 1–2 line adapters over
  one executor built per schema generation; per-call cloneDeep retired
  (ADR-0003 pairs this with incremental reindex).
- generators/arguments.ts and resolvers/arguments.ts deleted (shallow
  single-caller mirrors; failed the deletion test). Arg declarations
  moved byte-identically to query-executor/graphql-arguments.ts.
- One deliberate behavior extension: filters traversing a list
  relation are now existential (previously non-functional — the old
  path returned undefined for array traversal). Everything else is
  test-locked: relational scalar filters, ID normalization, paging
  slice quirk, DESC-without-sortBy, findMany order/dedupe/error text,
  validation snapshot messages.

Test plan: 17 new AVA executor unit tests (filter incl. derived
fields and fragment-parent parity, sort stability + input
non-mutation, paging, lookup normalization, result immutability,
list-relation existential regression) plus unchanged provider and
validation-snapshot suites. Full suite green: pnpm verify (271 AVA +
52 vitest).

Co-authored-by: Cursor <[email protected]>
Change-Id: If0e0928c6b4a152a502361905bebe5cefcca9332
@tonyketcham

tonyketcham commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 docs(effort-graph): add Effort Graph glossary and ADRs 0001-0007 #205
2 feat(core,cli): unified watch with incremental reindex and live schema swap #206
3 feat(effort-graph): journaled semantic writer for the Effort Graph #207
4 refactor(config): loadConfig returns an initialized LoadedFlatbreadConfig #208
5 refactor(core): single Record-production module owns file→Record and path classification #209
6 refactor(effort-graph): per-mutate snapshot + Decision lifecycle module #210
7 refactor(core): collection query executor owns filter/sort/paging with copy-on-output #211 👈
8 refactor(core,cli): unified watch coordinator behind one seam, two adapters #212
9 refactor(core): per-build schema composer; delete the config-keyed schema cache #213
10 feat(effort-graph,flatbread): committed-generation bridge completes the ADR-0003/0004 contract #214
11 chore(core): drop unused graphql-compose-json and lru-cache dependencies #215
12 test: raise AVA concurrency now that the global-composer pin is obsolete #216
13 docs(agents): define orchestrator-executor operating model #217

@tonyketcham

Copy link
Copy Markdown
Collaborator Author

@Mergifyio queue

@mergify

mergify Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-07-18 08:36 UTC · Rule: owner-bypass · triggered by @tonyketcham with the @mergifyio queue command
  • Checks passed · in-place
  • Merged2026-07-18 09:05 UTC · at 6dfa849c253d42645ccecce821e98c29b9f43c8f

This pull request spent 29 minutes 34 seconds in the queue, including 4 minutes 3 seconds running CI.

Required conditions to merge
  • author = tonyketcham
  • check-success = build (20.x, ubuntu-latest)
  • check-success = build (22.x, ubuntu-latest)
  • check-success = integration-nextjs (20.x, macos-latest)
  • check-success = integration-nextjs (20.x, ubuntu-latest)
  • check-success = integration-nextjs (20.x, windows-latest)
  • check-success = integration-nextjs (22.x, macos-latest)
  • check-success = integration-nextjs (22.x, ubuntu-latest)
  • check-success = integration-nextjs (22.x, windows-latest)
  • check-success = integration-sveltekit (20.x, macos-latest)
  • check-success = integration-sveltekit (20.x, ubuntu-latest)
  • check-success = integration-sveltekit (20.x, windows-latest)
  • check-success = integration-sveltekit (22.x, macos-latest)
  • check-success = integration-sveltekit (22.x, ubuntu-latest)
  • check-success = integration-sveltekit (22.x, windows-latest)
  • check-success = lint (20.x, ubuntu-latest)
  • check-success = lint (22.x, ubuntu-latest)
  • check-success = test (20.x, ubuntu-latest)
  • check-success = test (22.x, ubuntu-latest)
  • depends-on = FlatbreadLabs/flatbread#210 [⛓️ refactor(effort-graph): per-mutate snapshot + Decision lifecycle module #210]

@mergify mergify Bot added the queued label Jul 18, 2026
Base automatically changed from stack/tonyketcham/feat/unified-watch-effort-graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2 to main July 18, 2026 09:00
…graph/collection-query-executor-owns-filter-sort-paging--f0e0928c
@tonyketcham
tonyketcham merged commit 044023f into main Jul 18, 2026
19 checks passed
@tonyketcham
tonyketcham deleted the stack/tonyketcham/feat/unified-watch-effort-graph/collection-query-executor-owns-filter-sort-paging--f0e0928c branch July 18, 2026 09:05
@mergify mergify Bot removed the queued label Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant