Skip to content

Commit 862ab8e

Browse files
Claudehotlong
andauthored
fix: bridge ALL SchemaRegistry objects to metadata service, not just DB-loaded ones
Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/9416386f-16ce-4a3e-83b8-b2c80c7cc6d4 Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 511972c commit 862ab8e

File tree

1 file changed

+55
-34
lines changed

1 file changed

+55
-34
lines changed

packages/objectql/src/plugin.ts

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,16 @@ export class ObjectQLPlugin implements Plugin {
129129
// object registered by plugins (e.g., sys_user from plugin-auth).
130130
await this.syncRegisteredSchemas(ctx);
131131

132+
// Bridge all SchemaRegistry objects to metadata service
133+
// This ensures AI tools and other IMetadataService consumers can see all objects
134+
await this.bridgeObjectsToMetadataService(ctx);
135+
132136
// Register built-in audit hooks
133137
this.registerAuditHooks(ctx);
134138

135139
// Register tenant isolation middleware
136140
this.registerTenantMiddleware(ctx);
137-
141+
138142
ctx.logger.info('ObjectQL engine started', {
139143
driversRegistered: this.ql?.['drivers']?.size || 0,
140144
objectsRegistered: this.ql?.registry?.getAllObjects?.()?.length || 0
@@ -365,9 +369,6 @@ export class ObjectQLPlugin implements Plugin {
365369
* This closes the persistence loop so that user-created schemas survive
366370
* kernel cold starts and redeployments.
367371
*
368-
* Also registers loaded objects with the metadata service so they are
369-
* visible to tools like AI chat that query the metadata service.
370-
*
371372
* Gracefully degrades when:
372373
* - The protocol service is unavailable (e.g., in-memory-only mode).
373374
* - `loadMetaFromDb` is not implemented by the protocol shim.
@@ -398,47 +399,67 @@ export class ObjectQLPlugin implements Plugin {
398399
ctx.logger.info('Metadata restored from database to SchemaRegistry', { loaded, errors });
399400
} else {
400401
ctx.logger.debug('No persisted metadata found in database');
402+
}
403+
} catch (e: unknown) {
404+
// Non-fatal: first-run or in-memory driver may not have sys_metadata yet
405+
ctx.logger.debug('DB metadata restore failed (non-fatal)', {
406+
error: e instanceof Error ? e.message : String(e),
407+
});
408+
}
409+
}
410+
411+
/**
412+
* Bridge all SchemaRegistry objects to the metadata service.
413+
*
414+
* This ensures objects registered by plugins and loaded from sys_metadata
415+
* are visible to AI tools and other consumers that query IMetadataService.
416+
*
417+
* Runs after both restoreMetadataFromDb() and syncRegisteredSchemas() to
418+
* catch all objects in the SchemaRegistry regardless of their source.
419+
*/
420+
private async bridgeObjectsToMetadataService(ctx: PluginContext): Promise<void> {
421+
try {
422+
const metadataService = ctx.getService<any>('metadata');
423+
if (!metadataService || typeof metadataService.register !== 'function') {
424+
ctx.logger.debug('Metadata service unavailable for bridging, skipping');
425+
return;
426+
}
427+
428+
if (!this.ql?.registry) {
429+
ctx.logger.debug('SchemaRegistry unavailable for bridging, skipping');
401430
return;
402431
}
403432

404-
// Phase 3: Bridge SchemaRegistry objects to metadata service
405-
// This ensures objects loaded from sys_metadata are visible to AI tools and other
406-
// consumers that query via IMetadataService.listObjects()
407-
if (loaded > 0) {
433+
const objects = this.ql.registry.getAllObjects();
434+
let bridged = 0;
435+
436+
for (const obj of objects) {
408437
try {
409-
const metadataService = ctx.getService<any>('metadata');
410-
if (metadataService && typeof metadataService.register === 'function' && this.ql?.registry) {
411-
const objects = this.ql.registry.getAllObjects();
412-
let bridged = 0;
413-
for (const obj of objects) {
414-
try {
415-
// Check if object is already in metadata service to avoid duplicates
416-
const existing = await metadataService.getObject(obj.name);
417-
if (!existing) {
418-
// Register object that exists in SchemaRegistry but not in metadata service
419-
await metadataService.register('object', obj.name, obj);
420-
bridged++;
421-
}
422-
} catch (e: unknown) {
423-
ctx.logger.debug('Failed to bridge object to metadata service', {
424-
object: obj.name,
425-
error: e instanceof Error ? e.message : String(e),
426-
});
427-
}
428-
}
429-
if (bridged > 0) {
430-
ctx.logger.info('Bridged objects from SchemaRegistry to metadata service', { count: bridged });
431-
}
438+
// Check if object is already in metadata service to avoid duplicates
439+
const existing = await metadataService.getObject(obj.name);
440+
if (!existing) {
441+
// Register object that exists in SchemaRegistry but not in metadata service
442+
await metadataService.register('object', obj.name, obj);
443+
bridged++;
432444
}
433445
} catch (e: unknown) {
434-
ctx.logger.debug('Metadata service unavailable for bridging, skipping', {
446+
ctx.logger.debug('Failed to bridge object to metadata service', {
447+
object: obj.name,
435448
error: e instanceof Error ? e.message : String(e),
436449
});
437450
}
438451
}
452+
453+
if (bridged > 0) {
454+
ctx.logger.info('Bridged objects from SchemaRegistry to metadata service', {
455+
count: bridged,
456+
total: objects.length
457+
});
458+
} else {
459+
ctx.logger.debug('No objects needed bridging (all already in metadata service)');
460+
}
439461
} catch (e: unknown) {
440-
// Non-fatal: first-run or in-memory driver may not have sys_metadata yet
441-
ctx.logger.debug('DB metadata restore failed (non-fatal)', {
462+
ctx.logger.debug('Failed to bridge objects to metadata service', {
442463
error: e instanceof Error ? e.message : String(e),
443464
});
444465
}

0 commit comments

Comments
 (0)