Repository navigation
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
Conversation
…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.
… docs wiring note
…de deprecated mustache
…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.
… docs wiring note
…de deprecated mustache
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 listmvn metaobjects:generateruns, with its options in<args>; nothing is written that the list does not declare. Themetaobjects:docsgoal is removed (it remains only to fail with the migration line), theDocsGeneratormarker and the generate/verify skips are gone, an 8.0 pom's docs parameters are refused instead of silently writing nothing, and Javaentityno longer writes<Name>Extractorclasses the pom did not declare. The branch also carries the earlier eject work: every Java and Kotlin generator excepttemplateand the deprecatedmustacheis ejectable with its emitters, andeject.pendingholds 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
generateandverifywrite the same pages, byte for byte, as the published 8.0.13metaobjects: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 untouchedmvn clean packagebuilds 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.jsonconflict) 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
JavaApiDocsGeneratorandKotlinApiDocsGeneratorregisterapi-docsin the generator registry. The unlisted Java generatorsplantuml,html-docs,ai-docsandjson-schemaare also registered, and eject now covers all but the deprecatedmustache.metaobjects:docsno longer runs anything.DocsMojois now a stub that fails with a pointer to the migration section, andRemovedDocsGoalTestcovers 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).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 Javaentitygenerator no longer writes a<Name>Extractorthe pom did not declare.EmitsObjectExtractorsis deleted, andExtractorCodeGeneratoris now a separateextractorentry.EjectSupportandMetaDataEjectMojoare extended and are covered by new and updated eject tests (ApiDocsEjectBothPortsTest,EjectSupportTest,EjectContractTest, the KotlinEjectContractTest).fixtures/generator-registry-conformance/registry.jsondrops the Java/Kotlin entries fromeject.pendingand updates the engine lists.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.CHANGELOG.md,docs/features/own-your-codegen.md, the Java and Kotlin port docs and the READMEs are updated.sdkassemble step has a matching small change with a test.api-docsfrom 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/EmitsObjectExtractorsseam affects no released owned copy, because Javaentitybecame 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-onlyCLI 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.meta init --docs-only --server javawrites AGENTS.md whose Not-there line says to declare JavaApiDocsGenerator as a <generator> entry, then runmvn metaobjects:generate--server kotlinnames KotlinApiDocsGeneratorNot there? Run <docsCommand>line, and AGENTS.md matches CLAUDE.md byte for byte for every portscripts/ci-local.sh --quick) passesEvidence: meta init Not-there line per port (live CLI output)
Evidence: Java five-module install with tests
~/.no-mistakes/evidence/01M4X551RT1JY1BD8XF83S8E5Q/run3-ci-local-quick.log)Evidence: Targeted TypeScript suites
Pipeline
Updates from git push no-mistakes
✅ **Intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
server/java/maven-plugin/src/main/java/com/metaobjects/mojo/DocsMojo.java:16- The removeddocsgoal 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 = "docs", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, ...). The stub is@Mojo(name = "docs", threadSafe = true)with no phase. Maven 3 binds an<execution>that has no<phase>through the mojo descriptor's phase (DefaultLifecycleMappingDelegate looks upmappings.get(mojoDescriptor.getPhase())). When that phase is null, Maven silently skips the goal. Failing sequence: an 8.0 pom with<execution><goals><goal>generate</goal><goal>docs</goal></goals></execution>, which is the migration guide's own "Before" shape, and the docs parameters left at their defaults, or only<outputDirectory>set, or set in the docs execution's own<configuration>. On 8.1.0,generatefinds no<language>/<apiSubDir>/<layout>/<modelBaseUrl>in its own configuration and does not refuse.docsis never scheduled, soDocsMojo.execute()never throws.mvn installis 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 thedocsgoal 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... buildsnew DocsMojo()directly, so it never goes through lifecycle binding and cannot see this. Fix: restoredefaultPhase = LifecyclePhase.GENERATE_RESOURCESon the stub's@Mojoso 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-docsCommandis nowmvn 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:generatewrites ONE reference file ... Not there? Runmvn 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 Javaentitycopies "the eight files above,ExtractorCodeGeneratoramong them". That is false on this branch.GeneratorRegistry.ENTITY_EMIT(codegen-spring GeneratorRegistry.java:277-280) givesentitysix extra classes and noExtractorCodeGenerator, so an eject writes seven files. The same page says so at line 937 ("entitycopies seven files") and line 957 ("entitywrites no extractors; those are theextractorgenerator's"). An adopter who follows the row looks for anExtractorCodeGenerator.javathat-Dnames=entitydoes not write. Fix: change the row to "the seven files above (its writers);ExtractorCodeGeneratorisextractor's", or drop the row, because those writers were handed over before 1.1. Stale sibling: the comment in EjectSupportTest.javaaFileTwoGeneratorsCopyComparesIdenticalWhicheverOneWroteIt("The extractor is copied with entity") repeats the same old claim.🔧 **Test** - 1 issue found → auto-fixed ✅
scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsPlan 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 docsLive S2: declared api-docs entry writes AGENT-API.md, README.md and entity pagesLive S3: bound docs goal fails with removal messageLive S3a: 1.0 docs config refused before writingLive S5a/S5b: api-docs ejected for java then kotlin; second call prints remedy and leaves pom untouchedLive 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.
meta init --docs-only --server javawrites AGENTS.md whose Not-there line says to declare JavaApiDocsGenerator as a <generator> entry, then runmvn metaobjects:generate--server kotlinnames KotlinApiDocsGeneratorNot there? Run <docsCommand>line, and AGENTS.md matches CLAUDE.md byte for byte for every portscripts/ci-local.sh --quick) passesscripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchainsbun 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.mdmvn -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 testfor sdk test/agent-context-conformance.test.ts and test/agent-context (75 pass)bun testfor cli test/unit/init-agent-context.test.ts and test/unit/init-docs-only.test.ts (9 pass)bun testfor 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.