Repository navigation
Commit bad5a59
authored
feat: FR-044 Plan 1 reporting vocabulary with loader validation in all five ports (metamodel 1.1) (#397)
* feat(metamodel): register the FR-044 reporting vocabulary in TypeScript (metamodel 1.1)
Registers dimension.attribute, dimension.time, measure.aggregate, measure.ratio,
segment.filter and object.report (the ADR-0023 list agreed for FR-044), with their
node classes and the report accessors Plan 2's lowering will use. object.entity
gains dimension/measure/segment child wildcards; object.report inherits the
object.base children like object.projection. measure.derived is deliberately absent.
metamodelVersion moves 1.0 -> 1.1; the version check classifies all nine changes
ADDITIVE. The registry manifests of the other four ports are regenerated for the
byte-match, but those ports do not register the vocabulary yet, so their
registry-conformance is red until the fan-out lands.
Also updates the derived artifacts the vocabulary changes: metamodel docs, site
payload and reference, the registry coverage baseline (the six new subtypes are
untested until the conformance fixtures land), the BASE_TYPES count, and the
project's own requirements ledger with the harness regenerated from it.
* feat(metadata): validate the FR-044 reporting vocabulary at load (TypeScript)
* fix(metadata): FR-044 reporting validation — keep each inheritor's error
* test(conformance): FR-044 reporting vocabulary fixtures (1 positive, inheritance, 22 error cases)
* test(conformance): FR-044 fixtures — make non-temporal F2 fixture reach F2, de-collide declared field, map fixtures to a doc
* feat(metadata): relative-date filter values, legal on reporting hosts only (TypeScript)
A { now: "<ISO duration>" } operand survives filter desugaring as an opaque
value. The operator-less shorthand { field: { now: ... } } is read as the value
of an implicit eq (now is not an operator), so F2 refuses it by name instead of
S1 reporting a bad op. F1's search for a relative value on a non-reporting host
now walks operand values only, so a field named now is no longer misread.
The TS view lowering throws on a { now } operand that bypassed the loader.
* feat(csharp): register and validate the FR-044 reporting vocabulary
Registers dimension.attribute/time, measure.aggregate/ratio, segment.filter
and object.report in the C# port, and ports validateReporting rule-for-rule
from the TypeScript reference with the same message text.
- SpecMetamodel/reporting.json (new) and object.json are byte copies of the
root spec; SpecMetamodelReader lists 19 files. Attr schemas declare only
value type, array-ness, requiredness and allowedValues; descriptions and
the structural child graph come from the embedded JSON.
- ValidationPasses.ValidateReporting (new partial file) runs after the
projection-filter pass: D1-D4, M1-M6, S1, R1-R7, F1/F2. One broken rule
is one error; inherited members are validated per inheritor with the TS
ErrorSink dedupe. F1 classifies filter attrs through the registry, since
C# keeps inline attrs as values rather than typed attr nodes.
- Filter desugar: a shorthand { f: { now } } becomes { eq: { now } }; an
explicit-op relative value stays an opaque operand.
- Four new error codes: ERR_INVALID_DIMENSION, ERR_INVALID_MEASURE,
ERR_INVALID_REPORT, ERR_REPORT_FOREIGN_MEASURE.
- ReportingAccessorsTests and ReportingValidationTests (exact message text,
inherited-member dedupe, relative-date desugar, F1 operand-only walk).
All 24 FR-044 conformance fixtures, registry-conformance and spec-embed
gates pass; codegen output for the positive fixture is byte-identical with
and without the reporting vocabulary.
* feat(java): register and validate the FR-044 reporting vocabulary
Registers dimension.attribute, dimension.time, measure.aggregate,
measure.ratio and segment.filter (new reporting-types provider) and
object.report (beside object.projection). Structural children and
descriptions come from spec/metamodel/reporting.json and object.json, now
embedded (19 spec files). Kotlin composes from the same JVM provider set and
needs no code; the facade carries no requirement accessors, so none are added
for reporting.
ReportingValidation ports the TS validateReporting pass rule for rule
(D1-D4, M1-M6, S1, R1-R7, F1/F2) with the same message text, the same
pass-1/pass-2 inheritance dedupe and the same no-cascade short-circuits.
FilterAttribute desugars an operator-less { now } clause to eq, as the TS
desugarClause does.
Two Java-specific findings:
1. The registry manifest decided array-ness by attr NAME alone. FR-044 makes
@Of an array on measure.aggregate while it stays scalar on dimension.* and
origin.*, so every @Of in the manifest flipped to isArray: true. The
emitter now resolves the array constraint per type (own, inherited, then
common attr).
2. .withEnum is decorative on attr children in this port, so the closed
@agg and @Grains sets the TS registry enforces generically are checked in
the pass as ERR_BAD_ATTR_VALUE.
Tests: all 24 FR-044 conformance fixtures and registry-conformance pass. A
new test asserts every error fixture yields exactly one finding with the TS
loader's code and message. Disabling the pass fails exactly the 22 error
fixtures.
* fix(java): FR-044 reporting — match TS message text for closed-set values
- ERR_BAD_ATTR_VALUE for @agg / @Grains now uses the short node label
(type.subType 'name'), byte-identical to the TS attr-schema check; the
tests assert the full message, one error per bad array element.
- A bare string authored for an array attr is ONE element, commas included,
as TS normalizeStringArrayAttr wraps it. The canonical parser's
comma-delimited array path split it (and left stray quotes on the
pieces); it is now re-set whole. ReportingAttrs no longer splits a bare
string either. A JSON-array value still yields its items.
- Drop the four unused FR-044 ErrorMessageConstants entries, which had
orphaned the ERR_VALIDATION_FORMAT doc comment.
- ValidationPhase.run javadoc lists validateReporting in call order.
* feat(python): register and validate the FR-044 reporting vocabulary
Registers dimension.attribute, dimension.time, measure.aggregate,
measure.ratio, segment.filter and object.report in the Python port, with
the MetaDimension / MetaMeasure / MetaSegment node classes and the report
accessors. The spec copies (reporting.json, object.json) are byte copies of
spec/metamodel/, so the registry manifest byte-matches expected-registry.json.
validate_reporting ports the TypeScript loader pass rule for rule (D1-D4,
M1-M6, S1, R1-R7, F1/F2) with the same message text, the same short-circuits,
the same error sources (R4/R5 on the declared child) and the same pass-2
inheritance dedupe. Every attr read resolves (get_meta_attr): Python attr() is
own-only (ADR-0039); the two own reads (R4, the F1 tree walk) say why.
The filter desugar now matches the TypeScript reference: and/or arrays
recurse (they were collapsed into an `in` clause), an object clause carrying
a `now` key is the value of an implicit eq, and any other object clause is
an op map kept as-is (it was wrapped in eq, which hid a bad op).
All 24 FR-044 conformance fixtures pass; a TS-vs-Python probe over 96 models
produced byte-identical codes and messages.
* test: FR-044 reporting nodes are inert in every generator and in migrate — fixtures
fixtures/codegen-noop/reporting/{with,without}: a model pair that differs only by the
reporting vocabulary (with/ adds a report declaring a read-only source.rdb @kind: view,
the shape that leaked in C#). Shared by the five per-port inert tests.
meta.fitness.json: Program gains one dimension.time, one measure.aggregate and one
segment.filter, so every port's persistence corpus and codegen-compile gate load the
vocabulary. No object.report there (no lowering until Plan 2). schema.postgres.sql
regenerated with bun run gen:schema: byte-identical.
* fix(codegen-ts): object.report emits nothing until its lowering lands (FR-044 Plan 1)
runGen dropped nothing for a report, so entity, names, barrel, queries, routes,
routes-hono and hooks each emitted files for the three reports in the inert model.
Reports now leave the entity set at runGen's single selection choke point (isReport in
source-detect.ts, keyed on OBJECT_SUBTYPE_REPORT).
reporting-inert.test.ts compares every catalog generator, alone and as one suite, with
and without the reporting nodes, and checks that migrate-ts builds the same postgres
schema and diff() proposes no change.
* fix(codegen-ts): view lowering reads { now } as a value, matching the attr.filter desugar
extract-view-spec's local desugarClause claimed to mirror metadata's but read an object
with a now key as an op map, so a pre-desugar shorthand { f: { now: "-P7D" } } lowered
as op "now" and assertNoRelativeDate never fired. It now applies the same now-key rule
(-> eq). Tests cover the shorthand on the row-scope @filter and both forms on the
origin.aggregate @filter (resolveAggregateFilter); the test's own-attr mutation names
its sanctioned case.
* fix(csharp): object.report emits nothing until its lowering lands (FR-044 Plan 1)
A report declaring a read-only source.rdb @kind: view emitted an empty entity, names, a
filter allowlist, a GET-only route and a keyless DbSet with ToView. CodegenRunner.Run now
drops reports from the entity set every generator reads (MetaObject.IsReport()).
ReportingInertTests runs every registered generator, alone and together.
* fix(java,kotlin): object.report emits nothing until its lowering lands (FR-044 Plan 1)
Java: the entity generator emitted a class and an extractor per report (and bound them in
GeneratedObjectClassBindingProvider); names emitted StoreTotalsNames for the view-backed
report. GeneratorUtil.getFilteredMetaData, which every direct per-object generator selects
through, now drops reports via the shared GeneratorUtil.isReport predicate; the names
generator, which walks the loader itself, uses the same predicate.
Kotlin: only names leaked (StoreTotalsNames.kt); same predicate.
ReportingInertTest in codegen-spring and codegen-kotlin runs every registry generator
alone, plus the coherent combined suites.
* fix(python): object.report emits nothing until its lowering lands (FR-044 Plan 1)
entity wrote an empty BaseModel module per report; filter-allowlist, names and routes
emitted for the view-backed report. run_gen now drops reports from the entity set every
generator reads. test_reporting_inert.py runs every registered generator alone and the
runnable ones together.
* docs: list fixtures/codegen-noop as the 25th shared corpus
The FR-044 inert model pair is run by all five ports, so the site-payload gate counts it
as a corpus. CONFORMANCE.md gains its matrix row (a template-quality check, not a core
promise), and the corpus count moves 24 -> 25 there, in AGENTS.md and in the site payload.
* fix(docs): object.report gets no docs page until its lowering lands (FR-044 Plan 1)
A report's fields are derived by its lowering, so a docs page for one today shows none of
them. Every docs surface now skips it, with the FR-044 Plan 1 comment:
- TS meta docs: docsFile (model pages + README index), api-model (api surface), and the
docs-site link graph that every site page, index and nav list is built from. The site's
coverage audit also skips the reporting vocabulary, which it renders nothing for by
design until the lowering lands, instead of reporting it as a rendering gap.
- C#, Java, Python api-docs builders. Kotlin's already admitted only entity/value/projection.
The inert tests in all five ports now also compare docs output with and without the
reporting nodes: TS runs meta docs (model, agent, requirements, site) and the api surface;
the other ports render every api-docs unit page, the index and the agent page. The Java
test normalizes the model tier's wall-clock "Generated On:" header line, which made the
comparison flaky across a second boundary.
* fix(codegen-ts): servesReadApi excludes object.report, so agent/ui.md documents no report endpoint
A report declaring a read-only source.rdb @kind: view passed servesReadApi (non-abstract +
any rdb source), so agent/ui.md, rendered when the UI tier is wired, documented an endpoint
for StoreTotals that nothing serves. runGen already drops reports, so this only bit doors
that read the model directly. The predicate now excludes reports.
The inert test gains an agent-surface case (schema, ui, requirements) run with the Hono
routes and the UI tier wired; the earlier --agent run emitted nothing without a gen config.
It also pins the generators that cannot run from a bare model (shared-model) by name, and
checks that a selection of only reports warns that there is nothing to generate.
* fix: FR-044 Plan 1 review minors — only-reports warning, deferred coverage, filter host
- TS and Python runners: a selection made only of object.report nodes now gets the
"No entities to generate" warning instead of a silent empty run.
- docs-site coverage audit: dimension/measure/segment/report are reported as deferred
(FR-044 Plan 2/3) on the returned report and in its warnings, rather than dropped, so the
gap stays visible without changing a rendered page. The comment says to delete it when the
lowering lands.
- extract-view-spec: the relative-date / int-enum guard messages name the filter host, so
the origin.aggregate @filter path no longer reads as a projection's view @filter.
- Python apidocs builder: one object_constants import.
- Inert tests: string literals replaced with the report subtype constants; each port pins,
by name, the generators that cannot run from a bare model (TS shared-model, Java extractor
and template, none in C#/Kotlin/Python), so one that starts throwing cannot drop out.
* docs(fr-044): reporting vocabulary feature doc and changelog (metamodel 1.1)
* docs(fr-044): correct fixture count and R5/D2/R6 wording in reporting.md
* fix(python): dataGrid preset filters recurse into and/or
The dataGrid @filter pass read 'and'/'or' as field names, so a valid
composed preset was refused ("references field 'or' which is not a
filterable field") even in a model with no reporting nodes, and a
relative date inside an 'or' gave two errors where TS gives one.
Recurse into each composition item like the TS checkFilterClauses.
* fix(java): keep distinct reporting errors on one node
The validation phase's envelope dedupe keyed on code + source, so two
different reporting findings on one node (two unresolved @Dimensions
items; a bad dimension item and a bad @segment; two inheritors breaking
one inherited dimension) collapsed to one in Java while TS, C# and
Python report each. Findings from the reporting pass now add their
message to the dedupe key; the pass already drops its own true repeats.
* test(conformance): two reporting errors on one node; relative dates on dataGrid and origin hosts
error-report-two-unresolved-dimensions gates that two distinct findings
on one report are both reported in every port. The two relative-date
host fixtures gate F1 on a layout.dataGrid preset and an
origin.aggregate @filter, beside the existing projection host.
* test(csharp): select reporting fixture children by type and name
Two inherited-member tests edited BaseEvent children by index, so a
fixture reorder would silently retarget them; select by (type, name).
The field-named-now F1 test now asserts the model loads clean rather
than only that no relative-date error appears.
* fix: FR-044 R5 message names every read-only kind; pin R6 attribute-dimension collision
R5 accepts any read-only @kind (view, materializedView, storedProc,
tableFunction) but its message told the author to declare @kind: view.
The message now names the read-only kinds, identically in all four
loaders, with the pins updated (Java's fixture table, the TS test) and
new pins in C# and Python.
R6 for an attribute dimension (bare name, no grain suffix) colliding
with a same-named measure was untested; it is now pinned in TS with
mirrors in C#, Java and Python. Python also pins two unresolved
@Dimensions items as two errors.
* docs: FR-044 changelog states what loads differently; fixture counts
The 1.1 note claimed every model that loaded under 1.0 loads the same,
which the Java and Python parser fixes contradict; say which models
each fix affects, describe the Python non-operator object clause
accurately per pass, and record the Python dataGrid and/or fix. The
reporting doc's compatibility note gets the same correction.
Metamodel corpus 358 -> 361 fixtures (AGENTS.md, CONFORMANCE.md, site
payload); the changelog counts 27 new reporting fixtures.
* no-mistakes(document): docs: update FR-044 roadmap status and stale corpus/matrix/porting facts1 parent 70423f9 commit bad5a59
235 files changed
Lines changed: 23005 additions & 148 deletions
File tree
- .claude/rules
- docs
- features
- examples/showcase
- fixtures
- codegen-noop/reporting
- without
- with
- conformance
- error-dimension-grain-on-date
- input
- error-dimension-of-unresolved
- input
- error-dimension-time-not-temporal
- input
- error-dimension-via-to-many
- input
- error-measure-distinct-not-count
- input
- error-measure-of-foreign
- input
- error-measure-segment-unresolved
- input
- error-measure-sum-non-numeric
- input
- error-measure-tuple-without-distinct
- input
- error-ratio-operand-not-aggregate
- input
- error-relative-date-bad-duration
- input
- error-relative-date-datagrid-host
- input
- error-relative-date-non-temporal
- input
- error-relative-date-origin-host
- input
- error-relative-date-wrong-host
- input
- error-report-declares-field
- input
- error-report-dimension-grain
- input
- error-report-field-collision
- input
- error-report-foreign-measure
- input
- error-report-from-not-entity
- input
- error-report-measure-unresolved
- input
- error-report-segment-unresolved
- input
- error-report-two-unresolved-dimensions
- input
- error-report-writable-source
- input
- error-segment-filter-bad-field
- input
- reporting-inherited-members
- input
- reporting-vocabulary
- input
- metamodel-docs/expected
- types
- persistence-conformance/canonical
- registry-conformance
- requirement-harness
- csharp
- java
- kotlin
- python
- typescript
- metaobjects
- scripts
- site
- server
- csharp
- MetaObjects.Codegen.Tests
- MetaObjects.Codegen
- ApiDocs
- MetaObjects.Conformance.Tests
- MetaObjects
- Core
- Object
- Reporting
- Loader
- Meta
- Registry/Spec
- SpecMetamodel
- java
- codegen-base/src/main/java/com/metaobjects/generator/util
- codegen-kotlin/src
- main/kotlin/com/metaobjects/generator/kotlin
- test/kotlin/com/metaobjects/generator/kotlin
- codegen-spring/src
- main/java/com/metaobjects/generator
- apidocs
- spring
- test/java/com/metaobjects/generator/spring
- metadata
- src
- main
- java/com/metaobjects
- attr
- loader
- parser/json
- object
- registry
- spec
- reporting
- resources/META-INF/services
- test/java/com/metaobjects
- conformance
- loader
- registry/spec
- reporting
- python
- src/metaobjects
- apidocs
- codegen
- loader
- meta/core
- attr
- object
- reporting
- spec_metamodel
- tests
- unit
- typescript/packages
- cli/test/unit
- codegen-ts
- src
- generators
- projection
- test
- projection
- docs-site
- src
- test
- metadata
- src
- core
- attr
- object
- reporting
- loader
- test
- site-reference
- types
- spec
- metamodel
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
51 | | - | |
52 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
| |||
74 | 75 | | |
75 | 76 | | |
76 | 77 | | |
77 | | - | |
| 78 | + | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
84 | | - | |
| 85 | + | |
85 | 86 | | |
86 | 87 | | |
87 | 88 | | |
| |||
0 commit comments