ID

R951

Status

Backlog

Bucket

dx

Priority

1

Theme

dev-loop

Created

2026-09-15

Updated

2026-09-15

A consumer pom that adds a dependency to the plugin realm can shadow the jOOQ graphitron parses with, and the store will not open

Goal

A consumer whose build adds a dependency to the graphitron plugin’s own <dependencies> gets a working graphitron:dev and graphitron:generate. Today it can get neither, with a failure that names a view body and points at nothing the author wrote or can change.

What happens

Found while trying to price the refresh pass on the sis consumer. The module’s pom adds sis-service to the plugin block’s <dependencies>, which is the ordinary way a consumer puts its own service classes where the classpath scan can read them. That dependency brings org.jooq.pro:jooq:3.19.18 into the plugin realm, where graphitron’s own org.jooq:jooq:3.20.11 already is. The two artifacts carry the same package and class names under different group ids, so Maven’s version mediation never compares them, both land in the realm, and which one answers a given class load is an ordering accident.

When the older one answers, the failure is this:

graphitron: could not open the fact store at <cache>/...-dev:
the stored definition of view intent_field_scope_table_live did not parse,
and a definition walk reads it: Token ')' expected: [69:9]

ViewReferences.parse reads each registered rule’s stored definition back with dsl.parser().parseQuery so MaterializeDependencies.populate can derive the refresh order, and H2 renders that view’s derived table with its UNION ALL arms parenthesised. jOOQ 3.20.11 parses that; 3.19.18 does not. So the store cannot be created at all, on every goal, and no schema the author writes makes any difference.

Three things make this worth an item rather than a consumer-side note. The failure is total, not degraded: openAt cannot fall back to warmth because the schema never gets created. It is invisible in this repository, every reactor test booting a store with one jOOQ on the classpath. And it is silent about its cause: nothing in the message mentions jOOQ, a realm, or the consumer’s own pom, so the reading an author will reach for first is that their schema broke the generator.

Plan

Not settled; this is a Backlog stub with the diagnosis in it. Two directions worth pricing against each other, and they are not exclusive.

Stop depending on the ordering. The parse exists to answer which relations a rule reads, and the relations a rule reads are a function of the DDL, which is a resource this project ships and hashes already. Deriving the read sets at build time from the authored DDL, rather than at boot from the engine’s rendering of it, removes the parser from the boot path entirely and would also remove the per-boot cost of the walk. That is the larger change and probably the right one.

Or make the realm’s jOOQ unambiguous, by whatever the plugin can do about a dependency it does not control: an exclusion the plugin declares, a check at startup that names the clash in the author’s terms, or both. A check alone does not fix anything, but a failure that says which two artifacts are present and which pom line added one of them is worth a great deal more than the current message, and it is cheap.

Whatever lands, the acceptance is the same: a module whose plugin block adds a dependency carrying a second jOOQ opens its store and completes a round.