Skip to content

Commit ff9da91

Browse files
os-zhuangclaude
andauthored
fix(metadata-protocol): draft package inheritance reads the active base across scopes (#11087 layer 1b) (#11171)
The console's session carries an active org, so its draft lands in the org overlay scope while the active base row is env-wide (ADR-0005 overlay order). The inheritance lookup went through whereFor, which pins organization_id to the repo's own scope — so for exactly the console-session saves this inheritance exists for, it found nothing and the draft stayed package_id NULL (measured live on staging after the layer-1 deploy: an org-scoped view draft over an env-wide active row bound to app.k9qk). Resolve the base with the same $or two-scope reach listDrafts applies; a null-org repo keeps the env-wide-only equality. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 0d4bd93 commit ff9da91

3 files changed

Lines changed: 48 additions & 3 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
Draft package inheritance (#11087) resolves the overlaid active row with the same two-scope reach as `listDrafts` (caller org + env-wide) — an org-scoped console save now inherits from the env-wide active base instead of finding nothing in its own scope.

packages/metadata-protocol/src/sys-metadata-repository.draft-package-inherit.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,28 @@ describe('SysMetadataRepository draft-save package inheritance (#11087)', () =>
131131
expect(draft.package_id ?? null).toBeNull();
132132
});
133133

134+
it('an ORG-scoped save inherits from the env-wide active row — the ADR-0005 overlay reach (#11087 layer 1b)', async () => {
135+
// The console's session carries an active org, so its draft lands in the
136+
// org scope while the active base row is env-wide. A same-org-only
137+
// inheritance lookup finds nothing there (measured live), so the reach
138+
// must mirror listDrafts' $or contract.
139+
const engine = makeFakeEngine([
140+
{
141+
type: REF.type, name: REF.name, organization_id: null, state: 'active',
142+
package_id: 'app.k9qk', metadata: '{"label":"Member"}', checksum: 'sha-active', version: 1,
143+
},
144+
]);
145+
const repo = new SysMetadataRepository({
146+
engine: engine as never,
147+
organizationId: 'org_1',
148+
orgLabel: 'org_1',
149+
} as never);
150+
await repo.put(REF, { label: 'Member v2' }, { parentVersion: null, actor: 't', state: 'draft' as const });
151+
const draft = engine.rows.find((r) => r.state === 'draft')!;
152+
expect(draft.organization_id).toBe('org_1'); // org-scoped save, unchanged
153+
expect(draft.package_id).toBe('app.k9qk'); // inherited across scopes
154+
});
155+
134156
it('adopts a pre-fix orphan draft (NULL package) instead of forking a second draft row', async () => {
135157
const engine = makeFakeEngine([
136158
{

packages/metadata-protocol/src/sys-metadata-repository.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,27 @@ export class SysMetadataRepository implements MetadataRepository {
464464
// ADR-0048), and with no active row — a brand-new item drafted first —
465465
// there is nothing to inherit and the package-less semantics stand.
466466
if (state === 'draft' && opts.packageId == null) {
467-
const activeRow = await this.engine.findOne('sys_metadata', {
468-
where: this.whereFor(ref, 'active'),
469-
});
467+
// The overlaid base is resolved with the SAME two-scope reach the draft
468+
// list applies (`listDrafts`' $or contract): an org-scoped caller's
469+
// active row usually lives ENV-WIDE (`organization_id IS NULL`) — the
470+
// ADR-0005 overlay order — so a same-org-only lookup here found nothing
471+
// for exactly the console-session saves this inheritance exists for
472+
// (measured live: an org-scoped view draft stayed `package_id NULL`
473+
// over an env-wide active row bound to `app.k9qk`).
474+
const activeWhere: Record<string, unknown> = {
475+
type: ref.type,
476+
name: ref.name,
477+
state: 'active',
478+
};
479+
if (this.organizationId != null) {
480+
activeWhere.$or = [
481+
{ organization_id: this.organizationId },
482+
{ organization_id: null },
483+
];
484+
} else {
485+
activeWhere.organization_id = null;
486+
}
487+
const activeRow = await this.engine.findOne('sys_metadata', { where: activeWhere });
470488
const activePkg = (activeRow as { package_id?: string | null } | null)?.package_id ?? null;
471489
if (activePkg) targetPackageId = activePkg;
472490
}

0 commit comments

Comments
 (0)