Skip to content

Ship the API reference inside the jar so offline agents stop decompiling - #177

Merged
jcgueriaud1 merged 1 commit into
masterfrom
issue-176
Sep 8, 2026
Merged

jcgueriaud1 merged 1 commit into
masterfrom
issue-176

Conversation

@jcgueriaud1

Copy link
Copy Markdown
Collaborator

Fixes #176.

Agents working offline decompiled the DramaFinder jar to discover its API, because every agent-facing doc was behind an HTTP fetch. A dependency resolve puts only the jar and the pom in ~/.m2 — no sources jar, no classifier — so the main jar is the only channel that always arrives.

What ships in the jar now

Verified on a clean package:

Entry Raw In jar
META-INF/dramafinder/START-HERE.md 3.9 KB 1.9 KB
META-INF/dramafinder/api-reference.md 110 KB 19 KB
META-INF/dramafinder/agent-api-reference.md 3.4 KB 1.4 KB

22 KB compressed, on a 177 KB jar. Discovery is now two commands:

jar tf ~/.m2/…/dramafinder-*.jar | grep -v class
unzip -p ~/.m2/…/dramafinder-*.jar META-INF/dramafinder/api-reference.md

The two references stay generated into skills/ — the copy that travels with the Claude Code plugin — and are copied from there by maven-resources-plugin, so there is one source of truth and no 110 KB duplicate committed to git. START-HERE.md is hand-written in src/main/resources and deliberately contains nothing that can drift: a working test skeleton, the tag-to-wrapper naming rule, grep recipes for navigating the full reference, and the three things that are easy to get wrong.

The gap that mattered most

AbstractBasePlaywrightIT and HasTestView appeared in no generated doc — generate-api-reference.java only walked element/. That is what sent the agent in the trial to javap -c on bytecode to work out how to obtain a Page.

The reference now opens with a Test setup section: a working @SpringBootTest(RANDOM_PORT) skeleton, then both types rendered from source. The renderer gained an includeProtected mode for this, since page, getPage() and isHeadless() are the API a subclass actually writes against and the public-only renderer dropped them.

Also in here

  • CellElement.getText() / assertText(String). getLocator() returns the grid, not the cell; the rendered text lives behind getCellContentLocator(). That trap cost a compile round-trip in the trial. Covered by two new tests in GridBasicViewIT.
  • Manifest entries naming all three files, so unzip -p <jar> META-INF/MANIFEST.MF also leads to them.
  • <description> in the pom points at the bundled docs — the pom is the only other file an offline reader always has, and the trial shows agents cat it.
  • The offline unzip -p fallback documented in llms.txt, AGENTS.md, GEMINI.md, README.md and both SKILL.md files. Every one of them previously assumed an HTTP fetch would succeed.
  • Workflow path filters widened to src/main/java/org/vaadin/addons/dramafinder/*.java, so CI regenerates the reference when the setup API changes.
  • Long constant initializers are elided in the reference. WAIT_FOR_VAADIN_SCRIPT is a page of JavaScript; DateTimeFormatter ISO_LOCAL_DATE_TIME was already a builder chain that told the reader nothing.

DocumentationBundledInJarTest fails if any of the three stops being bundled, or if the reference loses its test-setup or element-index section.

Verification

  • mvn -o clean test — 8 tests pass (the 3 new ones included; the "0" surefire prints for the class is this repo's parallel-class reporting quirk, -Djunit.jupiter.execution.parallel.enabled=false shows 3 + 5 = 8).
  • mvn -Pit verify -Dit.test=GridBasicViewIT — 38 pass, including testCellGetText and testCellAssertText.
  • Clean package, then extracted ### GridElement straight out of the jar with one unzip -p.

Not in scope

The factory-uniformity sweep from the issue's item 4. Adding get(Page) / getById across 55 wrappers needs a per-element judgment call and changes the public API surface — worth its own PR. The narrow, concrete part of that item (the CellElement naming trap) is done here.

Unrelated, spotted while testing: mvn -Pit bakes META-INF/VAADIN/** into target/classes, so a -Pit package produces a 6.8 MB jar instead of 177 KB. release-build.yml runs mvn -Prelease deploy without it, so releases are unaffected — but the two profiles must never be combined.

🤖 Generated with Claude Code

Agents working offline decompiled the jar to discover the API, because
every agent-facing doc was behind an HTTP fetch. A dependency resolve puts
only the jar and the pom in ~/.m2 — no sources jar, no classifier — so the
main jar is the only channel that always arrives.

- Copy api-reference.md and agent-api-reference.md into the jar under
  META-INF/dramafinder/, next to a new hand-written START-HERE.md
  (test skeleton, tag-to-wrapper rule, the three common traps). They stay
  generated into skills/, so there is still one source of truth.
- Name all three in the manifest, and point at them from the pom
  description — the two files an offline reader always has.
- Cover AbstractBasePlaywrightIT and HasTestView in the reference. They
  were in no generated doc at all, which is what sent agents to `javap -c`
  to work out how to obtain a Page.
- Add CellElement.getText()/assertText(). getLocator() returns the grid,
  not the cell, and that trap cost a compile round-trip.
- Document the offline `unzip -p` fallback in llms.txt, AGENTS.md,
  GEMINI.md, README.md and both SKILL.md files.

DocumentationBundledInJarTest guards the bundling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jcgueriaud1
jcgueriaud1 merged commit 85617b4 into master Sep 8, 2026
5 checks passed
@jcgueriaud1
jcgueriaud1 deleted the issue-176 branch September 8, 2026 10:35
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.

Ship the API reference inside the jar: offline agents decompile DramaFinder to discover its API

1 participant