Repository navigation
1.0.10
npm 1.0.10 (all 14 @metaobjectsdev/* packages in lockstep), PyPI 1.0.10, NuGet 1.0.10 and
Maven Central 8.0.10. metamodelVersion stays 1.0. Gated by a private 1.0.10-rc.5 build of the
release commit on the adopter estate (rc-gate.sh 7/7) and a full local CI run on that commit.
Added
-
Records in a store MetaObjects does not manage keep their wire contract, in every port.
A sourceless entity is anobject.entitywith anidentity.primaryand nosource.*:
a record kept in MongoDB, Cassandra, Neo4j or behind an API. It still gets no table,
queries, routes or migration (#248). It now gets the create and PATCH schemas and the filter
allowlist that the adopter's own endpoint needs. Before, it was treated like a value
object.- TypeScript:
<E>UpdateSchema,<E>Patch,<E>FilterAllowlist,<E>SortAllowlistand
<E>Filterbeside the interface and<E>InsertSchema. - Python:
<e>_filter_allowlist.py.<E>Create/<E>Patchwere already emitted. - Java:
<E>Patchand<E>FilterAllowlistbeside<E>Dto. - Kotlin and C#:
<E>FilterAllowlist.
One predicate per port picks these objects:
isSourcelessEntity(TS),
is_sourceless_entity(Python),RestSurfaceGate.isSourcelessEntity(JVM) and
InstanceArtifacts.IsSourcelessEntity(C#). Value objects are unchanged, since ADR-0028
forbids them an identity. Upgrading: runmeta gen(or the port'sgen). A project
with such an entity gets the new symbols and files. No existing symbol changes. - TypeScript:
-
MySQL as a TypeScript codegen and runtime dialect. Set
dialect: "mysql", and the
generated tier targetsdrizzle-orm/mysql-core:mysqlTableentities, and queries and
Fastify/Hono routes that read rows back after a write (MySQL has noRETURNING). The
runtime's generated routes, and theObjectManageroverkyselyDriver(Kysely
MysqlDialect) ordrizzleDriver(drizzle-orm/mysql2), run on MySQL. MySQL-specific
behaviour:likestays case-sensitive (ADR-0049) throughCOLLATE utf8mb4_bin;- timestamps use Drizzle's
datemode and store the UTC wall clock inDATETIME(3); - a
field.timecolumn's write schema refuses a UTC offset, which MySQL'sTIMErejects
withERROR 1292(a value object'sfield.timeis JSON and keeps the offset); - identifiers are backtick-quoted;
- constraint errors map to 409/400 as on the other dialects.
MetaObjects does not own a MySQL schema.
meta migrate --dialect mysqlis refused with that
explanation,meta gentells you to update your DDL instead of pointing atmigrate, and
agent/schema.mdis skipped. Seedocs/recipes/mysql.md. Covered against MySQL 8.4 by
mysql-generated-app.test.tsandmysql-object-manager.test.ts, and the MySQL dialect
joins the codegen-compile gate. -
Recipe: MongoDB, Cassandra, Neo4j and other unmanaged stores
(docs/recipes/document-graph-and-wide-column-stores.md). It shows how to model the records
and what each port generates. It includes a MongoDB repository generator
(docs/recipes/generators/typescript/mongo-repository.ts), run end to end against MongoDB 7
and copied into a fresh project by thedocument-store-recipeintegration test.
Fixed
-
CLI errors no longer print an empty reason. A refused Postgres connection on a dual-stack
host rejects with anAggregateErrorwhose own message is empty, someta migrate apply-pendingprintedapply failed:and nothing else. Every CLI command now prints the
inner errors instead (connect ECONNREFUSED 127.0.0.1:55440; …), or the error's code or name
when it carries no message. -
meta verifycounts only requirement gaps a@dispositioncould settle. The "recorded
gap(s) with no @disposition" line counted everypartial/plannedrequirement without a
disposition, including roll-up parents that arepartialonly because a descendant is. On one
adopter ledger it read 19 where 7 were actionable. A requirement is now counted only when no
descendant also carries outstanding work. -
meta types <subtype> --detailprinted every attribute twice. The subtype's block lists
its@attrswith full descriptions, and each attr then printed again as its own(attr)block
with the same text. It now prints once, which halves the output (field.string: 9.7KB to
4.9KB). Asking for one attr (meta types "field.string @maxLength" --detail) still prints its
block. -
TypeScript
ObjectManager: scalar array fields can be written. A
field.string isArray(or any scalar array) was type-checked as a single string, so every
create or update carrying one failed validation (tags: type) on every dialect. Each element
is now checked, and an element error names its index (tags[1]). The Kysely driver binds a
scalar array as JSON text on SQLite and MySQL, and a JSON-text array reads back as an array.
Before, libsql could not bind the array at all and mysql2 expanded it into a value list.
The value-object branch of the validator also reads the resolvingisArray, not the
own-only flag (ADR-0039). -
Java: OMDB's
MySQLDriverworks against a MySQL server. It had never been run
against one, and it failed on the first insert:@generation: incrementkeys threw "This should not get called". They are now read
back withLAST_INSERT_ID().- Timestamp and uuid values were bound with JDBC types Connector/J rejects
("Unsupported SQL type"). A timestamp is now stored in aDATETIMEas the UTC wall
clock of the instant, whatever the JVM zone. A uuid is stored as text. - Identifiers are backtick-quoted, so a column named after a reserved word (
rank) works.
MySqlObjectManagerTestcovers insert, read, filtered and paged queries, update and
delete against MySQL 8.4. MetaObjects still does not own a MySQL schema: write the DDL
yourself.
Changed
- Behaviour change in
extract, every port:validator.arraybounds are enforced. An
array field'svalidator.array(@min/@max) now reaches the extract engine. A reply with
more elements than@maxkeeps the first@max(atruncatecoercion, like a numeric clamp)
under the default tolerance and isMALFORMEDunderSTRICT; a reply with fewer than@min
isMALFORMEDunder every tolerance, soorThrowfails on a@requiredone and
malformedRequired()names it. The TypeScript generatedparse<Name>/safeParse<Name>
Zod schema carries the same bounds (z.array(…).min(n).max(m)). Before, the bounds were
declared and ignored, so code re-checked the count by hand. The extract-conformance corpus
gains three cases (48 in all), aminItems/maxItemsschema key and an optional per-case
tolerance, run by every port. - SQLite: projection views that use
origin.collectnow open on SQLite 3.35. They used an
in-aggregateORDER BY(json_group_array(x ORDER BY …)), which needs 3.44, so a database
holding one could not be opened by Ubuntu 22.04's SQLite 3.37 or Python 3.10's stdlib. A
collect column is now built in two steps: the grouped query gathers each element with its
sort keys, and an outer query sorts them. Element order and empty-set results are
unchanged, and whole-object collects still return objects. The floor is now 3.35 for tables
and views alike. Upgrading: the nextmeta migrateagainst a SQLite or D1 database with
such a view replaces the view once. - SQLite: a timestamp column that defaults to "now" uses the ISO UTC form generated code
writes.@autoSetand@default: "now"on afield.timestampemit
DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))(2026-09-26T14:08:05.053Z) instead of
DEFAULT CURRENT_TIMESTAMP(2026-09-26 14:08:05), so database-defaulted rows and
application-written rows share one spelling that sorts and compares correctly. The generated
Drizzle schema carries the same default. Upgrading: existingCURRENT_TIMESTAMPcolumns
are not rebuilt for this: migrate treats the two as the same default. Rows already stored keep
their old spelling. Regenerate withmeta gento pick up the Drizzle default. - SQLite: a function-call column default is wrapped in parentheses when emitted, as SQLite
requires (DEFAULT (expr)). Before, an authored expression default such as
datetime('now')produced aCREATE TABLEthat did not parse. meta init's config passes its own typecheck.extStylewas required by the config
type but absent from the scaffoldedmetaobjects.config.ts, so
npx tsc -p tsconfig.codegen.json --noEmitfailed withProperty 'extStyle' is missing
on a fresh project. It is optional now and defaults to"js", which every generator
already assumed.dbImportis demanded only by a generator whose output importsdb. The reference
entity,routesandroutes-honogenerators (the copiesmeta ejecthands over) copy
the render context with an object spread, and the demand fired on that read, so
meta eject entityalone failed withcodegen config is missing dbImportin a project
whose queries takedbas a parameter. The runner now checks the emitted output instead,
which also fixes copies already ejected.- A new database is pointed at
meta migrate apply-pending.meta init's next steps
name it, andmeta migrate --db <url> --applyagainst an empty database with committed
migrations now says to replay them rather than only asking for--slug. meta verifynames the prompt file it could not find.ERR_PARTIAL_UNRESOLVEDnow
says which file to create (prompts/<ref>.mustacheor.txt) and that--promptsmoves
the root, instead of only naming the directory it searched.- Agent context: an agent can work out any database, store or framework. The skills
meta initinstalls now carry a playbook for a stack no reference generator targets
(metaobjects-codegen/references/any-stack.md): MySQL is adialect; MongoDB, DynamoDB,
Cassandra, Neo4j or an API are sourceless entities plus a repository generator you own;
other HTTP and UI frameworks are mounted, retargeted or generated. TypeScript projects also
get the MySQL guide and the tested MongoDB repository generator. The always-on context and
themeta gen --listheader point at it. In cold runs, a fresh agent built a MongoDB app
this way with no hints. - Agent context: the skills are split into a short
SKILL.mdplus topic files. The
authoring skill went from 75KB to 40KB, codegen from 49KB to 23KB, and the TypeScript codegen
reference from 30KB to 17KB. EachSKILL.mdopens with a table of itsreferences/files and
when to open each. A reference named<language>-<topic>.mdinstalls only for that language.
Runmeta init --refresh-docsto take the new files. - Agent context: hand-built LLM prompts (#389). The always-on context installed by
meta initgains a rule: once a project declares anytemplate.prompt, every LLM prompt is
one, replies are read through a response model, and changing a hand-built prompt means
migrating it first. Themetaobjects-promptsskill now triggers on code that builds text for
an LLM or parses its reply, and has a migration procedure; themetaobjects-auditprompt
census covers every module. Refresh withmeta init --refresh-docs.