Skip to content

feat(java): consume a shared model from committed dependency snapshot - #500

Merged
dmealing merged 5 commits into
mainfrom
fm/mo-deps-p2-s3
Oct 28, 2026
Merged

dmealing merged 5 commits into
mainfrom
fm/mo-deps-p2-s3

Conversation

@dmealing

Copy link
Copy Markdown
Member

What Changed

  • Loader and config (server/java/metadata, maven-plugin): added Dependencies and DependencySet. NeutralConfig now parses dependencies. The Java loader and the Maven plugin (every rung of the source ladder) load the committed .metaobjects/deps/ snapshot ahead of the project's own files. The lock is checked as in TypeScript and Python: ERR_DEPENDENCY_SNAPSHOT_STALE, _METAMODEL_INCOMPATIBLE and _NODE_COLLISION. A local top-level node declared into a package a dependency owns fails with ERR_DEPENDENCY_PACKAGE_NOT_OWNED. The Node meta deps sync stays the only writer of the snapshot and lock.
  • Generators (codegen-base, codegen-spring, codegen-kotlin): Java and Kotlin generators now emit nothing for an imported object unless scope.include names its package. Kotlin loads through the same loader and Maven plugin, so it inherits the loading and lock checks.
  • Tests and docs: DependencyConformanceTest runs the 24 resolution cases in fixtures/dependency-conformance/. New tests: MavenLoaderDependenciesTest, DependencyImportExclusionTest (Spring) and KotlinDependencyImportExclusionTest. Added a CHANGELOG entry, Java and Kotlin port-doc sections, and updates to the conformance, roadmap, dependencies feature doc and agent-context skill reference (with its expected fixtures). Spring runtime auto-load and verify drift for dependencies are documented as later slices.
Validation

Risk Assessment

✅ Low: The change meets the Java and Kotlin build-time consumption intent. The loader, the Maven plugin and the generator selection points all exclude imported objects, and the earlier review fixes (collision check, configured-loader test, forRoot removal) are in place. The tests exercise real loaders and generators against the shared corpus. The only defect found was a stale case count in docs, fixed inline.

Testing

I ran the real Maven plugin built from this branch against disposable consumer projects, covering the happy path and four adversarial cases, and all behaved as intended. The targeted Java tests and the full Java reactor unit run pass; the Testcontainers integration modules and the C#, Python and TypeScript lanes were not run here, though the TypeScript fast and unit baseline passed. The shared-corpus runner scenario is an automated test, not a live product run, so it is recorded as untested.

  • Live validation: ✅ go - 7 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Java consumer imports shared model via snapshot and generators emit only its own Order ✅ pass live mvn metaobjects:generate with the Spring DTO and value-object generators emitted only app/OrderDto.java and app/OrderPatch.java; no acme/common output
Own entity extends an imported abstract and references an imported value object ✅ pass live Generated OrderDto carries createdAt (inherited from acme::common::Audited) and ship of type acme.common.Address, while the imported nodes themselves were not emitted
scope.include naming the imported package opts its objects into generation ✅ pass live With scope.include set to the pattern acme::common::**, the run also emitted acme/common CustomerDto, CustomerPatch and Address
Hand-edited snapshot is refused ✅ pass live After appending a byte to the snapshot, generate failed: 'the committed snapshot for "acme-common" does not match the lock ... run meta deps sync'; no output was generated
Declaring a new node in a dependency-owned package is refused ✅ pass live Declaring acme::common::Sneaky in the project failed the build; the message says the package belongs to a metadata dependency (ERR_DEPENDENCY_PACKAGE_NOT_OWNED path)
Own app::Customer alongside imported acme::common::Customer does not trigger a REST collection-name collision ✅ pass live generate succeeded and emitted app/CustomerDto and app/OrderDto; the imported Customer was left out
Kotlin generator via the same plugin skips imported objects ✅ pass live KotlinEntityGenerator emitted only app/Order.kt; nothing under acme/common
Java runner covers every case in the shared dependency corpus ⏸️ untested no The prior payload recorded this only as an existing automated corpus runner (DependencyConformanceTest, 48 tests, 0 failures), not a live product run, so it did not establish a live result.
Evidence: live generate output
own-only generation:
consumer/target/gen/app/OrderDto.java
consumer/target/gen/app/OrderPatch.java
extends+ref imported:
kotlin:
c5/target/kgen/app/Order.kt

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

🔧 **Rebase** - 4 issues found → auto-fixed ✅
  • ⚠️ docs/CONFORMANCE.md - merge conflict rebasing onto origin/main
  • ⚠️ docs/features/cli.md - merge conflict rebasing onto origin/main
  • ⚠️ docs/features/metadata-dependencies.md - merge conflict rebasing onto origin/main
  • ⚠️ fixtures/dependency-conformance/README.md - merge conflict rebasing onto origin/main

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

⚠️ **Review** - 1 info
  • ℹ️ CHANGELOG.md:25 - Inline doc fix: the new CHANGELOG entry said the Java runner "runs all 23 cases" of fixtures/dependency-conformance/. After the rebase onto main the corpus has 35 cases (24 resolution and 11 check), and DependencyConformanceTest skips the 11 expectCheck cases. This matches the figure in docs/CONFORMANCE.md. I changed the text to "all 24 resolution cases" and noted that the 11 check cases pin the Node meta deps check.
✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 7 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Java consumer imports shared model via snapshot and generators emit only its own Order ✅ pass live mvn metaobjects:generate with the Spring DTO and value-object generators emitted only app/OrderDto.java and app/OrderPatch.java; no acme/common output
Own entity extends an imported abstract and references an imported value object ✅ pass live Generated OrderDto carries createdAt (inherited from acme::common::Audited) and ship of type acme.common.Address, while the imported nodes themselves were not emitted
scope.include naming the imported package opts its objects into generation ✅ pass live With scope.include set to the pattern acme::common::**, the run also emitted acme/common CustomerDto, CustomerPatch and Address
Hand-edited snapshot is refused ✅ pass live After appending a byte to the snapshot, generate failed: 'the committed snapshot for "acme-common" does not match the lock ... run meta deps sync'; no output was generated
Declaring a new node in a dependency-owned package is refused ✅ pass live Declaring acme::common::Sneaky in the project failed the build; the message says the package belongs to a metadata dependency (ERR_DEPENDENCY_PACKAGE_NOT_OWNED path)
Own app::Customer alongside imported acme::common::Customer does not trigger a REST collection-name collision ✅ pass live generate succeeded and emitted app/CustomerDto and app/OrderDto; the imported Customer was left out
Kotlin generator via the same plugin skips imported objects ✅ pass live KotlinEntityGenerator emitted only app/Order.kt; nothing under acme/common
Java runner covers every case in the shared dependency corpus ⏸️ untested no The prior payload recorded this only as an existing automated corpus runner (DependencyConformanceTest, 48 tests, 0 failures), not a live product run, so it did not establish a live result.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • mvn -o test -Dtest=DependencyConformanceTest,NeutralConfigTest,DependencyImportExclusionTest,KotlinDependencyImportExclusionTest,MavenLoaderDependenciesTest (all green)
  • Live: built the maven-plugin from this branch into a throwaway local repo and ran metaobjects:generate against disposable consumer projects built from the dependency-conformance corpus
  • mvn -o test over the whole Java reactor, excluding the Testcontainers integration modules (BUILD SUCCESS, all 15 modules)
  • Baseline scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains already green
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…023)

Read dependencies, scope.include and migrate.scope from .metaobjects/config.json, load the
committed .metaobjects/deps/ snapshot ahead of the project's own files at every rung of the Maven
plugin's source ladder, check it against the lock, refuse a local node declared into a dependency's
package, and keep the Java and Kotlin generators off imported objects. Runs all 23 cases of the
shared dependency corpus.
…ver the configured-loader path

Drop the unused DependencySet.forRoot, run the REST collection-segment collision check over
the objects that generate, and test the Maven loader configuration path end to end.
@dmealing

Copy link
Copy Markdown
Member Author

Gate runs: 2. Run 1 failed at the test step on agent-provider errors (not a code failure) after its review step, and its fix commit could not be recovered, so the review fixes (route-collision check scoped to own objects, a test through the configured Maven loader path, removal of an unused accessor, one docs prose fix) were reapplied by hand before run 2. Run 2 passed rebase, review, test, document, lint, push and PR; CI is local and was skipped.

@dmealing
dmealing merged commit 98d6823 into main Oct 28, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-deps-p2-s3 branch October 28, 2026 14:38
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