What to build
The remainder of superseded epic #225, downstream of the materialization-plan substrate (#277): aggregations (sum/avg/min/max, GROUP BY) materializing into projected records, plus the two projection extensions deliberately deferred from #277 because they share one design — output aliases and traversed projection (select reaching across a relation, INNER-narrowing per ADR-0006).
Design note carried from the 2026-07-10 session: aliases name output record fields, not join identities (no conflict with ADR-0006's no-alias rule for joins). Leading surface candidate: the dict-returning lambda — select(lambda t: {"id": t.id, "account_name": t.account.name, "total": ...}) — keys are field names; one lambda, no new proxy API, generalizes to aggregate expressions. t.account.id as "x" is a Python SyntaxError and cannot be a surface. The record plan shape already reserves name-vs-column separation, per-field path, and expr (ADR-0007), so this issue extends the section without reshaping it. Unaliased traversed projection must resolve name collisions (t.id vs t.account.id) with a build-time error pointing at the alias form.
Acceptance criteria
Blocked by
What to build
The remainder of superseded epic #225, downstream of the materialization-plan substrate (#277): aggregations (
sum/avg/min/max,GROUP BY) materializing into projected records, plus the two projection extensions deliberately deferred from #277 because they share one design — output aliases and traversed projection (selectreaching across a relation, INNER-narrowing per ADR-0006).Design note carried from the 2026-07-10 session: aliases name output record fields, not join identities (no conflict with ADR-0006's no-alias rule for joins). Leading surface candidate: the dict-returning lambda —
select(lambda t: {"id": t.id, "account_name": t.account.name, "total": ...})— keys are field names; one lambda, no new proxy API, generalizes to aggregate expressions.t.account.id as "x"is a PythonSyntaxErrorand cannot be a surface. The record plan shape already reservesname-vs-columnseparation, per-fieldpath, andexpr(ADR-0007), so this issue extends the section without reshaping it. Unaliased traversed projection must resolve name collisions (t.idvst.account.id) with a build-time error pointing at the alias form.Acceptance criteria
Rows[Row]) with user-named fields, both backends;GROUP BYcomposes withwhere(traversal included).expr/pathfield shapes exercised end-to-end; golden vectors + Rust round-trip pins; static fixtures extended.Blocked by
featPartial selects 2/4: select(lambda) → Rows[Row] — the tracer bullet #279