Skip to content

feat(marquee): Phase 2 joins + aggregation via the named-operation port - #52

Closed
2bTwist wants to merge 1 commit into
app/marquee-phase1from
app/marquee-phase2
Closed

2bTwist wants to merge 1 commit into
app/marquee-phase1from
app/marquee-phase2

Conversation

@2bTwist

@2bTwist 2bTwist commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Stacked on #51 (Phase 1). Phase 2 is the first "SDK gap" phase, and the finding is that there is no core gap: joins and aggregation ride the existing named-operation port (store.run), whose calling convention is portable but whose implementation is per-backend. That is exactly the seam the core reserves for genuine divergence. The capability flags declare it; the UI surfaces it.

Schema (both backends)

people + credits make cast/director a real many-to-many relation. Supabase adds FK constraints (so PostgREST can embed) and a genre_counts GROUP BY view.

Named queries: same shape, divergent implementation

  • movieCredits (the JOIN): Supabase = a server-side PostgREST embed over the FKs; Convex (convex/marquee.ts) + memory = follow refs by hand.
  • genreCounts (the AGGREGATION): Supabase = a GROUP BY view; Convex + memory = scan and tally (the cost the aggregations: false flag warns about, exercised for real at Phase 5 scale).

makeBackend wires the typed MarqueeSchema with per-backend query impls (src/lib/enrich.ts, env-free so tests build typed backends directly). The catalog functions became generic over the schema so the bare test backends still fit.

UI (the thesis, made visible)

MovieDetail shows the real joined cast (director + billed actors with characters); the catalog shows per-genre counts. Both render an understated caption declaring HOW the data loaded on the active backend: "Cast joined server-side" vs "assembled by following references"; "Counted with a SQL view" vs "by scanning".

Verification

  • test/enrich.live.test.ts: 12 tests (join order + name resolution, empty cast, aggregation, capability declaration) green on memory + Supabase + Convex. Catalog gate still 20/20. Full suite 32/32.
  • Playwright UI smoke confirmed the cast join and the genre-stats strip with the correct divergence captions on both live backends, 0 console errors.

No packages/* change (the named-op port already covered it), so no changeset. avg-rating aggregation defers to Phase 3 (reviews need auth); Phase 2's aggregation is genre counts over existing data.

The first "SDK gap" phase. Finding: joins and aggregation do NOT need a core
change. They ride the existing named-operation port (`store.run`), whose calling
convention is portable but whose implementation is per-backend, which is exactly
the seam the core reserves for where backends genuinely diverge. The capability
flags (serverSideJoins / aggregations) declare the divergence; the UI surfaces it.

Schema (both backends): people + credits make cast/director a real many-to-many
relation; Supabase adds FK constraints (for PostgREST embeds) and a genre_counts
SQL view.

Named queries, same shapes, divergent implementations:
- movieCredits (the JOIN): Supabase does a server-side PostgREST embed over the
  FKs; Convex (convex/marquee.ts) and memory follow refs by hand.
- genreCounts (the AGGREGATION): Supabase reads a GROUP BY view; Convex and memory
  scan and tally (the cost the `aggregations: false` flag warns about, and which
  Phase 5 scale will exercise).

makeBackend wires the typed MarqueeSchema with per-backend query impls (src/lib/
enrich.ts, kept env-free so tests build typed backends directly). The catalog
functions are generic over the schema so the bare test backends still fit.

UI: MovieDetail shows the real joined cast (director + billed actors with
characters); the catalog shows per-genre counts. Both render an understated
caption declaring HOW the data loaded on the active backend ("Cast joined
server-side" vs "assembled by following references"; "Counted with a SQL view"
vs "by scanning") -- the thin-honest-waist thesis made visible.

Verification: enrich.live.test.ts (12 tests: join order + name resolution, empty
cast, aggregation, capability declaration) green on memory + Supabase + Convex;
catalog gate still 20/20. A Playwright UI smoke confirmed the cast join and the
genre-stats strip with the correct divergence captions on both live backends.
avg-rating aggregation defers to Phase 3 (reviews need auth); Phase 2's
aggregation is genre counts over existing data.
@2bTwist
2bTwist deleted the branch app/marquee-phase1 June 20, 2026 21:33
@2bTwist 2bTwist closed this Jun 20, 2026
@2bTwist
2bTwist deleted the app/marquee-phase2 branch June 20, 2026 21:42
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