Repository navigation
fix(codegen-ts): keep reverse @via view columns and refuse value-object aggregates - #561
Merged
Merged
Conversation
The meta init agent guide gains one rule: a grouped total, dashboard number or read-only report view is an object.report over dimension.* / measure.*, with a hand-written @SQL view body as the last resort. The metaobjects-authoring routing table sends report, dashboard, totals, GROUP BY and per-month wording to declaring-reports.md first, and the @SQL sections of read-views-and-projections.md and sources.md point an aggregating body at it and say what @SQL is for.
…@via hop An origin @via hop that walks an identity.reference backwards (#560) loaded, but the projection view emitter resolved hops only through relationships and forward references and silently dropped the aggregate and first columns from CREATE VIEW. It now falls back to the same reverse-hop lookup the loader uses and joins from the reference holder. Gated by a Postgres test that migrates, inserts and reads the view.
…bject field @Of naming a field.object (flattened or jsonb) loaded and emitted SUM(<field>), a column that does not exist. All four loaders (Kotlin through Java) now refuse it with ERR_INVALID_ORIGIN: a value object is several columns or a document, and summing a Money-shaped value would add amounts in different currencies. count is unaffected. Gated by the new error-origin-aggregate-value-object conformance fixture.
Fixtures, the metamodel spec and its generated per-port copies, the generated reference site and the showcase payload.
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.
What Changed
@viahop columns.codegen-tsextract-view-spec.tsnow resolves an origin@viathat walks anidentity.referencebackwards (for exampleProject.timeEntries). Previouslymeta migratedropped theorigin.aggregate/origin.firstcolumns fromCREATE VIEW. The emitter joins from the holder of the reference. A Postgres integration test (origin-reverse-hop-view-pg.test.ts) creates the view and reads it.origin.aggregaterefusessum/avg/min/maxover a value-object field. The loader in TypeScript, Java, C#, and Python now raisesERR_INVALID_ORIGINwhen@ofnames afield.objector an@objectReffield. The message tells the author to model the amount as a scalar field. Theerror-origin-aggregate-value-objectconformance fixture gates this across ports.countis unchanged.object.report. Themetaobjects-authoringskill, its reference docs, thealways-ontemplate, and the generated agent-context fixtures now point grouped totals, dashboard numbers, and read-only report views atobject.report(declaring-reports.md) before the@sqlescape.docs/features/source-kinds.mddocuments the scalar-@ofrule. CHANGELOG records both fixes under Unreleased.Note: this branch does not change the reporting vocabulary itself. It fixes two correctness gaps and changes what agents are told to reach for.
Validation
What the reporting vocabulary covers (smoke-test reports, Postgres)
The three reports from the large-application smoke test, rewritten with
object.report/dimension.*/measure.*/@spineand loaded and migrated on Postgres. This PR adds no vocabulary; the gaps are findings.object.reportfromPayment:clientId/clientNameviaPayment.invoice.client,receivedOn:month, acountmeasure. Gap: the amount is a flattenedMoneyvalue object, and a dimension or measure@ofcannot name a value-object member (Payment.amount.currencyisERR_INVALID_DIMENSION), so neither summing the amount nor grouping by its currency is possible. Workaround: model the amount and currency as scalar fields.object.reportfromTimeEntrygrouped by member andworkDate:month, summing total and billable hours, with weekly capacity carried as a dimension. Gaps: there is no calendar-month x member spine.@spinetakes rows from one to-one dimension entity, so "a zero row for every month in which anyone logged time" needs a cross product it cannot express. Utilization % needs measure-by-dimension arithmetic (hours / capacity x weeks), andmeasure.ratioonly divides two measures.@spine: TimeEntry.project, which gives zero rows for idle projects. Gaps: cost = hours x member cost rate needs a per-row product measure. Invoiced and paid areMoneysums, the value-object gap again. Budget burn reads an optional jsonbMoney, and no measure reaches into a jsonb value object. Combining measures from three fact tables (time entries, invoices, payments) does not fit a report with a single@from.A fresh agent build session on packages built from this branch read
declaring-reports.mdand wrote its first report as anobject.report. It fell back to@sqlonly after the Money value-objectERR_INVALID_DIMENSIONabove.Risk Assessment
✅ Low: The change is small. The docs route report wording to object.report, and the generated agent-context fixtures match the template and skill edits. The reverse-hop view emitter uses the same order as the loader (relationship, then forward reference, then a unique reverse reference) and joins from the reference holder. The new value-object aggregate refusal is the same check in all four loader ports, uses resolving accessors, and only refuses input that produced invalid SQL before.
Testing
The earlier timeout is resolved: the server suite was rerun per package, every package exited 0, and
integration-testsran file by file, all 65 files exit 0 (api-contract report/generated/projection/m2m, report views on PG, MySQL and SQLite, and the origin reverse-hop view). All other server packages report 0 failures.forgeandtest-generatorscontain no test files (bun exits 1 with "0 test files matching"), so they have nothing to run. Live checks on disposable projects confirmed the value-object aggregate guard (the Java-side@aggguard in fe55b6d was not driven here). The docs-routing scenario has live evidence from the fresh agent session: it reached the reports reference and wrote anobject.reportfirst. It then fell back to@sqlon an unrelated Money value-object@ofgap (ERR_INVALID_DIMENSION), which is outside this change. No browser or UI surface exists, so no screenshots. Evidence logs are in the evidence directory. The worktree is clean and no background jobs remain.metaon a disposable project refuses sum/avg/max aggregate over a field.object field with ERR_INVALID_ORIGIN, while count and scalar aggregates still loadobject.reportdocs and writes a report asobject.reportfirstmeta init). Fell back to@sqlonly on an unrelated Money value-object@ofgap (ERR_INVALID_DIMENSION).Evidence: per-package regression summary
Evidence: integration-tests per-file results
Evidence: value-object aggregate guard CLI transcript
Evidence: reverse-hop view on Postgres
Pipeline
Updates from git push no-mistakes
✅ **Intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
🔧 **Test** - 2 issues found → no changes applied ✅
server/typescript- Full server regression suite (bun testin server/typescript) timed out at 1500s with no result. Regression is unproven. Rerun per package or with a longer timeout before the PR gate.scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsbun installat repo root (dependency materialize)bun server/typescript/packages/cli/bin/meta.ts verify --codegenon disposable project with fixtureerror-origin-aggregate-value-objectinput (sum over field.object Invoice.total)Variants of the same project: @agg avg and max over value-object field; @agg count over value-object field; @agg sum over new scalar field Invoice.amountbun test test/origin-reverse-hop-view-pg.test.tsin server/typescript/packages/integration-tests (Testcontainers Postgres: migrate, insert, read view)bun testin server/typescript (full server regression suite) - killed at timeout 1500s, no resultNot run: pre-fix reproduction of reverse-hop view bug (would require reverting product code)🔧 No changes applied.
✅ Re-checked - no issues remain.
metaon a disposable project refuses sum/avg/max aggregate over a field.object field with ERR_INVALID_ORIGIN, while count and scalar aggregates still loadobject.reportdocs and writes a report asobject.reportfirstmeta init). Fell back to@sqlonly on an unrelated Money value-object@ofgap (ERR_INVALID_DIMENSION).scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsscripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains(baseline, passed)metaCLI on disposable projects:origin.aggregatesum/avg/max over a field.object field fails with ERR_INVALID_ORIGIN, count and scalar aggregates load (loader-variants.log)origin-reverse-hop-view-pg.test.tsagainst Testcontainers Postgres (reverse identity.reference @via hop view column)Server regression per package,bun testfrom inside each package: metadata, cli, codegen-ts, codegen-ts-react, codegen-ts-tanstack, codegen-ts-angular, conformance, docs-site, migrate-ts, render, runtime-ts, sdk, ai-runtimeintegration-tests: each of the ~65test/*.test.tsfiles run separately (Postgres, MySQL and SQLite containers/engines), all exit 0Fresh agent build session on packages built from this branch (1.3.1-rrd.2, freshmeta init): opened declaring-reports.md, saw the routing line, wrote its first report asobject.report✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.