Skip to content

Commit 11b779e

Browse files
os-elonclaude
andauthored
fix(spec): declare MetadataProtocol.getMetaItemLayered; drop the dead 'overlay' lockSource arm (#9740) (#9773)
* fix(spec): declare MetadataProtocol.getMetaItemLayered; drop the dead 'overlay' lockSource arm Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw * test(spec): pin MetadataProtocol.getMetaItemLayered's declaration and the closed lockSource vocabulary Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw * chore: changeset for #9740 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 985a9cd commit 11b779e

4 files changed

Lines changed: 62 additions & 12 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@objectstack/spec": minor
3+
"@objectstack/metadata-protocol": patch
4+
---
5+
6+
Declare `MetadataProtocol.getMetaItemLayered` — the layered three-way diagnostic read (`GET /api/v1/meta/:type/:name/layers`) now appears on the protocol interface, typed against the already-declared `GetMetaItemLayeredRequestSchema` / `GetMetaItemLayeredResponseSchema`, so callers no longer reach the verb through `any`. Declared optional like its `getMetaItemCached` / `deleteMetaItem` siblings: a declared-surface catch-up to a shipped verb, not a new capability.
7+
8+
In `@objectstack/metadata-protocol`, the implementation's inline return-type annotation for `getMetaItemLayered` drops its dead `'overlay'` arm on `lockSource` and annotates with `MetadataLockSource` directly — the only producer feeding that field on the layered read path is `resolveLockState`, whose return is already typed `MetadataLockSource | undefined` (`'artifact' | 'package' | 'env-forced'`); the `'overlay'` literal in the file belongs to `getEffectiveLock`, a write/delete-door helper that never feeds this response. Type-level change only; no runtime behaviour or wire vocabulary changes.

‎packages/metadata-protocol/src/protocol.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import {
7373
evaluateLockForDelete,
7474
resolveLockState,
7575
type MetadataLock,
76+
type MetadataLockSource,
7677
type MetadataProvenance,
7778
} from '@objectstack/spec/kernel';
7879
import { validateObjectNamespacePrefix, deriveNamespaceFromPackageId } from '@objectstack/spec/kernel';
@@ -6081,7 +6082,14 @@ export class ObjectStackProtocolImplementation implements
60816082
// ── ADR-0010 protection envelope ──
60826083
lock: MetadataLock;
60836084
lockReason?: string;
6084-
lockSource?: 'artifact' | 'package' | 'env-forced' | 'overlay';
6085+
// `MetadataLockSource` (artifact | package | env-forced) — the only
6086+
// producer feeding this field on this path is `resolveLockState`,
6087+
// whose return is typed `MetadataLockSource | undefined`. The
6088+
// `'overlay'` arm this annotation used to carry was dead: the one
6089+
// `lockSource: 'overlay'` producer in this file belongs to
6090+
// `getEffectiveLock`, a write/delete-door helper that never feeds
6091+
// this response (#9740).
6092+
lockSource?: MetadataLockSource;
60856093
lockDocsUrl?: string;
60866094
provenance?: MetadataProvenance;
60876095
packageId?: string;

‎packages/spec/src/api/protocol.test.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ import {
5959
GetFieldLabelsResponseSchema,
6060
} from './protocol.zod';
6161
import type { ListNotificationsRequest } from './protocol.zod';
62+
import { expectTypeOf } from 'vitest';
63+
import type {
64+
MetadataProtocol,
65+
GetMetaItemLayeredRequest,
66+
GetMetaItemLayeredResponse,
67+
} from './protocol.zod';
6268

6369
describe('ObjectStack Protocol', () => {
6470

@@ -1350,3 +1356,31 @@ describe('meta-read request schemas declare organizationId (#9726 — declared =
13501356
expect(GetMetaItemLayeredRequestSchema.safeParse({ name: 'account_list' }).success).toBe(false);
13511357
});
13521358
});
1359+
1360+
describe('MetadataProtocol declares getMetaItemLayered (#9740)', () => {
1361+
// Type-level pins (compiled by the spec test typecheck, the
1362+
// translation-typegen.test.ts pattern). The member is an interface
1363+
// declaration with no runtime shadow, so its presence and shape are only
1364+
// observable to tsc — these pins are what turns red if the member is
1365+
// dropped again or drifts off the layered schemas.
1366+
1367+
it('declares the member optional, against the layered request/response schemas', () => {
1368+
// Optional like its `getMetaItemCached` / `deleteMetaItem` siblings:
1369+
// additive to a shipped contract, implementation predating declaration.
1370+
expectTypeOf<MetadataProtocol['getMetaItemLayered']>().toEqualTypeOf<
1371+
((request: GetMetaItemLayeredRequest) => Promise<GetMetaItemLayeredResponse>) | undefined
1372+
>();
1373+
});
1374+
1375+
it("keeps the layered lockSource vocabulary closed — no 'overlay' arm", () => {
1376+
// The drift this card closed: the implementation's inline annotation
1377+
// carried an 'overlay' arm no producer on the layered read path can emit
1378+
// (the only `lockSource: 'overlay'` producer is `getEffectiveLock`, a
1379+
// write/delete-door helper). The declared wire enum stays
1380+
// artifact | package | env-forced; widening it without a producer is the
1381+
// declared-but-unenforced direction Prime Directive #10 forbids.
1382+
expectTypeOf<GetMetaItemLayeredResponse['lockSource']>().toEqualTypeOf<
1383+
'artifact' | 'package' | 'env-forced' | undefined
1384+
>();
1385+
});
1386+
});

‎packages/spec/src/api/protocol.zod.ts‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,17 +2337,17 @@ export interface MetadataProtocol {
23372337
*/
23382338
deleteMetaItem?(request: DeleteMetaItemRequest): Promise<DeleteMetaItemResponse>;
23392339
getMetaItemCached?(request: GetMetaItemCachedRequest): Promise<GetMetaItemCachedResponse>;
2340-
// `getMetaItemLayered` is deliberately NOT declared here yet, although both
2341-
// its request and response schemas are (see them above). Declaring the
2342-
// member makes tsc check `metadata-protocol`'s implementation against
2343-
// `GetMetaItemLayeredResponse`, and that check currently fails on one
2344-
// member: the implementation's inline return type annotates `lockSource`
2345-
// with an `'overlay'` arm that its only producer (`resolveLockState`, whose
2346-
// return is typed `MetadataLockSource | undefined`) can never emit — an
2347-
// over-wide annotation, not enforced behaviour. Until that annotation is
2348-
// corrected at the implementation, adding the member here would either lie
2349-
// about the wire enum or hard-break the implementing class. Tracked as its
2350-
// own card (filed from #9726).
2340+
/**
2341+
* Three-layer diagnostic read (`GET /api/v1/meta/:type/:name/layers`) —
2342+
* packaged baseline, tenant overlay row and merged result side by side; see
2343+
* {@link GetMetaItemLayeredResponseSchema} for the shape and its #5882
2344+
* route-separation rationale. A declared-surface catch-up, not a new
2345+
* capability: `metadata-protocol` has shipped this verb since the layered
2346+
* route landed. Declared optional like its `getMetaItemCached` /
2347+
* `deleteMetaItem` siblings — additive to a shipped contract, with the
2348+
* implementation predating the declaration.
2349+
*/
2350+
getMetaItemLayered?(request: GetMetaItemLayeredRequest): Promise<GetMetaItemLayeredResponse>;
23512351
getUiView?(request: GetUiViewRequest): Promise<GetUiViewResponse>;
23522352
}
23532353

0 commit comments

Comments
 (0)