MetamodelProcessor.initClassFields (storm-metamodel-processor/src/main/java/st/orm/metamodel/MetamodelProcessor.java:1136-1192) has no cycle guard; the guards at :81-110 only prevent generating a file twice, and navPath (:1430-1434) applies exclusively to Navigable*/*Ref* classes. For two entities referencing each other with non-Ref @FK fields, the generated metamodels construct each other from static final INSTANCE initializers, so first metamodel use after a clean build dies at class initialization (ExceptionInInitializerError/StackOverflowError). The comment at :90 claims the guard prevents exactly this. KSP has the same shape at .kt:1596, doubled by the Nullable metamodel variants.
Note the engine already rejects a self-referencing non-Ref FK at template level (TemplatePreparation.java:955-959), which is the right model: a cycle needs a Ref boundary to be loadable at all. So the processors should reject non-Ref FK cycles at generation time with a diagnostic naming the cycle and the fix (mark one side Ref), rather than emitting code that compiles and then cannot initialize.
Reproduce with a mutual pair, e.g. record Owner(@PK Integer id, @FK Pet pet) / record Pet(@PK Integer id, @FK Owner owner).
MetamodelProcessor.initClassFields(storm-metamodel-processor/src/main/java/st/orm/metamodel/MetamodelProcessor.java:1136-1192) has no cycle guard; the guards at:81-110only prevent generating a file twice, andnavPath(:1430-1434) applies exclusively toNavigable*/*Ref*classes. For two entities referencing each other with non-Ref@FKfields, the generated metamodels construct each other fromstatic final INSTANCEinitializers, so first metamodel use after a clean build dies at class initialization (ExceptionInInitializerError/StackOverflowError). The comment at:90claims the guard prevents exactly this. KSP has the same shape at.kt:1596, doubled by theNullablemetamodel variants.Note the engine already rejects a self-referencing non-Ref FK at template level (
TemplatePreparation.java:955-959), which is the right model: a cycle needs aRefboundary to be loadable at all. So the processors should reject non-Ref FK cycles at generation time with a diagnostic naming the cycle and the fix (mark one sideRef), rather than emitting code that compiles and then cannot initialize.Reproduce with a mutual pair, e.g.
record Owner(@PK Integer id, @FK Pet pet)/record Pet(@PK Integer id, @FK Owner owner).