Skip to content

feat: any/all measures without @of and 1.1.0 rc.5 adopter fixes - #443

Merged
dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-rc5-pl-fixes
Oct 17, 2026
Merged

dmealing merged 9 commits into
mainfrom
fm/mo-1-1-0-rc5-pl-fixes

Conversation

@dmealing

@dmealing dmealing commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

What Changed

  • measure.aggregate with @agg: any or all may omit @of and declare a @filter or @segment instead. Lowers to bool_or/bool_and over COALESCE(condition, FALSE) on Postgres and MAX/MIN over COALESCE(condition, 0) on SQLite, D1 and MySQL, and to the same SQL in the Cube model. A new loader rule M0 refuses an @of-less measure with any other @agg, and an @of-less any/all without a condition. Gated by three new conformance fixtures across the ports.
  • A dimension @of after a @via hop, and a report's @spine owner, now resolve a bare Owner in the package of the entity the path reaches (or @from), not the declaring package. Fully-qualified names are unchanged. Gated by the reporting-cross-package-short-names fixture.
  • meta gen no longer advises wiring promptRender() for a docs-only run, and only claims payload value objects were emitted when an entity-module generator ran. agentDocsFile() warns when it skips agent/ui.md because includeUiTier is unset. meta init warns when a pre-release CLI stamps generatedBy, and detect-prerelease-pins.sh flags a pre-release generatedBy in .metaobjects/.agent-context.json.
Validation

Risk Assessment

✅ Low: The latest fix round (F1 cleanup command that tolerates grep's exit 1, F2 hedged notice wording, and a test where the exclude file holds only the legacy line) is correct. Tracing the earlier R1–R6/D11 changes across the TS, C#, Java and Python loaders and report shapes, the view and Cube SQL lowering, and the prerelease scripts found no wrong-result path, and recorded operator decisions are respected.

Testing

Baseline ts-fast/ts-unit ran earlier. Live drives: R1 and R2 with the built-from-source meta CLI on scratch projects (docs-only config, codegen with template.prompt, agentDocsFile with and without includeUiTier). R5 with meta verify on the cross-package fixture, positive and bad-@Of negative. R3, R4, D11 and the notice/cleanup behaviour through the real prerelease scripts in temp repos (tools/prerelease/test-prerelease-link.sh). R6 through the real emitter on real SQLite and Postgres (report-views-sqlite/pg). Not live: C# ReportRowCodegenTests (72 pass) and full server TS bun test (11132 pass, 0 fail), both reported as untested.

  • Live validation: ✅ go - 10 of 12 scenarios driven live against the product
Scenario Result Live Evidence
R1: docs-only config runs meta gen with no false prompt-generator advisory ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r1-gen-docs-only.txt
R1: codegen run with template.prompt and no prompt generator names promptRender and keeps the value-object claim only when entityFile ran ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r1-gen-with-codegen.txt
R2: agentDocsFile without includeUiTier skips agent/ui.md with a warning that names includeUiTier ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r2-agentdocs-no-ui.txt
R2: agentDocsFile with includeUiTier: true writes docs/generated/agent/ui.md ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r2-agentdocs-includeUi.txt
R3: prerelease link/unlink round trip leaves poms, pyproject.toml and .npmrc byte-identical and reports only the root pom repin ✅ pass live tools/prerelease/test-prerelease-link.sh run (real script in temp git repos): 'the round trip leaves the root pom byte-identical', 'the round trip leaves the pyproject.toml byte-identical', 'link does…
R4: detect-prerelease-pins fails a tree stamped by a pre-release CLI and passes a release-stamped tree ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'detect-prerelease-pins fails a tree whose agent context was generated by a pre-release CLI' and 'passes a tree whose agent context was generated by a REL…
R5: via dimension of 'Realm.name' resolves Realm in the entity the via reaches; a bad field names the reached entity ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r5-verify-ok.txt (rc=0); ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r5-verify-bad.txt (ERR_INVALID_DIMENSION names acme::wo…
D11: unlink removes its own exclude entry and NuGet.config, and prunes only when no subproject holds the entry ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'unlinking one subproject removes its own .npmrc', 'unlink removes the NuGet.config link wrote', 'unlinking the last subproject removes the managed block'
D11 adversarial: legacy bare exclude line gets a notice, no duplicate on link, and a cleanup command that works even when the legacy line is the whole file ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'link adds no duplicate of the bare legacy line', 'the printed cleanup command removes a legacy line that is the whole file'
R6: any/all with @filter and no @Of compute the WHERE-rule result on real SQLite and Postgres, and a spine row with no facts reads NULL ✅ pass live bun test packages/integration-tests/test/report-views-sqlite.test.ts and report-views-pg.test.ts (Docker available) passed within the 512-test focused run
C# codegen for an any measure with no @Of compiles and passes ReportRowCodegenTests ⏸️ untested no Not driven against the live product. The prior run was a unit-level test (dotnet test MetaObjects.Codegen.Tests --filter ReportRowCodegenTests, 72 passed), which does not establish a live result.
Full regression: the complete server TypeScript suite ⏸️ untested no Not a live scenario. The prior run was a bun test regression suite (11132 pass, 0 fail), which does not establish a live product result.
Evidence: R1 docs-only meta gen output
gen[3]{file,status}:
  docs/generated/README.md,new
  docs/generated/AuthorPayload.md,new
  docs/generated/authorPrompt.md,new
summary: 3 written
help[1]: typecheck the generated code with `npx tsc`
antiPatterns:
  status: ran
  total: 0
  rows: []
Evidence: R1 meta gen with codegen generator, advisory wording
meta: 1 declared template.prompt (authorPrompt) generated no prompt code: no prompt generator is wired, so there is no render function to send one and nothing read a reply. Add promptRender() to `generators` in metaobjects.config.ts (import it from "@metaobjectsdev/codegen-ts/generators"). The payload value objects were emitted regardless — those are object.value nodes, which is why this looked like it had worked.
gen[4]{file,status}:
  docs/generated/README.md,unchanged
  docs/generated/AuthorPayload.md,unchanged
  docs/generated/authorPrompt.md,unchanged
  src/generated/AuthorPayload.ts,new
summary: "1 written, 3 unchanged"
help[1]: typecheck the generated code with `npx tsc`
antiPatterns:
  status: ran
  total: 0
  rows: []
warnings[1]: "1 declared template.prompt (authorPrompt) generated no prompt code: no prompt generator is wired, so there is no render function to send one and nothing read a reply. Add promptRender() to `generators` in metaobjects.config.ts (import it from \"@metaobjectsdev/codegen-ts/generators\"). The payload value objects were emitted regardless — those are object.value nodes, which is why this looked like it had worked."
Evidence: R2 agentDocsFile without includeUiTier
gen[1]{file,status}:
  docs/generated/agent/schema.md,new
summary: 1 written
help[1]: typecheck the generated code with `npx tsc`
antiPatterns:
  status: ran
  total: 0
  rows: []
warnings[1]: "[agent-docs] agent/ui.md not written: no client UI generator in this run; set includeUiTier: true if another config generates the UI, or includeUiTier: false if there is no UI."
Evidence: R2 agentDocsFile with includeUiTier
gen[2]{file,status}:
  docs/generated/agent/schema.md,unchanged
  docs/generated/agent/ui.md,new
summary: "1 written, 1 unchanged"
help[1]: typecheck the generated code with `npx tsc`
antiPatterns:
  status: ran
  total: 0
  rows: []
Evidence: R5 verify positive
meta: meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: meta verify — no template.* nodes found; the template gate had nothing to check.
meta: meta verify — 1 foreign key(s) with no index on the referencing column(s) (advisory — does not fail the build):
meta:   Game.fkRealm: foreign key (realmId) → realms has no index covering it, so joins from realms and its ON DELETE checks scan games. Declare one on Game: index.lookup: { name: gameByRealmId, fields: realmId }
meta: meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
verify[6]{gate,ran,ok}:
  templates,true,true
  schema,false,true
  codegen,false,true
  deps,false,true
  requirements,true,true
  replay,false,true
exitCode: 0
summary: "2 gate(s) ran, all clean; 1 advisory finding(s)"
help[1]: "1 foreign key(s) with no covering index — see antiPatterns.rows[] (rule \"unindexed-foreign-key\"); each names the index.lookup to declare"
antiPatterns:
  status: ran
  total: 1
  rows[1]{file,line,rule,construct,message}:
    meta.play.json,0,unindexed-foreign-key,"acme::play::Game.fkRealm","Game.fkRealm: foreign key (realmId) → realms has no index covering it, so joins from realms and its ON DELETE checks scan games. Declare one on Game: index.lookup: { name: gameByRealmId, fields: realmId }"
requirements:
  status: ran
  total: 0
  rows: []
overlays:
  status: ran
  total: 0
  rows: []
names:
  status: ran
  total: 0
  rows: []
deprecations:
  status: ran
  total: 0
  rows: []
fields:
  status: ran
  total: 0
  rows: []
notRepresented[2]: "per-gate drift detail for every gate but schema (which template variable drifted, which generated file differs, which migration failed to replay) — printed as text on stderr; this payload carries each gate's pass/fail verdict, and the schema gate's differences in schemaDrift",the loader's own warnings and the agent-context/manifest advisories — printed as text on stderr
Evidence: R5 verify negative
meta: meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: failed to load metadata: ERR_INVALID_DIMENSION: dimension 'realmName' on entity 'acme::play::Game': @of 'Realm.nope' names no field 'nope' on 'acme::world::Realm'.
  in meta.play.json at $['metadata.root'].children[0]['object.entity'].children[5]['dimension.attribute']
error: "failed to load metadata: dimension 'realmName' on entity 'acme::play::Game': @of 'Realm.nope' names no field 'nope' on 'acme::world::Realm'."
code: ERR_INVALID_DIMENSION
files[1]: meta.play.json
jsonPath: "$['metadata.root'].children[0]['object.entity'].children[5]['dimension.attribute']"
hint: fix the metadata error above and re-run
Evidence: Full server TS regression output
bun test v1.3.14 (0d9b296a)

packages/metadata/test/registry-coverage.test.ts:

[registry-coverage] subtypes: 53/65 exercised (81.5%), 12 UNTESTED
[registry-coverage] untested subtypes:
  attr.boolean
  attr.class
  attr.double
  attr.expression
  attr.filter
  attr.int
  attr.intMap
  attr.long
  validator.atLeastOne
  validator.comparison
  validator.presentIff
  validator.requiredWhen
[registry-coverage] exercised subtypes with untested attrs: 28

packages/codegen-ts/test/format.test.ts:
[codegen-ts] Biome formatter: 2 diagnostics; first: expected `,` but instead found `'x'`

packages/cli/test/migrate-drop-unmanaged.test.ts:
meta: migrate: refusing to drop public.theirs — absent from the committed schema snapshot, so this toolchain never managed it and the migration could not replay against a database where it never existed. Re-run with '--allow drop-unmanaged' if the drop is intended.
meta migrate — sqlite, file:<test temp dir>/t.db

  Changes: 1 drop-table

  Written:
    <test temp dir>/.metaobjects/migrations/20261017103706-x/up.sql
    <test temp dir>/.metaobjects/migrations/20261017103706-x/down.sql

meta migrate — sqlite, file:<test temp dir>/t.db

  Changes: 1 drop-table

  Written:
    <test temp dir>/.metaobjects/migrations/20261017103706-x/up.sql
    <test temp dir>/.metaobjects/migrations/20261017103706-x/down.sql

meta migrate — sqlite, file:<test temp dir>/t.db

  Changes: 1 drop-table

  Written:
    <test temp dir>/.metaobjects/migrations/20261017103706-x/up.sql
    <test temp dir>/.metaobjects/migrations/20261017103706-x/down.sql

meta: migrate: refusing to drop public.orders.orders_job_id_fk — absent from the committed schema snapshot, so this toolchain never managed it and the migration could not replay against a database where it never existed. Re-run with '--allow drop-unmanaged' if the drop is intended.
meta migrate — sqlite, file:<test temp dir>/t.db

  Changes: 1 drop-fk

  Written:
    <test temp dir>/.metaobjects/migrations/20261017103706-x/up.sql
    <test temp dir>/.metaobjects/migrations/20261017103706-x/down.sql

meta migrate — sqlite, file:<test temp dir>/t.db

  Changes: 1 drop-fk

  Written:
    <test temp dir>/.metaobjects/migrations/20261017103706-x/up.sql
    <test temp dir>/.metaobjects/migrations/20261017103706-x/down.sql


packages/cli/test/help-and-exit.test.ts:
meta gen — codegen TS targets from your declared metadata

USAGE:
  meta gen [<entity>...] [flags]

FLAGS:
  --dry-run             Compute and print, don't write
  --list                Print the generator CATALOG (name, layer, tier, what it emits,
                        what to install) and exit. Codegen is opt-in: nothing runs until
                        you wire it, and this is where you choose. No config or metadata
                        required — add --format json for the same catalog, machine-readable.
  --probe               With --list: construct every catalog generator and dry-run it
                        against YOUR model, reporting how many files each would emit.
                        A count per generator beats any category label — and it cannot go
                        stale, because it runs the generators. Needs a project.
  --baseline <default|adopt|fresh>
                        First-time-on-existing-file behavior. Default: refuse a file that
                        cannot be proved to be generated output. "adopt" records the files
                        you have as the merge base and writes NOTHING — the first run a
                        project with no committed .hashes.json can perform. "fresh"
                        overwrites them with fresh output and re-baselines (discards edits).
  --no-antipatterns     Suppress the advisory "hand-rolled what MetaObjects can model" pass
  --limit <n|all>       Advisory lines TEXT output prints before truncating (default 20)
  --generators <names>  Narrow the run to these DECLARED generators, by stable name, comma-
                        separated (e.g. docs,api-docs to write only the documentation). A
                        name metaobjects.config.ts does not declare is refused: it never adds one.
  --format <toon|json|text>   Output format (global flag; default toon off-TTY)
  <entity> [<entity>]   Positional filter on entity names
  --help, -h            Print this help

A real write run (not --dry-run) also runs an ADVISORY anti-pattern pass: it scans
your authored source for hand-rolled aggregates, money-as-float, and CHECK-IN enums
and points you at the construct that models them (origin.aggregate / field.currency /
field.enum). Warnings only — it never fails the build. Opt out with --no-antipatterns
or META_NO_ANTIPATTERNS=1.

Text output caps that list at --limit lines (default 20) and says how many it held
back. --format toon / --format json carry EVERY finding in the payload's
antiPatterns block, uncapped — --limit is a display cap for a terminal, never a
limit on the machine-readable result.

NOTE: outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts

meta migrate — diff metadata vs live DB; emit migration SQL files

USAGE:
  meta migrate [baseline|apply-pending] [flags]

SUBCOMMANDS:
  baseline             Snapshot an EXISTING database's schema as the reference point
                       (use with --from-db). NOTE: for a brand-new/empty database use
                       the greenfield example below, NOT baseline — an offline baseline
                       records your metadata as already-applied and emits no CREATE TABLE.
  apply-pending        Replay committed migration files against --db (no diff).
                       Provisions a fresh/CI database when the chain BUILDS the
                       schema — 'meta verify --replay' proves that it does. A
                       database adopted via 'baseline --from-db' has no such
                       chain. postgres/sqlite only.

MIGRATE FLAGS:
  --db <url>           DB connection URL (required for live-introspect / --apply / --rollback)
                       Supports: file:, libsql:, postgres:, postgresql:
  --dialect sqlite|postgres|d1
                       Optional dialect override (auto-detected from URL scheme)
  --migration-format default|flyway
                       Migration file layout (default: default). 'flyway' emits
                       V<N>__<slug>.sql + U<N>__<slug>.sql for a Flyway runner;
                       --apply/apply-pending/--rollback are refused under it
                       because Flyway owns apply and flyway_schema_history.
                       Also settable once as migrate.format in
                       .metaobjects/config.json.
  --out-dir <path>     Migration directory (default: ./.metaobjects/migrations;
                       flyway: src/main/resources/db/migration)
  --slug <name>        Required when changes are present (e.g., --slug add-user-shipping)
  --allow <csv>        Comma-separated destructive-change permissions:
                       drop-column,drop-table,type-change,drop-index,drop-fk,
                       drop-check,drop-view,drop-view-cascade,
                       adopt-view,nullable-to-not-null,drop-identity-default,
                       drop-unmanaged
                       drop-column DELETES the column and every value in it. If
                       the column was RENAMED, use --rename-column instead: it
                       keeps the data.
                       drop-unmanaged permits dropping a table/view the committed
                       snapshot never contained — one this toolchain never managed.
                       Without it that drop is refused, because the migration it
                       writes cannot replay against a database where the object
                       never existed.
  --on-ambiguous abort|rename|drop-add
                       How to handle ambiguous renames (default: abort)
  --rename-table [schema.]old=new
                       Declare a table rename

... [242244 bytes truncated] ...

      "file": "out--site/site/acme/shop/StoreTotals.html",
      "status": "new"
    },
    {
      "file": "out--site/site/acme/shop/WorkoutEvent.html",
      "status": "new"
    },
    {
      "file": "out--site/site/coverage.html",
      "status": "new"
    },
    {
      "file": "out--site/site/assets/site.css",
      "status": "new"
    },
    {
      "file": "out--site/site/assets/site.js",
      "status": "new"
    },
    {
      "file": "out--site/site/assets/search-index.json",
      "status": "new"
    }
  ],
  "summary": "22 written",
  "help": [
    "typecheck the generated code with `npx tsc`",
    "create your database tables with `meta migrate --from-db --db <url> --dialect postgres --slug init --apply`"
  ],
  "antiPatterns": {
    "status": "ran",
    "total": 0,
    "rows": []
  },
  "warnings": [
    "1 wired generator(s) matched nothing and wrote no file: requirements-file. That is often correct — a generator whose metadata this model does not declare has nothing to emit. It is reported because a run that emitted nothing and a run that did the work otherwise look identical."
  ]
}

packages/cli/test/unit/migrate-d1.test.ts:
migrate: wrote <test temp dir>/migrations/0001_init.sql
migrate: wrote <test temp dir>/migrations/.down/0001_init.sql
  create-table: 1
migrate: wrote <test temp dir>/migrations/0001_init-programs.sql
migrate: wrote <test temp dir>/migrations/.down/0001_init-programs.sql
  create-table: 2
  add-fk: 1
  create-view: 1
meta: migrate: no wrangler.toml found in <test temp dir> or parents; pass --d1 <binding> to bypass
meta: migrate: --db / DATABASE_URL is not used for dialect 'd1' — wrangler.toml owns connection

packages/cli/test/unit/verify-strict.test.ts:
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: failed to load metadata: ERR_UNKNOWN_ATTR: Unknown attribute '@madeUpAttr' on field.string 'email' — not declared by any registered provider for field.string
  in meta.users.json at $['metadata.root'].children[0]['object.entity'].children[1]['field.string']
meta: meta verify is strict (ADR-0023): every authored @attr must be declared. Fix: register the attr on a metadata provider, OR move arbitrary author-supplied properties into an `attr.properties` bag, OR re-run with `meta verify --lax` to keep the legacy open-attr load.
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — no template.* nodes found; the template gate had nothing to check.
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — no template.* nodes found; the template gate had nothing to check.
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it

packages/cli/test/unit/verify-output.test.ts:
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: failed to load metadata: ERR_INVALID_TEMPLATE: template "Broken" @payloadRef "DoesNotExist" does not resolve to an object.value or sourceless object.projection at root
  in meta.ai.json at $['metadata.root'].children[0]['template.output']
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: [Welcome] (email/html) ERR_VAR_NOT_ON_PAYLOAD: nonExistentField (prompts/e/html:1)
meta: meta verify — 1 drift error(s) across 1 template(s).
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta verify — 1 template(s) clean.
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it
meta verify — running --templates (default). Explicit subverbs: --templates (prompt drift), --db/--dialect d1 (schema drift), --codegen (drift of everything `generators` declares, documentation included), --deps (dependency drift), --replay/--replay-snapshot (the committed migration chain replays from empty).
meta: [Doc] (document) ERR_VAR_NOT_ON_PAYLOAD: missingField (prompts/o/x:1)
meta: meta verify — 1 drift error(s) across 1 template(s).
meta verify — not run: schema (--db <url>), codegen (--codegen), deps (--deps), replay (--replay) — a bare 'meta verify' runs only the template gate (plus the requirement ledger); name each gate to run it

 11082 pass
 50 skip
 0 fail
 4 snapshots, 38955 expect() calls
Ran 11132 tests across 1045 files. [840.82s]

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (2) ✅
  • ⚠️ server/typescript/packages/codegen-ts/src/projection/report-ddl-emit.ts:72 - R6 no-@Of any/all aggregates the raw condition, so a row whose condition evaluates to NULL is skipped instead of counted as not passing. As a result, all reads TRUE when some rows fail the @filter/@segment. The docs define the measure as "all is true when every row does [pass the @filter]" (docs/features/reporting.md:472), and a @filter is WHERE semantics everywhere else. Concrete case: the committed fixture allSlow (@agg all, @segment slow = latencyMs &gt; 1000 on a nullable latencyMs). A game with calls latency=2000 and latency=NULL reads allSlow = TRUE, yet a count with the same segment counts only 1 of its 2 calls. The integration tests lock this in: in report-views-pg.test.ts:798 and report-views-sqlite.test.ts:637, game 3 has success = TRUE and NULL, and allSucceeded (@filter success: true) asserts TRUE. Similarly, any over a non-empty group where no row passes reads NULL instead of FALSE (game 4). Sibling sites with the same semantics: cube-members.ts:246 (bool_or(c) / MAX(c) in the Cube model), the reporting.md:512 and CHANGELOG "skipped, as bool_or skips one" wording, and the two integration-test expectations. Possible remedy: aggregate COALESCE(c, FALSE) on Postgres and COALESCE(c, 0) elsewhere, inside the existing spine fact-row guard. A row then passes exactly when the WHERE form would keep it, and only an empty group reads NULL. This changes a documented, test-pinned rule, so it needs the author's decision.
  • ℹ️ tools/prerelease/prerelease-link.sh:903 - The D11 fix makes unlink remove only the new managed block in the shared info/exclude. A clone linked with the pre-fix script (the adopter's rc.4 clone that reported D11) holds bare .npmrc / NuGet.config / .mvn/* lines outside any block. The new unlink never removes them. A re-link also does not see them, because exclude_block_lines reads only the block, so it adds a duplicate inside the block. The leftover the intent asks to fix therefore stays in every clone that was already linked. Possible remedy: during unlink's pruning, also drop a line outside the block that exactly matches a path this script writes (prefix plus .npmrc, NuGet.config, .mvn/settings.xml, .mvn/maven.config) when no worktree still holds a managed file there. That would touch lines outside the block, against the documented 'never a line a user added' promise, so the author should decide.

🔧 Fix applied.
3 issues (1 warning, 2 infos) still open:

  • ℹ️ tools/prerelease/prerelease-link.sh:903 - The D11 fix makes unlink remove only the new managed block in the shared info/exclude. A clone linked with the pre-fix script (the adopter's rc.4 clone that reported D11) holds bare .npmrc / NuGet.config / .mvn/* lines outside any block. The new unlink never removes them. A re-link also does not see them, because exclude_block_lines reads only the block, so it adds a duplicate inside the block. The leftover the intent asks to fix therefore stays in every clone that was already linked. Possible remedy: during unlink's pruning, also drop a line outside the block that exactly matches a path this script writes (prefix plus .npmrc, NuGet.config, .mvn/settings.xml, .mvn/maven.config) when no worktree still holds a managed file there. That would touch lines outside the block, against the documented 'never a line a user added' promise, so the author should decide.
  • ⚠️ tools/prerelease/prerelease-link.sh:277 - The cleanup command printed by legacy_exclude_notice is grep -vxF -e &lt;p&gt;... &lt;ex&gt; &gt; &lt;ex&gt;.tmp &amp;&amp; mv &lt;ex&gt;.tmp &lt;ex&gt;. When every line of the exclude file is a legacy entry, grep -v prints nothing and exits 1, so the &amp;&amp; mv never runs. The legacy line stays and a stray exclude.tmp is left behind, with no error. I reproduced it: a file holding only .npmrc gives rc=1, the line is still there, and ex.tmp exists. This state is reachable. The pre-fix local_exclude created info/exclude with &gt;&gt; when it was absent, for example in a clone made without git's templates. Then the file holds only the bare lines this notice targets, and the only remedy the notice offers fails. The new test passes only because git init seeds the file with template comments. This code was added in fix round 1 (review-2 decision). Fix: print a command that tolerates an empty result, e.g. { grep -vxF ... &lt;ex&gt; || [ $? -eq 1 ]; } &gt; &lt;ex&gt;.tmp &amp;&amp; mv &lt;ex&gt;.tmp &lt;ex&gt;. Add a test case where the exclude file holds only the legacy line.
  • ℹ️ tools/prerelease/prerelease-link.sh:271 - The notice says the matching bare lines were (left by an earlier version of it), but the code cannot know that. The comment two lines above says such lines are 'indistinguishable from a line a user added'. Suppose a user hand-added .npmrc to info/exclude to protect their own credential-bearing .npmrc. Every link/unlink then tells them the line is the script's leftover and prints a command that removes it. Running that command un-ignores their own .npmrc, which is the credential exposure the exclude block exists to prevent. Keep the notice the operator decided on, but hedge the wording, e.g. 'may have been left by an earlier version of this script; keep any you added yourself'. This text was added in fix round 1.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 10 of 12 scenarios driven live against the product
Scenario Result Live Evidence
R1: docs-only config runs meta gen with no false prompt-generator advisory ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r1-gen-docs-only.txt
R1: codegen run with template.prompt and no prompt generator names promptRender and keeps the value-object claim only when entityFile ran ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r1-gen-with-codegen.txt
R2: agentDocsFile without includeUiTier skips agent/ui.md with a warning that names includeUiTier ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r2-agentdocs-no-ui.txt
R2: agentDocsFile with includeUiTier: true writes docs/generated/agent/ui.md ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r2-agentdocs-includeUi.txt
R3: prerelease link/unlink round trip leaves poms, pyproject.toml and .npmrc byte-identical and reports only the root pom repin ✅ pass live tools/prerelease/test-prerelease-link.sh run (real script in temp git repos): 'the round trip leaves the root pom byte-identical', 'the round trip leaves the pyproject.toml byte-identical', 'link does…
R4: detect-prerelease-pins fails a tree stamped by a pre-release CLI and passes a release-stamped tree ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'detect-prerelease-pins fails a tree whose agent context was generated by a pre-release CLI' and 'passes a tree whose agent context was generated by a REL…
R5: via dimension of 'Realm.name' resolves Realm in the entity the via reaches; a bad field names the reached entity ✅ pass live ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r5-verify-ok.txt (rc=0); ~/.no-mistakes/evidence/01M54MGDNNCJ5TW3XAP20QEB1P/r5-verify-bad.txt (ERR_INVALID_DIMENSION names acme::wo…
D11: unlink removes its own exclude entry and NuGet.config, and prunes only when no subproject holds the entry ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'unlinking one subproject removes its own .npmrc', 'unlink removes the NuGet.config link wrote', 'unlinking the last subproject removes the managed block'
D11 adversarial: legacy bare exclude line gets a notice, no duplicate on link, and a cleanup command that works even when the legacy line is the whole file ✅ pass live tools/prerelease/test-prerelease-link.sh run: 'link adds no duplicate of the bare legacy line', 'the printed cleanup command removes a legacy line that is the whole file'
R6: any/all with @filter and no @Of compute the WHERE-rule result on real SQLite and Postgres, and a spine row with no facts reads NULL ✅ pass live bun test packages/integration-tests/test/report-views-sqlite.test.ts and report-views-pg.test.ts (Docker available) passed within the 512-test focused run
C# codegen for an any measure with no @Of compiles and passes ReportRowCodegenTests ⏸️ untested no Not driven against the live product. The prior run was a unit-level test (dotnet test MetaObjects.Codegen.Tests --filter ReportRowCodegenTests, 72 passed), which does not establish a live result.
Full regression: the complete server TypeScript suite ⏸️ untested no Not a live scenario. The prior run was a bun test regression suite (11132 pass, 0 fail), which does not establish a live product result.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • bash tools/prerelease/test-prerelease-link.sh
  • cd server/typescript && bun test packages/integration-tests/test/report-views-sqlite.test.ts packages/codegen-ts/test/projection
  • cd server/typescript && bun test packages/integration-tests/test/report-views-sqlite.test.ts packages/integration-tests/test/report-views-pg.test.ts packages/codegen-ts/test/cube packages/codegen-ts/test/agent-docs-surface.test.ts packages/codegen-ts/test/prompt-generator-gate.test.ts packages/codegen-ts/test/runner-docs-generators.test.ts packages/metadata/test/report-shape.test.ts
  • cd server/csharp && dotnet test MetaObjects.Codegen.Tests --filter ReportRowCodegenTests
  • cd server/typescript && bun test (full regression)
  • R1 live: bun server/typescript/packages/cli/bin/meta.ts gen in scratch project, docs-only config and entityFile config
  • R2 live: meta gen with agentDocsFile(), with and without includeUiTier
  • R5 live: meta verify on fixtures/conformance/reporting-cross-package-short-names input, positive and @of 'Realm.nope' negative
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Reporting, in every port:
- A bare entity name the path already fixes resolves in that entity's
  package: a dimension's @Of after @via resolves in the package of the
  entity the @via reaches, and a report's @spine owner resolves in
  @from's package. Gated by reporting-cross-package-short-names.
- @agg any / all may omit @Of and quantify the measure's @filter or
  @segment over the rows (bool_or / bool_and on Postgres, MAX / MIN of
  the condition on SQLite, D1 and MySQL, and in the Cube model). New
  loader rule M0 refuses an @of-less measure of any other @agg, and an
  @of-less any / all with no condition, with ERR_INVALID_MEASURE.

Tooling:
- meta gen no longer tells a docs-only run to wire a prompt generator,
  and claims emitted value objects only when an entity generator ran.
- agentDocsFile() warns when it skips agent/ui.md; includeUiTier is
  documented.
- meta init warns when a pre-release CLI writes generatedBy, and
  detect-prerelease-pins.sh fails on a pre-release generatedBy.
- prerelease-link keeps a pom's final-newline state, reports only real
  repins, and keeps its git exclude lines in a managed block that
  unlink removes.
@dmealing dmealing changed the title fix: close 1.1.0 rc.4 adopter findings in reporting, prompt gate, and prerelease tooling feat: any/all measures without @of and 1.1.0 rc.5 adopter fixes Oct 17, 2026
@dmealing
dmealing merged commit bc8167b into main Oct 17, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-1-1-0-rc5-pl-fixes branch October 17, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant