Skip to content

feat(maven-plugin)!: one way to generate in Java and Kotlin; api-docs is a declared generator and the docs goal is removed - #437

Merged
dmealing merged 18 commits into
mainfrom
fm/mo-1-1-0-eject-java
Oct 14, 2026
Merged

dmealing merged 18 commits into
mainfrom
fm/mo-1-1-0-eject-java

Conversation

@dmealing

@dmealing dmealing commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

One way to generate, in Java and Kotlin (ADR-0034 Amendment 9). Every generator, the api reference included, is a <generator> entry in the list mvn metaobjects:generate runs, with its options in <args>; nothing is written that the list does not declare. The metaobjects:docs goal is removed (it remains only to fail with the migration line), the DocsGenerator marker and the generate/verify skips are gone, an 8.0 pom's docs parameters are refused instead of silently writing nothing, and Java entity no longer writes <Name>Extractor classes the pom did not declare. The branch also carries the earlier eject work: every Java and Kotlin generator except template and the deprecated mustache is ejectable with its emitters, and eject.pending holds no Java or Kotlin entry.

Amendment 9 is the cross-port spec the C#, Python and TypeScript changes follow; it fixes, per port, where the list lives, the option names that replace the docs flags, the exact removed-command message, the exact error for a 1.0-shape config, and the migration-guide shape. Migration for these two ports: docs/features/migrations/docs-are-generators.md, section "Java and Kotlin".

Live checks (scratch projects, 8.1.0 artifacts from this branch): api-docs entries through generate and verify write the same pages, byte for byte, as the published 8.0.13 metaobjects:docs; the removed goal and an 8.0 <language> setting fail with the specified lines; one eject call naming a Java-only and a Kotlin-only generator writes a pom whose untouched mvn clean package builds both source trees.

Validation runs: 2. Run 1 validated the eject work. Run 2 was needed because the branch was rebased onto merged PR 436 (a registry.json conflict) and the owner then replaced the two-path docs design with this one.

Line counts (vs main): 88 files, +3,184 / -1,128. Hand-written: code +1,072 / -546, tests +1,190 / -381, docs and ADRs +723 / -95, config (registry.json, poms, agent-context meta) +151 / -81. Copied: agent-context conformance fixture copies +48 / -25. Moved: two test poms renamed.

What Changed

  • Docs are ordinary generators in Java/Kotlin (8.1.0). JavaApiDocsGenerator and KotlinApiDocsGenerator register api-docs in the generator registry. The unlisted Java generators plantuml, html-docs, ai-docs and json-schema are also registered, and eject now covers all but the deprecated mustache. metaobjects:docs no longer runs anything. DocsMojo is now a stub that fails with a pointer to the migration section, and RemovedDocsGoalTest covers it. Each docs generator is a <generator> entry in the plugin's <generators>, with options in <args>. A project that declares no generator writes nothing (DeclaresNothingWritesNothingTest).
  • Owned generators no longer reach into packaged emit code. The prompt-tier emitters (OutputFormatSpecEmitter, KotlinOutputFormatSpecEmitter, KotlinExtractSchemaEmitter) and the shared-enum fragment emitters (Fr019SharedEnum, KotlinEnumEmitter) move into the generators that use them, so an ejected copy is whole. The Java entity generator no longer writes a <Name>Extractor the pom did not declare. EmitsObjectExtractors is deleted, and ExtractorCodeGenerator is now a separate extractor entry. EjectSupport and MetaDataEjectMojo are extended and are covered by new and updated eject tests (ApiDocsEjectBothPortsTest, EjectSupportTest, EjectContractTest, the Kotlin EjectContractTest). fixtures/generator-registry-conformance/registry.json drops the Java/Kotlin entries from eject.pending and updates the engine lists.
  • Docs and agent context.
    • New docs/features/migrations/docs-are-generators.md, with a before/after section for Java and Kotlin that a coding agent can apply by hand. No config-rewrite script is added.
    • ADR-0034 gets Amendment 9, with pointer lines added to ADR-0021, ADR-0022 and ADR-0025.
    • CHANGELOG.md, docs/features/own-your-codegen.md, the Java and Kotlin port docs and the READMEs are updated.
    • The agent-context server metadata and the Java/Kotlin codegen skill references are updated, the agent-context conformance expected outputs are regenerated, and the sdk assemble step has a matching small change with a test.
    • The TypeScript, C# and Python docs commands are not removed here. The registry note records that those ports still run api-docs from their own docs command until their Amendment 9 changes land.
Validation

Risk Assessment

✅ Low: The remaining files are tests, docs and ADR text that match the shipped behavior. Removing the derived emitExtractors/EmitsObjectExtractors seam affects no released owned copy, because Java entity became ejectable after 1.0.13. The only defect found is one wrong row in a doc table.

Testing

At the head commit, the real meta init --docs-only CLI wrote the right Not-there line for each of the five ports: Java and Kotlin name their api-docs generator entry before the command, and the other three are unchanged. The five touched Java modules (1,212 tests) and the quick repo gate pass. Two scenarios, a source search for the generator class names and the targeted TypeScript suites, were not recorded as live runs, so they are marked untested. I did not run the full Java reactor, Testcontainers, C# or Python suites, per the author's plan: this round's changes since the last Java-verified commit are limited to TypeScript and agent-context content. The change is text in a generated markdown file, so there is no UI to screenshot. The evidence is the CLI output.

  • Live validation: ✅ go - 5 of 7 scenarios driven live against the product
Scenario Result Live Evidence
Java-only project: meta init --docs-only --server java writes AGENTS.md whose Not-there line says to declare JavaApiDocsGenerator as a <generator> entry, then run mvn metaobjects:generate ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
Kotlin-only project: the same command with --server kotlin names KotlinApiDocsGenerator ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
Regression guard: Python, C# and TypeScript render the unchanged Not there? Run &lt;docsCommand&gt; line, and AGENTS.md matches CLAUDE.md byte for byte for every port ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
The two generator class names the line prints exist in the Java and Kotlin sources ⏸️ untested no The prior payload recorded only a source search (JavaApiDocsGenerator.java, KotlinApiDocsGenerator.kt) with live=false. That is not a live run of the product, so no live pass is established.
Maven plugin, codegen-base, codegen-plantuml, codegen-spring and codegen-kotlin install with their full test suites (including the descriptor-phase test for the docs goal, the eject tests and the both… ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-java-step2-tests.log
Quick repo gate (scripts/ci-local.sh --quick) passes ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-ci-local-quick.log
Targeted TypeScript suites: sdk agent-context and conformance, cli init-agent-context and init-docs-only, codegen-ts generator-registry ⏸️ untested no The prior payload recorded this scenario with live=false, so it did not establish a live result. The suite log exists but was not recorded as a live drive of the product.
Evidence: meta init Not-there line per port (live CLI output)
== java (exit 0)
Not there? Declare `com.metaobjects.generator.apidocs.JavaApiDocsGenerator` as a `<generator>` entry in the pom's `<generators>` if it is missing, then run `mvn metaobjects:generate`.
AGENTS==CLAUDE
== kotlin (exit 0)
Not there? Declare `com.metaobjects.generator.kotlin.apidocs.KotlinApiDocsGenerator` as a `<generator>` entry in the pom's `<generators>` if it is missing, then run `mvn metaobjects:generate`.
AGENTS==CLAUDE
== python (exit 0)
Not there? Run `metaobjects docs`.
AGENTS==CLAUDE
== csharp (exit 0)
Not there? Run `dotnet meta docs`.
AGENTS==CLAUDE
== typescript (exit 0)
Not there? Run `meta docs`. It reads the metadata and the config and needs nothing else.
AGENTS==CLAUDE
Evidence: Java five-module install with tests
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] MetaObjects :: Code Generation :: Base                             [jar]
[INFO] MetaObjects :: Code Generation :: PlantUML                         [jar]
[INFO] MetaObjects :: Codegen :: Spring                                   [jar]
[INFO] MetaObjects :: Codegen :: Kotlin                                   [jar]
[INFO] MetaObjects :: Maven Plugin                               [maven-plugin]
[INFO] 
[INFO] --------------< com.metaobjects:metaobjects-codegen-base >--------------
[INFO] Building MetaObjects :: Code Generation :: Base 8.1.0              [1/5]
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- jacoco:0.8.13:prepare-agent (prepare-agent) @ metaobjects-codegen-base ---
[INFO] argLine set to -javaagent:/tmp/mo-m2-01M4/org/jacoco/org.jacoco.agent/0.8.13/org.jacoco.agent-0.8.13-runtime.jar=destfile=~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/codegen-base/target/jacoco.exec,excludes=**/*Test*:**/test/**:**/*Exception*
[INFO] 
[INFO] --- resources:3.3.1:copy-resources (ship-eject-reference-source) @ metaobjects-codegen-base ---
[INFO] Copying 6 resources from src/main/java/com/metaobjects/generator/direct/object/javacode to target/classes/META-INF/metaobjects/reference/java
[INFO] Copying 2 resources from src/main/java/com/metaobjects/generator/direct/object to target/classes/META-INF/metaobjects/reference/java
[INFO] Copying 2 resources from src/main/java/com/metaobjects/generator/direct/metadata/html to target/classes/META-INF/metaobjects/reference/java
[INFO] Copying 2 resources from src/main/java/com/metaobjects/generator/direct/metadata/ai to target/classes/META-INF/metaobjects/reference/java
[INFO] Copying 2 resources from src/main/java/com/metaobjects/generator/direct/metadata/file/json to target/classes/META-INF/metaobjects/reference/java
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ metaobjects-codegen-base ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO] 
[INFO] --- compiler:3.13.0:compile (default-compile) @ metaobjects-codegen-base ---
[INFO] Nothing to compile - all classes are up to date.
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ metaobjects-codegen-base ---
[INFO] Copying 8 resources from src/test/resources to target/test-classes
[INFO] 
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ metaobjects-codegen-base ---
[INFO] Nothing to compile - all classes are up to date.
[INFO] 
[INFO] --- surefire:3.5.2:test (default-test) @ metaobjects-codegen-base ---
[INFO] Using auto detected provider org.apache.maven.surefire.junit4.JUnit4Provider
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.metaobjects.generator.SchemaReviewTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.386 s -- in com.metaobjects.generator.SchemaReviewTest
[INFO] Running com.metaobjects.generator.WorkingSchemaValidationTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.013 s -- in com.metaobjects.generator.WorkingSchemaValidationTest
[INFO] Running com.metaobjects.generator.SealedRegistryCodegenGateTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 s -- in com.metaobjects.generator.SealedRegistryCodegenGateTest
[INFO] Running com.metaobjects.generator.SchemaValidationTest
10:42:14.399 WARN  c.m.generator.SchemaValidationTest - ⚠️  JSON Schema pattern validation not working - known issue with json-schema-validator library
10:42:14.404 WARN  c.m.generator.SchemaValidationTest - ⚠️  JSON Schema pattern validation not working - known issue with json-schema-validator library
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.273 s -- in com.metaobjects.generator.SchemaValidationTest
[INFO] Running com.metaobjects.generator.BasicInfrastructureTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.109 s -- in com.metaobjects.generator.BasicInfrastructureTest
[INFO] Running com.metaobjects.generator.RecipeGeneratorsCompileRunTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.580 s -- in com.metaobjects.generator.RecipeGeneratorsCompileRunTest
[INFO] Running com.metaobjects.generator.verify.TemplateVerifyTest
[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.052 s -- in com.metaobjects.generator.verify.TemplateVerifyTest
[INFO] Running com.metaobjects.generator.direct.metadata.ai.MetaDataAIDocumentationTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.030 s -- in com.metaobjects.generator.direct.metadata.ai.MetaDataAIDocumentationTest
[INFO] Running com.metaobjects.generator.direct.metadata.DocumentationOutputIsStableTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.145 s -- in com.metaobjects.generator.direct.metadata.DocumentationOutputIsStableTest
[INFO] Running com.metaobjects.generator.direct.metadata.html.SingleHtmlBaseHookCompatTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.015 s -- in com.metaobjects.generator.direct.metadata.html.SingleHtmlBaseHookCompatTest
[INFO] Running com.metaobjects.generator.direct.metadata.jsonschema.MetaDataJsonSchemaGeneratorTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 s -- in com.metaobjects.generator.direct.metadata.jsonschema.MetaDataJsonSchemaGeneratorTest
[INFO] Running com.metaobjects.generator.direct.object.JavaObjectCodeGeneratorTypeFlavorTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 s -- in com.metaobjects.generator.direct.object.JavaObjectCodeGeneratorTypeFlavorTest
[INFO] Running com.metaobjects.generator.direct.object.JavaValueObjectCompileRunTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.077 s -- in com.metaobjects.generator.direct.object.JavaValueObjectCompileRunTest
[INFO] Running com.metaobjects.generator.direct.object.JavaAbstractClassEmissionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.metaobjects.generator.direct.object.JavaAbstractClassEmissionTest
[INFO] Running com.metaobjects.generator.direct.object.GeneratedBindingProviderTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 s -- in com.metaobjects.generator.direct.object.GeneratedBindingProviderTest
[INFO] Running com.metaobjects.generator.direct.object.JavaPojoAwareCompileRunTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 s -- in com.metaobjects.generator.direct.object.JavaPojoAwareCompileRunTest
[INFO] Running com.metaobjects.generator.direct.object.JavaAccessorNameCollisionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 s -- in com.metaobjects.generator.direct.object.JavaAccessorNameCollisionTest
[INFO] Running com.metaobjects.generator.direct.object.EjectedBaseGeneratorsCompileTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.187 s -- in com.metaobjects.generator.direct.object.EjectedBaseGeneratorsCompileTest
[INFO] Running com.metaobjects.generator.direct.object.StandaloneExtractorGeneratorTest
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 s -- in com.metaobjects.generator.direct.object.StandaloneExtractorGeneratorTest
[INFO] Running com.metaobjects.generator.direct.object.GeneratedHeaderIsStableTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.104 s -- in com.metaobjects.generator.direct.object.GeneratedHeaderIsStableTest
[INFO] Running com.metaobjects.generator.direct.object.DownstreamCustomizationTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 s -- in com.metaobjects.generator.direct.object.DownstreamCustomizationTest
[INFO] Running c

... [77900571 bytes truncated] ...

oinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.seeAlso.array] for attribute [seeAlso] with value [null] on [object:entity:verify::demo::Gadget]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Attribute [aliases] not found in [object:entity:verify::demo::Gadget], returning null
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.aliases.array] for attribute [aliases] with value [null] on [object:entity:verify::demo::Gadget]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.cache.HybridCache -- Cleared hybrid cache (all cache types)
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.MetaData -- Created MetaData: field:long:id
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.field.MetaField -- Created MetaField: field:long:id with dataType: LONG
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Enforcing 102 constraints for adding [LongField[field:long]{id}{dataType=LONG, defaultValue=null}] to [EntityMetaObject[object:entity]{verify::demo::Gadget}@kotlin_spi_test]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Attribute [seeAlso] not found in [field:long:id], returning null
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.seeAlso.array] for attribute [seeAlso] with value [null] on [field:long:id]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Attribute [aliases] not found in [field:long:id], returning null
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.aliases.array] for attribute [aliases] with value [null] on [field:long:id]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.cache.HybridCache -- Cleared hybrid cache (all cache types)
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.MetaData -- Created MetaData: field:string:label
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.field.MetaField -- Created MetaField: field:string:label with dataType: STRING
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Enforcing 102 constraints for adding [StringField[field:string]{label}{dataType=STRING, defaultValue=null}] to [EntityMetaObject[object:entity]{verify::demo::Gadget}@kotlin_spi_test]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Attribute [seeAlso] not found in [field:string:label], returning null
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.seeAlso.array] for attribute [seeAlso] with value [null] on [field:string:label]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Attribute [aliases] not found in [field:string:label], returning null
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.constraint.ConstraintEnforcer -- Validating attribute constraint [*.*.aliases.array] for attribute [aliases] with value [null] on [field:string:label]
10:44:10.605 [ForkJoinPool.commonPool-worker-1] DEBUG com.metaobjects.cache.HybridCache -- Cleared hybrid cache (all cache types)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.metaobjects.mojo.KotlinGeneratorViaGenSpiTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 201, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- jacoco:0.8.13:report (report) @ metaobjects-maven-plugin ---
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/jacoco.exec
[INFO] Analyzed bundle 'MetaObjects :: Maven Plugin' with 32 classes
[INFO] 
[INFO] --- jar:3.4.1:jar (default-jar) @ metaobjects-maven-plugin ---
[INFO] Building jar: ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/metaobjects-maven-plugin-8.1.0.jar
[INFO] 
[INFO] --- plugin:3.15.1:addPluginArtifactMetadata (default-addPluginArtifactMetadata) @ metaobjects-maven-plugin ---
[INFO] This Mojo is not used in Maven version 3.9.0 and above
[INFO] 
[INFO] --- jacoco:0.8.13:prepare-agent-integration (prepare-agent-integration) @ metaobjects-maven-plugin ---
[INFO] argLine set to -javaagent:/tmp/mo-m2-01M4/org/jacoco/org.jacoco.agent/0.8.13/org.jacoco.agent-0.8.13-runtime.jar=destfile=~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/jacoco-it.exec,excludes=**/*Test*:**/test/**:**/*Exception*
[INFO] 
[INFO] --- jacoco:0.8.13:report-integration (report-integration) @ metaobjects-maven-plugin ---
[INFO] Skipping JaCoCo execution due to missing execution data file.
[INFO] 
[INFO] --- jacoco:0.8.13:report-aggregate (report-aggregate) @ metaobjects-maven-plugin ---
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/jacoco.exec
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/codegen-base/target/jacoco.exec
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/codegen-plantuml/target/jacoco.exec
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/codegen-spring/target/jacoco.exec
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/codegen-kotlin/target/jacoco.exec
[INFO] Analyzed bundle 'metaobjects-codegen-base' with 48 classes
[INFO] Analyzed bundle 'metaobjects-codegen-plantuml' with 3 classes
[INFO] Analyzed bundle 'metaobjects-codegen-spring' with 59 classes
[INFO] Analyzed bundle 'metaobjects-codegen-kotlin' with 76 classes
[INFO] 
[INFO] --- jacoco:0.8.13:check (coverage-check) @ metaobjects-maven-plugin ---
[INFO] Loading execution data file ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/jacoco.exec
[INFO] Analyzed bundle 'metaobjects-maven-plugin' with 32 classes
[INFO] All coverage checks have been met.
[INFO] 
[INFO] --- install:3.1.3:install (default-install) @ metaobjects-maven-plugin ---
[INFO] Installing ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/pom.xml to /tmp/mo-m2-01M4/com/metaobjects/metaobjects-maven-plugin/8.1.0/metaobjects-maven-plugin-8.1.0.pom
[INFO] Installing ~/.no-mistakes/worktrees/5b8c6c97e760/01M4X551RT1JY1BD8XF83S8E5Q/server/java/maven-plugin/target/metaobjects-maven-plugin-8.1.0.jar to /tmp/mo-m2-01M4/com/metaobjects/metaobjects-maven-plugin/8.1.0/metaobjects-maven-plugin-8.1.0.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for MetaObjects :: Code Generation :: Base 8.1.0:
[INFO] 
[INFO] MetaObjects :: Code Generation :: Base ............. SUCCESS [  4.422 s]
[INFO] MetaObjects :: Code Generation :: PlantUML ......... SUCCESS [  1.013 s]
[INFO] MetaObjects :: Codegen :: Spring ................... SUCCESS [  9.900 s]
[INFO] MetaObjects :: Codegen :: Kotlin ................... SUCCESS [01:16 min]
[INFO] MetaObjects :: Maven Plugin ........................ SUCCESS [ 26.776 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:58 min
[INFO] Finished at: 2026-10-14T10:44:11-04:00
[INFO] ------------------------------------------------------------------------
  • Evidence: ci-local --quick (local file: ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-ci-local-quick.log)
Evidence: Targeted TypeScript suites
bun test v1.3.14 (0d9b296a)

 75 pass
 0 fail
 390 expect() calls
Ran 75 tests across 13 files. [114.00ms]
bun test v1.3.14 (0d9b296a)

 9 pass
 0 fail
 43 expect() calls
Ran 9 tests across 2 files. [190.00ms]
bun test v1.3.14 (0d9b296a)

 17 pass
 0 fail
 330 expect() calls
Ran 17 tests across 1 file. [478.00ms]
  • Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (1h50m52s)

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • 🚨 server/java/maven-plugin/src/main/java/com/metaobjects/mojo/DocsMojo.java:16 - The removed docs goal lost its default phase, so the usual 8.0 pom never reaches the refusal and the build passes with no documentation. The 8.0 DocsMojo was @Mojo(name = &#34;docs&#34;, defaultPhase = LifecyclePhase.GENERATE_RESOURCES, ...). The stub is @Mojo(name = &#34;docs&#34;, threadSafe = true) with no phase. Maven 3 binds an &lt;execution&gt; that has no &lt;phase&gt; through the mojo descriptor's phase (DefaultLifecycleMappingDelegate looks up mappings.get(mojoDescriptor.getPhase())). When that phase is null, Maven silently skips the goal. Failing sequence: an 8.0 pom with &lt;execution&gt;&lt;goals&gt;&lt;goal&gt;generate&lt;/goal&gt;&lt;goal&gt;docs&lt;/goal&gt;&lt;/goals&gt;&lt;/execution&gt;, which is the migration guide's own "Before" shape, and the docs parameters left at their defaults, or only &lt;outputDirectory&gt; set, or set in the docs execution's own &lt;configuration&gt;. On 8.1.0, generate finds no &lt;language&gt;/&lt;apiSubDir&gt;/&lt;layout&gt;/&lt;modelBaseUrl&gt; in its own configuration and does not refuse. docs is never scheduled, so DocsMojo.execute() never throws. mvn install is green and writes no api reference. Amendment 9 and the commit message promise the opposite: "a build never passes while writing no documentation". The same false claim is in docs/features/migrations/docs-are-generators.md:25 ("A pom that binds the docs goal fails with:"), CHANGELOG.md:998 ("A pom that still binds the goal fails with a line...") and ADR-0034 Amendment 9 (line ~1127, "the docs goal is kept only to print the line above"). RemovedDocsGoalTest.theDocsGoalSaysItWasRemoved... builds new DocsMojo() directly, so it never goes through lifecycle binding and cannot see this. Fix: restore defaultPhase = LifecyclePhase.GENERATE_RESOURCES on the stub's @Mojo so a bound execution runs and fails. Add a check of the goal's descriptor phase (the generated plugin descriptor is the contract Maven reads), or a lifecycle-level test, that fails without the default phase.
  • ℹ️ agent-context/servers/java.meta.json:5 - docsCommand is now mvn metaobjects:generate (same change in kotlin.meta.json:5). For a Java- or Kotlin-only stack (no Node docs surface), agent-context/templates/always-on.md.mustache:32 and :40 render this agent instruction: "mvn metaobjects:generate writes ONE reference file ... Not there? Run mvn metaobjects:generate." Since 8.1.0, generate writes AGENT-API.md only when the pom lists a JavaApiDocsGenerator/KotlinApiDocsGenerator entry. An agent following the instruction on a pom without that entry runs generate, gets no file, and has no hint why. Fix: add a clause to the non-Node branch, e.g. "(when the pom lists the api-docs generator)". Two comments outside the changed set also still name the removed goal: server/typescript/packages/sdk/src/agent-context/assemble.ts:30 and server/typescript/packages/cli/src/lib/docs-drift.ts:28.

🔧 Fix applied.
1 info still open:

  • ℹ️ docs/features/own-your-codegen.md:996 - The new hand-over table says Java entity copies "the eight files above, ExtractorCodeGenerator among them". That is false on this branch. GeneratorRegistry.ENTITY_EMIT (codegen-spring GeneratorRegistry.java:277-280) gives entity six extra classes and no ExtractorCodeGenerator, so an eject writes seven files. The same page says so at line 937 ("entity copies seven files") and line 957 ("entity writes no extractors; those are the extractor generator's"). An adopter who follows the row looks for an ExtractorCodeGenerator.java that -Dnames=entity does not write. Fix: change the row to "the seven files above (its writers); ExtractorCodeGenerator is extractor's", or drop the row, because those writers were handed over before 1.1. Stale sibling: the comment in EjectSupportTest.java aFileTwoGeneratorsCopyComparesIdenticalWhicheverOneWroteIt ("The extractor is copied with entity") repeats the same old claim.
🔧 **Test** - 1 issue found → auto-fixed ✅
  • ⚠️ live validation verdict: inconclusive (7 of 10 scenarios were driven live against the product); untested: Always-on agent-context wording for Java-only stacks qualifies api-docs clause (R11), Full Java regression of five touched modules, Repo quick gate and TypeScript targeted suites
  • Live validation: ⚠️ inconclusive - 7 of 10 scenarios driven live against the product
Scenario Result Live Evidence
Generate runs nothing undeclared: pom with no generator entries, mvn metaobjects:generate exits 0 and writes no docs ✅ pass live live-s1-generate-no-entries.log; /tmp/mo-live-01M4/p1 target empty
Declared api-docs entry writes the reference under declared outputDir ✅ pass live live-s2-generate-api-docs-entry.log
Pom still binding removed docs goal fails loudly with removal line ✅ pass live live-s3-bound-docs-goal-refused.log (BUILD FAILURE, 'metaobjects:docs was removed in 8.1.0 ...')
8.0 pom with docs params is refused by generate before any write ✅ pass live live-s3a-generate-with-1.0-config-refused.log
Adopter ejects api-docs for Java and Kotlin into one codegen module; printed remedy builds both trees ✅ pass live live-s5a, live-s5b, live-s5c-build-java-kotlin-module.log (BUILD SUCCESS, 30 apidocs classes)
One eject call naming Java-only and Kotlin-only generators builds both trees with no misleading warning ✅ pass live live-s6-eject-two-ports-one-call.log, live-s6b-build-two-port-module.log
Second-port eject into a pom lacking its needs prints remedy and never edits the pom ✅ pass live live-s5b-eject-api-docs-kotlin.log
Always-on agent-context wording for Java-only stacks qualifies api-docs clause (R11) ⏸️ untested no Only fixture-rendered tests cover this text. Live check needs a Java-only project through the meta CLI agent-context init, which was not built or driven.
Full Java regression of five touched modules ⏸️ untested no Unit and integration suites, not a product run. No live pass claimed.
Repo quick gate and TypeScript targeted suites ⏸️ untested no Test suites, not a product run. No live pass claimed.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • Plan step 1: mvn install of codegen-base, codegen-spring, codegen-kotlin, codegen-plantuml, maven-plugin (BUILD SUCCESS)
  • Plan step 2: Java five-module test suites (1212 tests, 0 failures)
  • Plan step 3: sdk agent-context tests (75 pass)
  • Plan step 4: cli init-agent-context and init-docs-only tests (9 pass)
  • Plan step 5: codegen-ts generator-registry test (17 pass)
  • Plan step 6: scripts/ci-local.sh --quick (LOCAL CI PASSED)
  • Live S1: generate with no entries writes no docs
  • Live S2: declared api-docs entry writes AGENT-API.md, README.md and entity pages
  • Live S3: bound docs goal fails with removal message
  • Live S3a: 1.0 docs config refused before writing
  • Live S5a/S5b: api-docs ejected for java then kotlin; second call prints remedy and leaves pom untouched
  • Live S5c: remedy applied, codegen module compiles (30 apidocs classes)
  • Live S6: one eject call for html-docs and exposed-table; module compiles with both sets of classes

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

  • Live validation: ✅ go - 5 of 7 scenarios driven live against the product
Scenario Result Live Evidence
Java-only project: meta init --docs-only --server java writes AGENTS.md whose Not-there line says to declare JavaApiDocsGenerator as a <generator> entry, then run mvn metaobjects:generate ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
Kotlin-only project: the same command with --server kotlin names KotlinApiDocsGenerator ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
Regression guard: Python, C# and TypeScript render the unchanged Not there? Run &lt;docsCommand&gt; line, and AGENTS.md matches CLAUDE.md byte for byte for every port ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-meta-init-not-there.txt
The two generator class names the line prints exist in the Java and Kotlin sources ⏸️ untested no The prior payload recorded only a source search (JavaApiDocsGenerator.java, KotlinApiDocsGenerator.kt) with live=false. That is not a live run of the product, so no live pass is established.
Maven plugin, codegen-base, codegen-plantuml, codegen-spring and codegen-kotlin install with their full test suites (including the descriptor-phase test for the docs goal, the eject tests and the both… ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-java-step2-tests.log
Quick repo gate (scripts/ci-local.sh --quick) passes ✅ pass live ~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-ci-local-quick.log
Targeted TypeScript suites: sdk agent-context and conformance, cli init-agent-context and init-docs-only, codegen-ts generator-registry ⏸️ untested no The prior payload recorded this scenario with live=false, so it did not establish a live result. The suite log exists but was not recorded as a live drive of the product.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • bun run server/typescript/packages/cli/bin/meta.ts init --docs-only --server {java,kotlin,python,csharp,typescript} in throwaway temp directories, reading the Not-there line from each AGENTS.md and comparing AGENTS.md with CLAUDE.md
  • mvn -B -Dmaven.repo.local=/tmp/mo-m2-01M4 -DskipTests -pl codegen-base,codegen-spring,codegen-kotlin,codegen-plantuml,maven-plugin -am install (BUILD SUCCESS)
  • mvn -B -Dmaven.repo.local=/tmp/mo-m2-01M4 -pl codegen-base,codegen-plantuml,codegen-spring,codegen-kotlin,maven-plugin install (111, 7, 374, 519 and 201 tests, 0 failures)
  • bun test for sdk test/agent-context-conformance.test.ts and test/agent-context (75 pass)
  • bun test for cli test/unit/init-agent-context.test.ts and test/unit/init-docs-only.test.ts (9 pass)
  • bun test for packages/codegen-ts/test/generator-registry.test.ts (17 pass)
  • scripts/ci-local.sh --quick (LOCAL CI PASSED, 4 lanes skipped by --quick)
  • Baseline already run by the pipeline: scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…r Java docs generators eject

An ejected Java or Kotlin generator was one file over packaged emitters, so a defect in the
enum, record-builder, extraction-mirror or prompt-format writers waited for a release. Eject
now copies those emitters with the generator, into its package, and points the imports the
copies have of one another at the copies.

- Java dto and value-object take Fr019SharedEnum and SpringRecordBuilder, output-parser takes
  Fr019SharedEnum, output-prompt takes OutputFormatSpecEmitter, and entity takes
  ExtractorCodeGenerator, so an owned entity writes its extractors through the owned copy.
- Kotlin entity takes KotlinEnumEmitter, output-parser takes KotlinExtractSchemaEmitter and
  output-prompt takes KotlinOutputFormatSpecEmitter. Kotlin Fr019SharedEnum stays in the package
  as engine: it writes nothing and the type mapper takes its config as an argument.
- api-docs is a generator in Java and Kotlin, run through a new DocsGenerator marker.
  metaobjects:docs runs the one the project lists for a surface in place of the packaged one;
  generate and verify skip it. Eject copies the page model, builder, renderer and path math,
  and for Java the page templates, with the copied template provider pointed at them.
- plantuml, html-docs, ai-docs and json-schema ship eject source, each with its writer.
  SingleHtmlDirectGeneratorBase drives the new HtmlDirectWriter, and the JSON Schema and AI
  documentation attributes move to CodeGenMetaDataProvider, with deprecated forwarders left on
  the two generators.

The eject contract's pending list holds no java or kotlin entry. Packaged output is unchanged.
…Html hook; verify the emitter recipe

- Kotlin GeneratorInfo keeps its six-argument constructor for JVM callers (@jvmoverloads), with a
  Java caller test.
- SingleHtmlDirectGeneratorBase still calls a subclass's writeHtml(MetaDataHtmlDocumentationWriter)
  through a deprecated bridge, with a test.
- The recipe for owning the emitters of an older owned copy is run against copies made by the last
  released artifacts, in Java and in Kotlin, and the docs say what it took: the copied Kotlin
  emitters import one another through the scratch package.
…r Java docs generators eject

An ejected Java or Kotlin generator was one file over packaged emitters, so a defect in the
enum, record-builder, extraction-mirror or prompt-format writers waited for a release. Eject
now copies those emitters with the generator, into its package, and points the imports the
copies have of one another at the copies.

- Java dto and value-object take Fr019SharedEnum and SpringRecordBuilder, output-parser takes
  Fr019SharedEnum, output-prompt takes OutputFormatSpecEmitter, and entity takes
  ExtractorCodeGenerator, so an owned entity writes its extractors through the owned copy.
- Kotlin entity takes KotlinEnumEmitter, output-parser takes KotlinExtractSchemaEmitter and
  output-prompt takes KotlinOutputFormatSpecEmitter. Kotlin Fr019SharedEnum stays in the package
  as engine: it writes nothing and the type mapper takes its config as an argument.
- api-docs is a generator in Java and Kotlin, run through a new DocsGenerator marker.
  metaobjects:docs runs the one the project lists for a surface in place of the packaged one;
  generate and verify skip it. Eject copies the page model, builder, renderer and path math,
  and for Java the page templates, with the copied template provider pointed at them.
- plantuml, html-docs, ai-docs and json-schema ship eject source, each with its writer.
  SingleHtmlDirectGeneratorBase drives the new HtmlDirectWriter, and the JSON Schema and AI
  documentation attributes move to CodeGenMetaDataProvider, with deprecated forwarders left on
  the two generators.

The eject contract's pending list holds no java or kotlin entry. Packaged output is unchanged.
…Html hook; verify the emitter recipe

- Kotlin GeneratorInfo keeps its six-argument constructor for JVM callers (@jvmoverloads), with a
  Java caller test.
- SingleHtmlDirectGeneratorBase still calls a subclass's writeHtml(MetaDataHtmlDocumentationWriter)
  through a deprecated bridge, with a test.
- The recipe for owning the emitters of an older owned copy is run against copies made by the last
  released artifacts, in Java and in Kotlin, and the docs say what it took: the copied Kotlin
  emitters import one another through the scratch package.
…the docs goal

Every generator, documentation included, is a <generator> entry in the list
metaobjects:generate runs, and nothing is written that <generators> does not
declare (ADR-0034 Amendment 9).

- JavaApiDocsGenerator and KotlinApiDocsGenerator read outputDir, apiSubDir,
  layout, projectName and modelBaseUrl from <args>. The DocsGenerator marker
  and the skips in metaobjects:generate and metaobjects:verify are deleted.
- metaobjects:docs only fails, with a line naming metaobjects:generate and
  the migration section. generate and verify refuse a pom that still sets
  the old goal's <language>, <apiSubDir>, <layout> or <modelBaseUrl>, so a
  build never passes while writing no documentation.
- The Java entity generator writes no <Name>Extractor; the extractor
  generator is the one entry that does, and entity no longer copies it on
  eject. EmitsObjectExtractors and the emitExtractors arg are removed (both
  were new in this unreleased version).
- Tests: api-docs through generate and verify, the removed goal and the
  refused 8.0 configuration, and a pom that declares nothing writes zero
  files (Java and Kotlin).
…docs generators

Amendment 9 is the cross-port rule: every generator, docs included, is an
entry in the project's native generator list; one command runs it; no docs
command, goal, marker, skip, second list or default set. It fixes, per port,
where the list lives, the option names that replace the docs flags, the exact
message a removed docs command prints, the exact error a gen command prints
for 1.0-shape configuration, and the shape of the migration guide. It
supersedes Amendment 7's docs seam, ADR-0021 D1, ADR-0022 Part 2 D1 and the
docs-door parts of ADR-0025.

- docs/features/migrations/docs-are-generators.md: the "Java and Kotlin"
  section, with before and after pom.xml and a parameter table.
- Java and Kotlin port pages, own-your-codegen, the agent-context codegen
  references and their conformance fixtures, and the CHANGELOG 1.1.0 entries
  for these two ports.
@dmealing dmealing changed the title feat(maven-plugin): make all Java and Kotlin generators ejectable feat(maven-plugin)!: one way to generate in Java and Kotlin; api-docs is a declared generator and the docs goal is removed Oct 14, 2026
@dmealing
dmealing merged commit 66056ae into main Oct 14, 2026
1 check passed
@dmealing
dmealing deleted the fm/mo-1-1-0-eject-java branch October 14, 2026 14:53
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