Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ here.**

_npm `1.1.0` (all 14 `@metaobjectsdev/*` packages in lockstep), PyPI `1.1.0`, NuGet `1.1.0` and
Maven Central `8.1.0`. **`metamodelVersion` moves `1.0` to `1.1`.** The vocabulary is additive:
a model that uses none of the new names loads as it did under 1.0, with two exceptions: a bare
`extends` naming an object in another package is refused in TypeScript, Python and C# (see
**Breaking** below), and the Java and Python parser fixes under **Fixed** bring those ports into
line with TypeScript. What it generates is NOT unchanged in every port: in C#, Java, Kotlin's runtime and
a model that uses none of the new names loads as it did under 1.0, with three exceptions: a bare
`extends` naming an object in another package is refused in TypeScript, Python and C#, a
fractional `validator.numeric` `@min` or `@max` is refused in TypeScript, C# and Python (both
under **Breaking** below), and the Java and Python parser fixes under **Fixed** bring those
ports into line with TypeScript. What it generates is NOT unchanged in every port: in C#, Java, Kotlin's runtime and
Python the default for column and table names flips from the field name to `snake_case`, so a
1.0 estate whose columns are named like its fields must add
`"naming": { "columns": "literal" }` (see **Breaking** below). Each entry says which models it
Expand All @@ -35,15 +36,18 @@ those four). A database whose columns are named exactly like the fields adds
(`--column-naming`, the `columnNaming` pom arg, `ObjectManager(column_naming=)`,
`columnNamingStrategy`) are removed and refused with a message naming the setting. Nothing in the
build fails if you skip this: the first failure is a query against a column that does not exist.
Migration: [physical-naming.md](docs/features/migrations/physical-naming.md). The entry is under
Upgrade every port of an estate together: a 1.1 TypeScript tool reads `naming`, a 1.0.x C#, Java
or Python tool silently ignores the unknown key and keeps the old column names, so two ports of
one estate disagree about the schema without any error (only a 1.0.x TypeScript tool refuses the
key). Migration: [physical-naming.md](docs/features/migrations/physical-naming.md). The entry is under
**Changed**.

**Breaking, in every port: documentation is generated the way code is.** Every generator,
documentation included, is an entry in the project's generator list; one command runs the
list; nothing is generated that the list does not declare. The docs commands are removed:
`meta docs` and `meta verify --docs` (TypeScript), `dotnet meta docs` (C#), `metaobjects docs`
(Python) and the `metaobjects:docs` goal (Java and Kotlin, 8.1.0). Each prints where its work
went and exits 2. A gen command given a 1.0 docs configuration fails before it writes anything
went and exits 2 (the Maven goal fails the build, so `mvn` exits 1). A gen command given a 1.0 docs configuration fails before it writes anything
rather than running and writing no documentation, and in TypeScript a project with no
`metaobjects.config.ts` fails `meta gen` with a pointer instead of having its pages written
from defaults. There is no upgrade script: [docs-are-generators.md](docs/features/migrations/docs-are-generators.md)
Expand Down Expand Up @@ -77,14 +81,25 @@ package.** It now fails with `ERR_UNRESOLVED_SUPER`, as Java already did and ADR
bare name binds the declaring file's own package, then the root. Qualify the reference:
`extends: other::Base`. The entry is under **Changed** (ADR-0059).

**Breaking, TypeScript, C# and Python: `attr.int` and `attr.long` take whole numbers only, so a
fractional validator bound is refused.** A `validator.numeric` declares `@min` and `@max` as
integers, and a fraction such as `@min: 0.5` on a `decimal` or `double` field is now
`ERR_BAD_ATTR_VALUE` at load ("'@min' must be of type 'int' but got the fraction 0.5"), as Java
already refused it, so the five ports agree. 1.0.x TypeScript accepted it. Fix: write an integer
bound (`@min: 0`). A decimal-valued bound (for example "at least 0.01") cannot be declared in
1.1.0: that needs new metamodel vocabulary, which is 1.2 work. A whole number written `1.0`
still loads, as `1`. The entry is under **Changed** and the FR-044 report entries.

**Breaking, in every port: a `field.decimal` crosses the API as one canonical string
(ADR-0019 Amendment 1).** Every decimal a generated route sends, on an entity, a projection or a
report row (an `avg`, a ratio, a `sum` of a decimal), is a JSON string in plain notation with no
exponent and no trailing fraction zeros: `12.50` is sent `"12.5"`, zero is `"0"`. Before 1.1.0
each port sent its own form (TypeScript a string, the others a number), so a client that read a
number from C#, Java, Kotlin or Python now reads a string. Display formatting, such as two digits
each port sent its own form (TypeScript a string that kept trailing zeros, the others a number), so
a client that read a number from C#, Java, Kotlin or Python now reads a string, and TypeScript's
`"12.50"` is now `"12.5"`. Display formatting, such as two digits
for an amount of money, is a view's job, never the wire's. On input a route takes a string in
JSON-number grammar or a JSON number, read exactly; anything else is `400 validation`, and so is
JSON-number grammar or a JSON number, read exactly (in TypeScript a JSON number is exact to 15 significant
digits, so send a string for more); anything else is `400 validation`, and so is
a value with more fraction digits than `@scale` or more integer digits than
`@precision - @scale` (nothing is rounded). A decimal filter operand uses the same grammar, is
compared exactly, and a bad one is `400 invalid_filter_value`. Postgres and MySQL keep the
Expand All @@ -93,7 +108,7 @@ declared precision; SQLite and D1, which have no decimal type, keep 15 significa
generator keeps its old decimal handling until you resync it. The per-port entries are under
**Changed**.

**Nine diffs to expect when you upgrade.**
**Eleven diffs to expect when you upgrade.**

- **Generated files no generator emits any more are deleted.** The first full `gen` after the
upgrade, in every port, deletes a file the committed hash manifest records writing, that no
Expand Down Expand Up @@ -167,6 +182,26 @@ generator keeps its old decimal handling until you resync it. The per-port entri
code that does not compile. The **Fixed** entry lists each port's before and after, and the
re-eject commands.

- **CLI exit codes changed.** `meta gen` on a model that does not load exits 1 (1.0.13 exited 2).
`meta init` in an initialised project is a no-op that exits 0 (it exited 1; `--force` still
re-scaffolds). The removed docs commands exit 2, except the Maven `docs` goal, which fails
the build, so `mvn` exits 1. A script that tests for a specific code needs the new one.
- **Smaller changes an adopter can hit.** Each has its own entry.
- TypeScript: `ObjectManager` now enforces numeric bounds, array bounds, uri/inet format and
an assigned primary key; `RequirementTestArgs` and `RequirementView` gained required
fields (a compile break for code that builds them by hand).
- Java: every regenerated class has a one-time header diff; `entity` no longer writes
`<Name>Extractor`; decimal filter operands are `BigDecimal`, not `String`, so a hand-written
repository that casts `(String) p.value()` breaks.
- Kotlin: also emits `MetaPortableColumnTypes.kt` and `<Report>Names`; `portableUuid` binds
text on MySQL and SQLite, which differs from an Exposed-created `BINARY(16)` column.
- Python: refuses a project module that shadows the standard library; answers 400, not 422,
for a malformed body.
- The `mermaid-er` generator is removed.
- C# and Python: `gen` and `verify --codegen` need a generator list (`codegen/Generators.cs`,
written by `dotnet meta init`; `metaobjects.config.yaml`), and exit 2 without one. Python's
`gen <dir> --out` form is gone.

**Not in 1.1.0.** Derived measures, the preview `reporting` library, the dbt exporter, the second
requirements slice and the advisory lint port, the JSON Schema and OpenAPI generators, the
generator catalog and the Atlas evaluation are 1.2 work.
Expand Down Expand Up @@ -1483,6 +1518,26 @@ until you regenerate.
### Fixed

- **Found by the 1.1.0 release review, in every port unless named.**
- **Python: writing a `field.decimal` array no longer answers 500.** The write coercion
converted the whole list as one decimal; it now converts each element and keeps `None`.
- **Kotlin: a `field.decimal` with no `@precision` is unbounded, as in the other four
ports.** It was an Exposed `decimal(19,4)` column that rounded to four places on read, and
the new range check refused values that column could not hold. An undeclared decimal now
has an unbounded column type and no range check; a declared `@precision` is checked as
before.
- **A `field.decimal` inside a jsonb value object follows the top-level decimal rule in all
five ports**: written as the canonical string, read from a string or a JSON number exactly.
The ports disagreed before (TypeScript refused a number, Python stored a float). A
`field.double` inside a value object stays a JSON number, and a fractional number that is
not a declared decimal stays a float in Python (it answered 500). A decimal inside a
`field.map` value object is not covered: it is not part of the rule and is not gated.
- **A `field.decimal` array works in C#, Java and Kotlin.** Java binds a Postgres `numeric[]`
as exact `BigDecimal` elements, a non-required C# decimal array is nullable, and each
element follows the scalar decimal rule (a bad element is `400 validation`).
- **C# and Java: a value object omits an unset member on the wire instead of sending `null`**,
as the other ports do. A C# value-object array property becomes nullable with no
initializer; an abstract shape's array keeps its `new List<T>()` initializer.
- **C#: a malformed or empty JSON body on a decimal-bearing entity is `400 validation`.**
- **The loader refuses four more report shapes**, with the cross-port code and a message
naming the fix: a dimension or measure over a `field.object` or `@objectRef` field
(D5, M10: `ERR_INVALID_DIMENSION` / `ERR_INVALID_MEASURE`; a `field.map` with `@valueType`
Expand Down
23 changes: 13 additions & 10 deletions agent-context/skills/metaobjects-codegen/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,19 @@ exact config, generator names and commands for this project; read it. The other

## Upgrading a project from 1.0 to 1.1

Read both guides before the first `gen` of an upgrade, for every port the project uses; the
build can stay green while the result is wrong.

- https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/physical-naming.md: the default column and table names are now `snake_case` in every
port (C#, Java, Kotlin's runtime and Python defaulted to the field names). A database whose
columns are named like its fields needs `"naming": { "columns": "literal" }` in
`.metaobjects/config.json`, or regenerated code queries columns that do not exist. The
per-port naming flags and generator args are removed.
- https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/docs-are-generators.md: documentation is a generator in the project's list, and the docs
commands are removed.
Read the one upgrade guide, https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/upgrading-1.0-to-1.1.md, before the first `gen`, for every
port the project uses; the build can stay green while the result is wrong. It lists the steps
in order and every change that needs action.

- The default column and table names are now `snake_case` in every port (C#, Java, Kotlin's
runtime and Python defaulted to the field names). A database whose columns are named like
its fields needs `"naming": { "columns": "literal" }` in `.metaobjects/config.json`, or
regenerated code queries columns that do not exist. The per-port naming flags and generator
args are removed.
- Documentation is a generator in the project's list, and the docs commands are removed. C#
and Python also need a generator list (`dotnet meta init`; `metaobjects.config.yaml`).
- A `field.decimal` crosses the API as one canonical string, and a fractional validator `@min`
or `@max` is refused (write an integer bound).
- **Resync the generators you own.** An ejected copy keeps the logic it was copied with, and a
plain `gen` does not refresh it. `meta eject --list` (`dotnet meta eject`, `metaobjects eject`,
`mvn metaobjects:eject`) reports each copy that differs from the reference;
Expand Down
2 changes: 1 addition & 1 deletion agent-context/templates/always-on.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ MetaObjects ships as separate packages per language on DIFFERENT version lines (

`metamodelVersion` is also the number that tells you whether YOUR METADATA needs work, separately from the package version, which tells you whether your BUILD does. A package major means imports/CLI/generated-code shape may need attention; a metamodel major means your model may. A release can move one without the other — so when you upgrade, read the changelog for a metamodel-version move, not just the package number. For a 1.0.x → 1.0.x upgrade, follow https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/upgrading-within-1.0.md: it covers resyncing the generators you own (`meta eject --list`), which a plain `meta gen` does not do.

**Upgrading from 1.0 to 1.1:** read both guides before you run `gen`, because the build can stay green while the result is wrong. https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/physical-naming.md: the default column and table names are now `snake_case` in every port, so a C#, Java, Kotlin or Python database whose columns are named like its fields needs `"naming": { "columns": "literal" }` in `.metaobjects/config.json`, or the regenerated code queries columns that do not exist. https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/docs-are-generators.md: documentation is a generator in the project's list, and the docs commands are removed. Then resync every generator you own (`meta eject --list` reports each copy that differs from the reference; `meta eject <name> --force` takes the new one, and a plain `meta gen` does not): an owned copy keeps the logic it was copied with.
**Upgrading from 1.0 to 1.1:** read https://github.com/metaobjectsdev/metaobjects/blob/main/docs/features/migrations/upgrading-1.0-to-1.1.md before you run `gen`, because the build can stay green while the result is wrong. It is the one page for every port and lists the steps in order. The changes that fail nothing when skipped: the default column and table names are now `snake_case` in every port, so a C#, Java, Kotlin or Python database whose columns are named like its fields needs `"naming": { "columns": "literal" }` in `.metaobjects/config.json`, or the regenerated code queries columns that do not exist; a `field.decimal` crosses the API as one canonical string; and a fractional validator `@min`/`@max` is refused. Documentation is a generator in the project's list, and the docs commands are removed. Then resync every generator you own (`meta eject --list` reports each copy that differs from the reference; `meta eject <name> --force` takes the new one, and a plain `meta gen` does not): an owned copy keeps the logic it was copied with.

## Going deeper (Claude Code)
For authoring, codegen, runtime/UI, prompts, verify, or adoption-audit work, use the
Expand Down
Loading
Loading