Skip to content

Commit f700c48

Browse files
committed
2 parents a86a4de + 1678089 commit f700c48

3 files changed

Lines changed: 61 additions & 10 deletions

File tree

packages/metadata/src/metadata-manager.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ export class MetadataManager implements IMetadataService {
137137
* @param environmentId - Environment ID (undefined = platform-global)
138138
*/
139139
setDatabaseDriver(driver: IDataDriver, organizationId?: string, environmentId?: string): void {
140+
if (environmentId !== undefined) {
141+
this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {
142+
organizationId,
143+
environmentId,
144+
});
145+
return;
146+
}
140147
const tableName = this.config.tableName ?? 'sys_metadata';
141148
const dbLoader = new DatabaseLoader({
142149
driver,
@@ -159,6 +166,13 @@ export class MetadataManager implements IMetadataService {
159166
* @param environmentId - Environment ID (undefined = platform-global)
160167
*/
161168
setDataEngine(engine: IDataEngine, organizationId?: string, environmentId?: string): void {
169+
if (environmentId !== undefined) {
170+
this.logger.info('Project kernel — skipping DatabaseLoader for sys_metadata (control-plane only)', {
171+
organizationId,
172+
environmentId,
173+
});
174+
return;
175+
}
162176
const tableName = this.config.tableName ?? 'sys_metadata';
163177
const dbLoader = new DatabaseLoader({
164178
engine,

packages/objectql/src/plugin.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,15 @@ export class ObjectQLPlugin implements Plugin {
166166
await this.syncRegisteredSchemas(ctx);
167167

168168
// Phase 2: Hydrate SchemaRegistry from sys_metadata (loads custom/template objects).
169-
await this.restoreMetadataFromDb(ctx);
169+
// Project kernels (environmentId set) never persist sys_metadata locally —
170+
// metadata is sourced from the artifact (MetadataPlugin) or routed to the
171+
// control plane via ControlPlaneProxyDriver. Skip to avoid querying a table
172+
// that does not exist on local project DBs.
173+
if (this.environmentId === undefined) {
174+
await this.restoreMetadataFromDb(ctx);
175+
} else {
176+
ctx.logger.info('Project kernel — skipping sys_metadata hydration (metadata sourced from artifact)');
177+
}
170178

171179
// Phase 3: Sync any new schemas that were just hydrated from the DB
172180
// (e.g. CRM objects seeded via template — they must have tables before use).
@@ -176,12 +184,12 @@ export class ObjectQLPlugin implements Plugin {
176184
//
177185
// `SchemaRegistry` is a process-wide singleton, so project kernels in a
178186
// multi-project server would otherwise inherit every object ever
179-
// registered by any sibling project (`saveMetaItem` writes to it).
180-
// When this plugin was constructed with an `environmentId`, the kernel
181-
// is project-scoped — its own ObjectQL.start already hydrated the
182-
// project-owned schemas from sys_metadata (env_id-filtered), and the
183-
// bridge would only pollute its metadata service with cross-project
184-
// leakage. Skip it in that case.
187+
// registered by any sibling project. When this plugin was constructed
188+
// with an `environmentId`, the kernel is project-scoped — its
189+
// metadata comes from the artifact (MetadataPlugin) or the
190+
// control-plane proxy, not from local sys_metadata. The bridge would
191+
// only pollute its metadata service with cross-project leakage, so
192+
// skip it in that case.
185193
if (this.environmentId === undefined) {
186194
await this.bridgeObjectsToMetadataService(ctx);
187195
}

packages/objectql/src/protocol.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
263263
// entries (the previous fallback-only logic meant project metadata
264264
// was never surfaced whenever system-bridged items populated the
265265
// registry). Deduplicate against whatever the registry returned.
266-
try {
266+
// Skip on project kernels — sys_metadata is control-plane only.
267+
if (this.environmentId === undefined) try {
267268
const whereClause: Record<string, unknown> = {
268269
type: request.type,
269270
state: 'active',
@@ -367,8 +368,10 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
367368
}
368369
}
369370

370-
// Fallback to database if not in registry
371-
if (item === undefined) {
371+
// Fallback to database if not in registry.
372+
// Skip on project kernels — sys_metadata is control-plane only;
373+
// project kernels source metadata from the artifact via MetadataService below.
374+
if (item === undefined && this.environmentId === undefined) {
372375
try {
373376
const scopedWhere: Record<string, unknown> = {
374377
type: request.type,
@@ -1085,6 +1088,26 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
10851088
throw new Error('Item data is required');
10861089
}
10871090

1091+
// Project kernels (environmentId set) never persist to sys_metadata
1092+
// locally — runtime metadata is sourced from the artifact and writes
1093+
// belong to the control plane. Update the in-memory registry only.
1094+
if (this.environmentId !== undefined) {
1095+
this.engine.registry.registerItem(request.type, request.item, 'name');
1096+
if (request.type === 'object' || request.type === 'objects') {
1097+
try {
1098+
this.engine.registry.registerObject(request.item as any, 'sys_metadata');
1099+
} catch (err: any) {
1100+
console.warn(
1101+
`[Protocol] registerObject failed for ${request.name}: ${err?.message ?? err}`,
1102+
);
1103+
}
1104+
}
1105+
return {
1106+
success: true,
1107+
message: 'Saved to memory registry (project kernel — sys_metadata is control-plane only)',
1108+
};
1109+
}
1110+
10881111
// 1. Always update the in-memory registry (runtime cache).
10891112
// For `type === 'object'` we additionally register in the
10901113
// dedicated objects map so that downstream calls (e.g.
@@ -1176,6 +1199,12 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
11761199
* Safe to call repeatedly — idempotent (latest DB record wins).
11771200
*/
11781201
async loadMetaFromDb(): Promise<{ loaded: number; errors: number }> {
1202+
// Project kernels never read sys_metadata locally — the table only
1203+
// exists on the control plane. Metadata is sourced from the artifact
1204+
// (MetadataPlugin) or routed via ControlPlaneProxyDriver.
1205+
if (this.environmentId !== undefined) {
1206+
return { loaded: 0, errors: 0 };
1207+
}
11791208
let loaded = 0;
11801209
let errors = 0;
11811210
try {

0 commit comments

Comments
 (0)