@@ -63,17 +63,32 @@ import { HttpDispatcher } from './http-dispatcher.js';
6363const PACKAGE_ID = 'com.example.instances' ;
6464
6565/**
66- * A kernel plugin that does at boot what every real connector plugin does:
67- * resolve the engine and keep it. That reference IS the cycle — the engine
68- * refers back to itself (`_ObjectQL.actionActivation.store.engine`, the
69- * constructor chain the production envelope named).
66+ * A kernel plugin doing at boot what every real connector plugin does: resolve
67+ * the engine, keep it, and materialize the instances it owns — each of which
68+ * holds its owner back.
69+ *
70+ * ⚠️ Where the cycle comes from, measured rather than assumed. In production
71+ * the circle the 500 named runs through the ENGINE
72+ * (`_ObjectQL.actionActivation.store.engine`), but that store only attaches on
73+ * a composition where `sys_action_activation` is registered and probeable
74+ * (`ObjectQLPlugin`), which a bare engine kernel is not: measured here, a
75+ * booted engine on this composition serializes clean, so a fixture that only
76+ * held the engine would make every pin below VACUOUS. So the fixture closes
77+ * the circle the other ordinary way a plugin does — an owned instance holding
78+ * its owner — and the first assertion of the first test is the guard that the
79+ * fixture really is cyclic. What is under test either way is the same and does
80+ * not depend on which edge closes it: a booted plugin instance carries an
81+ * arbitrary runtime object graph, and a package record must not carry one.
7082 */
7183class EngineHoldingPlugin implements Plugin {
7284 name = 'com.example.engine-holding' ;
7385 /** Set at init — the reference the record must not end up carrying. */
7486 engine ?: unknown ;
87+ /** Materialized at boot, each holding the plugin back. */
88+ readonly connectors : Array < { owner : EngineHoldingPlugin } > = [ ] ;
7589 init ( ctx : PluginContext ) : void {
7690 this . engine = ctx . getService ( 'objectql' ) ;
91+ this . connectors . push ( { owner : this } ) ;
7792 }
7893}
7994
@@ -147,6 +162,28 @@ const dispatcherOver = (ql: QlService): HttpDispatcher =>
147162 context : { getService : ( name : string ) => ( name === 'objectql' ? ql : null ) } ,
148163 } as never ) ;
149164
165+ /**
166+ * A read-only data-plane stub, registered AFTER boot so `getMetaItems` can make
167+ * its `sys_metadata` overlay read.
168+ *
169+ * The SchemaRegistry — the thing under test, and the thing that stores the
170+ * record — stays entirely real; this answers only the DB half `getMetaItems`
171+ * consults after the registry, which a driver-less kernel cannot answer at all
172+ * (it throws "The metadata store could not be read"). Hand-written rather than
173+ * `vi.fn`: nothing here is asserted on, and no code-authored package has an
174+ * overlay row anyway, so an empty answer IS the production answer for this
175+ * type.
176+ */
177+ const emptyOverlayDriver = {
178+ name : 'pin_empty_overlay' ,
179+ supports : { } ,
180+ async connect ( ) { /* nothing to open */ } ,
181+ async disconnect ( ) { /* nothing to close */ } ,
182+ async find ( ) { return [ ] ; } ,
183+ async findOne ( ) { return null ; } ,
184+ async count ( ) { return 0 ; } ,
185+ } ;
186+
150187/** Engine self-invocation — passes the domain's anonymous floor and read gate. */
151188const systemCaller = ( ) => ( { request : { } , environmentId : 'platform' , executionContext : { isSystem : true } } ) as never ;
152189
@@ -181,6 +218,8 @@ describe('#14442 — the package record of an instance-bearing stack is serializ
181218 expect ( ( ) => JSON . stringify ( detail . response ) ) . not . toThrow ( ) ;
182219
183220 // Pin 2b — the REST `/meta/package` door's primitive.
221+ ( ql as unknown as { registerDriver ( d : unknown , isDefault ?: boolean ) : void } )
222+ . registerDriver ( emptyOverlayDriver , true ) ;
184223 const protocol = new ObjectStackProtocolImplementation ( ql as never ) ;
185224 const meta = await protocol . getMetaItems ( { type : 'package' } ) ;
186225 expect ( ( ) => JSON . stringify ( meta ) ) . not . toThrow ( ) ;
0 commit comments