feat(intent): report structure moves to a generated .view - the .report keeps a thin parameterized SELECT (#6938) - #6941
Merged
Merged
Conversation
…ted .view - the .report keeps a thin parameterized SELECT (#6938) Split the generated report query at the parameter boundary: everything that does not depend on runtime input is emitted as a generated .view artifact next to the .report, and the query the generated repository carries binds only the named parameters - which a database view cannot hold, so the boundary is exactly there. - kind: statement -> <REPORT>_LINES: one parameter-free head arm per declared line (a selector matching nothing still renders, with 0) plus one SELECT DISTINCT arm per flattened term, enumerating account codes from the nomenclature the account path points at. The thin query LEFT JOINs the view to the ACCOUNT_BALANCES CTE (windowed sums, filter/scope, authored parameters) and decodes each row's measure. - correspondence: -> <REPORT>_CORRESPONDENCE: allocated line-level rows with the entry date as a plain column; the counter-side self-join, the proportional allocation and the static filter/scope live in the view. The .report reads the view as its base table through the same buildQuery as every plain report, so it now round-trips in the report editor's visual builder. A translated dimension exposes its base value plus a language key, and the .report re-applies the :language overlay; an authored parameter's static left side is exposed as a PARAM_* column. - The views ride the existing lifecycle: ViewsSynchronizer provisions them per tenant after the tables (SynchronizersOrder), and .view is an intent-owned extension, so they regenerate and scrub with the report. Plain aggregations and plain kind: balance are unchanged. - Belt-and-braces for whatever SQL still reaches generated Java: the repository QUERY is joined from per-line literals instead of one single-line literal - the #6936 trigger shape stops being emitted at all, from either end. StatementReportSqlTest and CorrespondenceReconciliationTest install the emitted views on H2 and run the thin queries, asserting figures (incl. the matches-nothing line and the buckets-reconcile property); ReportEditorRoundTripTest proves the correspondence round-trip; IntentEmissionCoverageIT seeds a little ledger and reads the statement figures over REST from the published app - the whole publish -> ViewsSynchronizer -> thin-query lifecycle. Fixes #6938 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rated .view, not in the .report query (#6938) The smoke suite's IntentEngineIT still pinned the pre-split emission: the correspondence self-join, the allocation and the statement line arms inside the .report query. Those now live in the generated OrderStatementLines.view / OrderItemCorrespondenceCorrespondence.view artifacts - the assertions move there, the thin-query side gains its own checks (view as base table, windowed ENTRY_DATE/ALLOCATED_* sums, LINE_ORDINAL ordering, no re-shipped structure), and the two .view files are pinned in the generate response's written list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
The smoke-tests failure was |
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.
Fixes #6938.
The intent report generators baked the whole query into the generated repository as one single-line Java constant - a 16-line
kind: statement= a 10.2 KB literal, acorrespondence:balance = 6.3 KB. That is the shape that overflowedJavaSourceParserand killed synchronization instance-wide (#6936), it hid the query from the Database perspective, and it re-shipped fully static SQL inside every consumer.What changed
Every heavy ledger query is split at the parameter boundary (the design agreed in the issue): the parameter-free structure becomes a generated
.viewartifact next to the.report; the.reportkeeps a thinSELECTbinding the named parameters - which a database view cannot hold, so the boundary sits exactly there, never a parameterized-view emulation.kind: statement→<REPORT>_LINES(<Report>Lines.view). Per declared line: one parameter-free HEAD arm (TERM_SIGN 0, NULL measure/account - what keeps a line whose selector matches nothing rendering with 0, and the count wrap seeing every line) plus oneSELECT DISTINCTarm per flattened term, classifying the account codes its selector matches, enumerated from the nomenclature theaccount:path points at (DISTINCT so a duplicated code cannot double a balance). The thin query LEFT JOINs the view to theWITH "ACCOUNT_BALANCES"CTE - which keeps the windowed sums,filter:/scope:and the authored parameters - and decodes each row'sTERM_MEASUREthrough a CASE carrying only the measures the report uses.correspondence:→<REPORT>_CORRESPONDENCE(<Report>Correspondence.view) of allocated line-level rows: the counter-side self-join, the proportional allocation with its correlated document totals, and the staticfilter:+ lifecycle scope live in the view, with the entry date as a plainENTRY_DATEcolumn. The.reportsetstable= the view and is assembled by the samebuildQueryas every plain report - so a correspondence report now round-trips in the report editor's visual builder (it reads the view like any table). A translated dimension exposes its base value plus a<DIM>_LANGUAGE_KEYcolumn and the.reportre-applies the:languageoverlay over them; an authored parameter's whole static left side (CAST + COALESCE included) is exposed as aPARAM_<NAME>column and compared in a plain builder-owned condition row.ViewsSynchronizerprovisions them per tenant,SynchronizersOrder(SCHEMA → TABLE → VIEW) already orders them after the tables they read, and.viewis an intent-owned extension - they regenerate with the report and are scrubbed with it. The unresolvable-document correspondence case falls back to the flat emission (which warns), and plain aggregations / plainkind: balanceare deliberately unchanged - small literals, and the flat balance emission is what the editor's builder owns today.ModelTemplateAdapterspre-escapes the query as per-line literals and the repository template joins them withString.join("\n", ...). Complementary to fix(engine-java): one long string literal must not turn synchronization off platform-wide (#6936) #6939 (the parser-side fix): this stops the generators producing the trigger input; that one fixes the parser and contains the blast radius.Tests
StatementReportSqlTest/CorrespondenceReconciliationTestinstall the emitted views on H2 and RUN the thin queries, asserting figures, never strings - all pre-existing assertions pass verbatim (netting-before-sum, flattened subtotals, allocation reconciling with the plain balance, the count/LIMIT wraps), plus a new one: a line whose selector matches no account still renders, with 0.ReportEditorRoundTripTest: the correspondence document round-trips over the view; the statement stays deliberately free-style.IntentEmissionCoverageIT(the outermost layer): the fixture gains a statement report over a seeded ledger - asserts the.view/.report/repository tokens, then reads the statement's figures over REST from the published app, proving the whole publish → ViewsSynchronizer → thin-query lifecycle including the netting arithmetic (100 debit vs 40 credit → 60).ModelGenerationITcovers the per-line-literal template rendering (idempotent, no unresolved placeholders).Verified locally: full
engine-intentunit suite (926 tests),ide-template(85),IntentEmissionCoverageIT,ModelGenerationIT,formatter:validateon all changed modules.No authoring change - the intent DSL is untouched, so no specification change rides along.
🤖 Generated with Claude Code