You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Five issues an adopter filed against 0.24.0-rc.3, all fixed in this release and none of
them previously in the changelog. Two are regressions from the metadata-source-resolution
work in this same section, which is why they belong in it rather than in a follow-up.
The Python `libraries` entry's closing parenthetical is corrected: it said the TypeScript
CLI "still lacks the key — parity follow-up", and both remaining ports now have it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+148-2Lines changed: 148 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -851,8 +851,7 @@ same refs (path under `library/` minus `.yaml`), same on-disk-first resolution:
851
851
config is a `ConfigError` naming the valid ones, while the programmatic API keeps
852
852
TypeScript's silent skip: a name typed into a config file is a mistake worth failing on,
853
853
where an API caller asking for a package this version does not ship should still load
854
-
its own metadata. (The TypeScript CLI still lacks the key — parity follow-up, not drift
855
-
introduced here.)
854
+
its own metadata. (The TypeScript CLI gained the same key in this release — see below.)
856
855
857
856
Three gates ship with it, since each of these failed silently before:
858
857
@@ -880,6 +879,153 @@ base, so it appears in codegen output and in a schema diff unless filtered. Docu
880
879
the Python prompts reference rather than changed, because `library/ai/llm-call.yaml` is
881
880
shared by every port and splitting it is a cross-port decision.
882
881
882
+
### Added — `libraries` on the TypeScript CLI and the whole feature on Java ([#333](https://github.com/metaobjectsdev/metaobjects/issues/333), [#332](https://github.com/metaobjectsdev/metaobjects/issues/332))
883
+
884
+
**No new vocabulary — port parity, twice.**`expected-registry.json` is untouched.
885
+
886
+
The Python entry above closes with "the TypeScript CLI still lacks the key". Both remaining
887
+
ports now have it, and the Java gap was the larger of the two.
888
+
889
+
**TypeScript (#333)** — `libraries` existed only on `MetaDataLoader.fromDirectory`. No CLI
890
+
command uses that factory; every one of them goes through `loadMemory` with a resolved file
891
+
list. So the option reached nothing, while the generators that consume a library are
892
+
registered *for the CLI* — the generator was reachable from the command line with its input
893
+
unreachable through it, and an adopter following the documented
894
+
`extends: "metaobjects::ai::LlmCallBase"` got `ERR_UNRESOLVED_SUPER` pointing at their own
895
+
metadata. `metaobjects.config.ts` now takes `libraries?: readonly string[]`, beside
896
+
`providers` because it answers the same shape of question — what does this project's model
897
+
need in scope beyond the files it declares.
898
+
899
+
Threaded to `loadMemory` at all eight load sites (`gen`, `verify`, `docs`, `prompt-snapshot`,
900
+
and `migrate`'s four) through one `loadMemoryOptionsFrom` helper, not a spread pair copied
901
+
eight times. That is the actual lesson of the bug: `providers` reached every command and
902
+
`libraries` reached none, so a fix leaving eight independent opportunities to thread one and
903
+
forget the other has not fixed the class.
904
+
905
+
`librarySources` is reached through a new **`@metaobjectsdev/metadata/library`** subpath and
906
+
imported lazily, never from the root barrel — it reads `node:fs`, and a root-reachable static
907
+
import drags Node built-ins into every consumer's module graph, which is the
908
+
[#287](https://github.com/metaobjectsdev/metaobjects/issues/287) bundle defect. The subpath
909
+
exists for exactly the reason `./constants` does.
910
+
911
+
**Java (#332)** — the port shipped `LlmTraceHelperGenerator` and *no way at all* to load the
912
+
metadata it consumes: no `libraries` option, no embed. What let that survive is the more
913
+
useful half: every test of that generator declares its own `LlmCallBase` inline under a
914
+
different package, so the suite could not tell a world where the library loads from one where
915
+
it does not exist. That is the bypass ADR-0024 already names — "the green tests pass only
916
+
because they bypass the shipped base with bespoke entities."
917
+
918
+
-`com.metaobjects.library.EmbeddedLibrary` — a generated **class** of string constants, not a
919
+
`src/main/resources` copy. A resource can be dropped or mangled by build configuration
920
+
(resource filtering, shading, repackaging) and the failure surfaces much later as
921
+
`ERR_UNRESOLVED_SUPER` against the adopter's own metadata; a class constant cannot go
922
+
missing without the class going missing. Same rationale Python records for its source module.
923
+
Emitted by the **existing**`scripts/generate-embedded-library.ts` rather than a second
924
+
script — two scripts walking one tree are two things that can drift, and an embed's whole job
925
+
is to be byte-identical to its source.
926
+
-`com.metaobjects.library.LibrarySources` — on-disk-first, embedded fallback, and an
927
+
unrecognised package contributing no sources, matching every other port.
928
+
- The opt-in: `MetaDataLoader.setLibraries(...)`, a `fromDirectory(..., libraries)` overload,
929
+
`LoaderConfiguration.getLibraries()` (a `default` method — this interface is the build-tool
930
+
seam and an implementor outside this repo must keep compiling), and a pom
An unknown name in a pom or a `metaobjects.config.ts` is a **hard error listing the packages
934
+
this version ships**, while the programmatic door keeps the silent skip. Both ports draw the
935
+
same line Python did: an API caller asking for a package this version does not ship should
936
+
still load its own metadata, but a name a human typed into a config file is a mistake worth
937
+
failing on — skipped, it resurfaces as `ERR_UNRESOLVED_SUPER` pointing at the wrong file.
938
+
939
+
Gated on Java by the freshness comparison, the positive arm, and a negative arm asserting the
940
+
same model still fails **and that the failure names `LlmCallBase`** — the loader wraps the real
941
+
diagnostic in a "Failed to load from directory <path>" envelope that names nothing, and the
942
+
first draft of that assertion passed on the envelope. Plus `TraceHelperOnShippedLibraryTest`,
943
+
which runs the generator against the **shipped** base with ADR-0024 FIX #1 asserted both
944
+
directions. The freshness gate was proven by breaking it, not by its silence.
945
+
946
+
C# has the same gap and is not addressed here.
947
+
948
+
### Fixed — two files, two questions: a sub-project's config governs its own codegen ([#326](https://github.com/metaobjectsdev/metaobjects/issues/326), [#327](https://github.com/metaobjectsdev/metaobjects/issues/327))
949
+
950
+
Both are regressions from the metadata-source-resolution work in this same release, both
951
+
reported by an adopter evaluating `0.24.0-rc.3`, and both come from one conflation: treating a
952
+
single directory as the answer to two different questions. Design §4.6 already draws the line —
953
+
`.metaobjects/config.json` says where metadata comes from (port-neutral, read by all five CLIs,
954
+
reasonably repo-global in a polyglot monorepo); `metaobjects.config.ts` says how **this**
955
+
TypeScript package generates code.
956
+
957
+
**#326 — `meta gen` could not run at all in a Maven- or pip-rooted monorepo.** Setting
958
+
`projectRoot = collection.configDir` closed a real divergence (a subdirectory run silently
959
+
defaulting `columnNamingStrategy` and emitting a migration that renamed every column), but it
960
+
assumed the two files are always co-located. When they are not — repo root declares the
961
+
collection, the JS app underneath carries the TS config — `gen` demanded `metaobjects.config.ts`
962
+
at the ancestor and exited 2 against a directory that has one sitting in it. `0.23.1` wrote 376
963
+
files on the same tree. The obvious workaround did not reach CI either, since `.metaobjects/` is
964
+
gitignored in such sub-projects, so the file existed only on developer machines.
965
+
966
+
`metaobjects.config.ts` now gets its **own** nearest-ancestor walk from the invocation
967
+
directory, with collection discovery untouched. Nearest wins, so a subdirectory declaring
968
+
nothing still walks up to the project root's config exactly as before; the fallback is the
969
+
collection's directory, so this can only ever move the answer *closer* to the invocation, and
970
+
a project with no TS config anywhere keeps today's diagnostics unchanged. In every `meta init`
971
+
project the two walks return the same directory by construction.
972
+
973
+
Applied at all five sites, not only the one that failed loudly. `gen` was the sole hard
974
+
failure; `verify --codegen` reported "no config" for a package that has one, `docs` silently
975
+
dropped its providers and skipped the api surface, `prompt-snapshot` lost its providers, and
976
+
`migrate` defaulted `columnNamingStrategy` — the identical rename-every-column failure the
977
+
co-location existed to prevent, reached from the other side. Everything each config **names**
978
+
follows it: `outDir`/`targets` and the `.metaobjects/.gen-state/` merge base that mirrors that
979
+
output (per-package, or two apps sharing one collection clobber each other's), `docs.outDir`,
980
+
the adopter `templates/` chain, the owned `codegen/docs-site/` theme, and `verify.testFiles`.
981
+
`.metaobjects/` state — migrations, snapshots, the operational block, `wrangler.toml`
982
+
discovery — stays on the collection's directory.
983
+
984
+
**#327 — `meta docs <path>` stopped scoping.** The positional has always meant "document this",
985
+
and before sources were resolvable it read `<path>/metaobjects/` and nothing else. Routing docs
986
+
through `resolveCollection(<path>)` turned the argument into a *starting point for an upward
987
+
walk*, so the nearest ancestor config was found and its declared sources were unioned in.
988
+
Nothing was lost — a set-diff of an adopter's page lists shows zero pages dropped — but pages
989
+
from unrelated trees appeared inside a tree meant to hold prompt contracts only. Unlike `gen`
990
+
this fails **open**: exit 0, just more pages than anyone asked for, invisible until someone
991
+
counts them. An explicit positional now pins the collection (`resolveCollection`'s existing
992
+
`explicitDir`); a bare `meta docs` still discovers. Both help blocks say so, since the old text
993
+
described the argument as a root without saying what it does to the source set.
994
+
995
+
### Fixed — the private-host check asks where packages come from, not what strings a build file holds ([#334](https://github.com/metaobjectsdev/metaobjects/issues/334))
996
+
997
+
`tools/prerelease/detect-prerelease-pins.sh` flagged any RFC1918, loopback or link-local host
998
+
anywhere in a manifest. An Android module with a LAN default for its own backend —
999
+
`buildConfigField("String", "SERVER_URL", … "http://10.0.0.5:8000")` — failed it permanently.
1000
+
That string is application config, where the built app looks for its own server; it says
1001
+
nothing about where Gradle resolves dependencies. The project had never been linked to a
1002
+
private registry and still exited 1 after a full, verified `unlink`.
1003
+
1004
+
The header calls this check load-bearing and says to treat a failure as a build break, never as
1005
+
advice. A permanent false positive inverts that: the only route to a green build is to stop
1006
+
running the check or to train everyone to ignore it — which is how the true positive it exists
1007
+
to catch gets waved through. It also made the `unlink` round-trip unverifiable, since `unlink`
1008
+
ends by running the detector.
1009
+
1010
+
The script already stated the right principle one level up — "Only DEPENDENCY DECLARATIONS are
1011
+
scanned… a check that cries wolf is a check people learn to ignore" — and that reasoning does
1012
+
not stop at the file boundary. The host scan now splits the manifest list in two: files that
1013
+
are package-resolution config end to end (`.npmrc`, every lockfile, `pip.conf`, `NuGet.config`,
1014
+
`settings.xml`, `requirements*.txt`, …) are scanned whole, exactly as before; files carrying
1015
+
both package sources and project configuration are scanned only inside the region that declares
1016
+
a source — a Gradle `repositories { }` / `pluginManagement { }` block, a pom's
1017
+
`<repositories>`/`<pluginRepositories>`/`<distributionManagement>`, an msbuild
1018
+
`<RestoreSources>`, a `package.json` dependency block or `registry` key, a
1019
+
`[[tool.uv.index]]`/`[[tool.poetry.source]]` section, a `gradle.properties` key naming a repo.
1020
+
1021
+
Checks 1, 3, 4 and 5 are untouched, and narrowing costs no detection on the npm path: a real
1022
+
link writes the registry line into `.npmrc` and the resolved URL into the lockfile, both still
1023
+
scanned whole. `settings.gradle`/`settings.gradle.kts` join the manifest list while here —
1024
+
`pluginManagement { repositories { } }` lives there, so a private registry declared in one was
1025
+
invisible to *every* check. Proven both directions against a fixture carrying each format twice,
1026
+
once as app config and once as a real declaration; the negative half caught a case-sensitivity
0 commit comments