Skip to content

Commit 9e0ba21

Browse files
Trumpclaude
andauthored
Retire the paper metadata-customization protocol with its full coupling set (#13186)
* wip: retire paper metadata-customization protocol (spec module, keys, contracts, metadata limb) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * wip: regenerate artifacts for the customization-protocol retirement Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * wip: fix contracts/api test typecheck debt; regen export-origins + strictness ledger Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * test(spec): re-baseline the two corpus counts the module deletion moves `scripts/file-description.test.ts` pins how many rendered descriptions the level-1 heading demotion touches (#12249). `kernel/metadata-customization.zod.ts` was one of those level-1 openers, so removing the module moves the count 38 -> 37 in both assertions. `src/type-alias-convention.pin.test.ts` pins the isomorphic-alias count in prose AND recomputes it from the source (ADR-0122), so the four vacated Iso408-411 rows move it 837 -> 833 in the section header and the test title the runtime companion checks the prose against. Both are re-baselines of a machine-checked count, not relaxations: the recomputation still runs and still fails if either number goes stale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * fix(spec,docs): strip internal issue ids from the three prescriptions, re-declare the kernel page count `check:doc-authoring` flagged the three `retiredKey()` prescriptions: a customer reading a parse refusal has no tracker, so an issue id there is a citation-shaped token resolving to nothing. Per that gate's rule the issue id NEXT TO an ADR id is the strippable half -- each prescription keeps `ADR-0049 enforce-or-remove` plus its ADR-0126/ADR-0005 FROM -> TO mapping, so none is left bare. Reference pages regenerated (the tombstone text projects into `content/docs/references/**`). `check:quick-reference-counts` flagged the [total] side: deleting the `kernel/metadata-customization` reference page moves what `content/docs/references/kernel/` publishes from 31 to 30. The curated table's own row count (17) is untouched -- no row ever pointed at that page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 * fix(spec): re-derive the llms.txt claims the module deletion falsified `check:llms-txt` reds on two counts, and the file is hand-kept with no generator on purpose: deleting `kernel/metadata-customization.zod.ts` takes `src/kernel/` from 32 to 31 and the domain-summed total from 208 to 207. Both rows re-read rather than digit-patched, per the gate's own instruction: - The heading's method prose ("counted as `*.zod.ts` under `src/<domain>/`") stays exactly true, and is in fact what explains 207 against the 208 files on disk -- `src/stack.zod.ts` sits outside any domain directory. Every other domain row was re-counted independently and already matched; only kernel was stale. - The kernel row's Key Schemas column (Plugin, Manifest, Events, Feature, Context, Package Registry) never named the removed module, so nothing beside that number became untrue. Third fix, which the gate does NOT catch: the section-6 contract table lists `IMetadataService` METHODS, and `overlay` was one of them. That entry was true at the base commit (4 members) and is false now (0) -- this retirement removed them. Left alone it would ship inside the npm tarball telling AI consumers to generate `metadataService.saveOverlay(...)`, which is exactly the failure the file's header warns about. Dropped from the row. ⛔ Deliberately NOT touched: the same row claims `delete`, which resolves to 0 members at the base commit too -- the interface spells it `unregister`. That is pre-existing and unrelated to this retirement, so it is reported rather than folded in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 518d5e5 commit 9e0ba21

61 files changed

Lines changed: 1164 additions & 1617 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
"@objectstack/spec": minor
3+
"@objectstack/metadata": minor
4+
---
5+
6+
feat(spec): retire the paper metadata-customization protocol with its full coupling set (#13135, re-charter of #12057; ADR-0049, ADR-0126)
7+
8+
<!-- adr-0087: registered metadata-customization-protocol-retired -->
9+
10+
**BREAKING** export removal + authorable-key retirement, landing after the
11+
v17.0.0 cut (the lockstep launch-window convention ships it as `minor`; the
12+
prescriptions are registered under protocol major 18 —
13+
`RETIRED_DEFS_BY_MAJOR[18]`, `RETIRED_KEYS_BY_MAJOR[18]` and the D3 semantic
14+
entry `metadata-customization-protocol-retired` — where `os migrate meta`
15+
users will look).
16+
17+
`kernel/metadata-customization.zod.ts` declared a three-layer platform/user
18+
patch-overlay protocol (field-level change tracking, customization policies, a
19+
3-way-merge story) that nothing reachable implemented: no route ever served
20+
the paper `…/overlay` / `…/effective` endpoints, the only implementation
21+
(`packages/metadata`'s manager limb) was called solely by its own unit tests,
22+
no merge engine ever existed, and no code read a `CustomizationPolicy`.
23+
ADR-0126 §6 wall 4 supersedes the protocol as a matter of record ("nothing may
24+
build against it"); the maintainer adopted retirement on #12057 (2026-08-29,
25+
「同意」), and #13135 charters the full coupling set the fork report measured.
26+
27+
FROM → TO:
28+
29+
- `MetadataOverlaySchema` / `FieldChangeSchema` / `CustomizationOriginSchema` /
30+
`MergeConflictSchema` / `MergeStrategyConfigSchema` / `MergeResultSchema` /
31+
`CustomizationPolicySchema` and their `…`/`…Parsed` types
32+
(`@objectstack/spec/kernel`) → *(removed — no replacement protocol)*. The
33+
customization that actually ships: ADR-0005's org-scoped overlay
34+
(`allowOrgOverride` on `DEFAULT_METADATA_TYPE_REGISTRY`, `sys_metadata` org
35+
rows, layered read `code`/`overlay`/`effective`) and ADR-0126's
36+
packaged-metadata model (clone + ledger disable).
37+
- `MetadataOverlayResponseSchema` / `MetadataOverlaySaveRequestSchema` /
38+
`MetadataEffectiveResponseSchema` (`@objectstack/spec/api` §5) →
39+
*(removed)* — contracts for endpoints no adapter ever served; the layered
40+
read's contracts (`getMetaItemLayered`) are the live API.
41+
- `IMetadataService.getOverlay` / `.saveOverlay` / `.removeOverlay` /
42+
`.getEffective` optional members (`@objectstack/spec/contracts`) →
43+
*(removed)*, together with `packages/metadata`'s in-memory limb and its
44+
`'overlay'` feature log entry.
45+
- `MetadataPluginConfig.customizationPolicies` / `.mergeStrategy` and
46+
`MetadataManagerConfig.persistence.overlayWritable` → *(removed — retiredKey
47+
tombstones)*: authoring one is now a `tsc` error and a parse error carrying
48+
the prescription. Delete the keys; nothing replaces them (`persistence.writable`
49+
remains the base write gate).
50+
51+
One-line fix: delete the keys and any code building against the removed
52+
exports — they configured and described nothing that ever ran; org-level
53+
customization keeps riding the ADR-0005 overlay unchanged.
54+
55+
The retirement kit: whole-module deletion + kernel barrel line; 10
56+
`RETIRED_DEFS_BY_MAJOR[18]` entries (7 kernel defs + 3 api §5 contracts); 3
57+
`RETIRED_KEYS_BY_MAJOR[18]` tombstone entries (no D2 conversion —
58+
plugin/manager configs are not stack collection members, the
59+
`kernel/MetadataPluginConfig:additionalTypes` precedent); D3 semantic entry
60+
`metadata-customization-protocol-retired`; retirement pin test
61+
(`kernel/metadata-customization-retirement.test.ts`); type-alias pin rows
62+
Iso408-411 vacated; api-surface / export-origins / json-schema manifest /
63+
authorable-surface / reference docs regenerated (the
64+
`kernel/metadata-customization` reference page disappears with the module).

‎content/docs/getting-started/quick-reference.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Presentation layer - views, forms, dashboards, and app branding.
6161
| **[Chart](/docs/references/ui/chart)** | `chart.zod.ts` | Chart, ChartType | Chart definitions |
6262
| **[Widget Contract](/docs/protocol/objectui/widget-contract)** ↗ | `widget.zod.ts` | FieldWidgetProps | Props a custom field widget receives — the contract is documented with ObjectUI, outside `references/ui/` |
6363

64-
## Kernel Protocol (17 of 31 schemas)
64+
## Kernel Protocol (17 of 30 schemas)
6565

6666
Plugin architecture, manifests, and kernel runtime.
6767

‎content/docs/kernel/contracts/metadata-service.mdx‎

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: IMetadataService Contract
3-
description: Reference for the Metadata Service contract — CRUD operations for object and field definitions, schema registry, overlay management, and import/export
3+
description: Reference for the Metadata Service contract — CRUD operations for object and field definitions, schema registry, and import/export
44
---
55

66
The Metadata Service manages all object and field definitions at runtime. It serves as the **schema registry** — plugins, the Kernel, and the API layer all query this service to discover what objects exist and what fields they contain.
@@ -61,12 +61,6 @@ export interface IMetadataService {
6161
bulkRegister?(items: Array<{ type: string; name: string; data: unknown }>, options?: { continueOnError?: boolean; validate?: boolean }): Promise<MetadataBulkResult>;
6262
bulkUnregister?(items: Array<{ type: string; name: string }>): Promise<MetadataBulkResult>;
6363

64-
// Overlay / customization (optional)
65-
getOverlay?(type: string, name: string, scope?: 'platform' | 'user'): Promise<MetadataOverlay | undefined>;
66-
saveOverlay?(overlay: MetadataOverlay): Promise<void>;
67-
removeOverlay?(type: string, name: string, scope?: 'platform' | 'user'): Promise<void>;
68-
getEffective?(type: string, name: string, context?: { userId?: string; tenantId?: string; positions?: string[]; permissions?: string[] }): Promise<unknown | undefined>;
69-
7064
// Watch / subscribe (optional)
7165
watch?(type: string, callback: MetadataWatchCallback): MetadataWatchHandle;
7266

@@ -206,35 +200,16 @@ const validation = await metadataService.validate('object', definition);
206200

207201
---
208202

209-
## Overlay Management
210-
211-
Overlays customize a metadata item without modifying the base (system) definition.
212-
A `MetadataOverlay` references the target by `baseType` + `baseName`, carries a JSON
213-
Merge Patch in `patch`, and resolves in the order **system ← platform ← user**.
214-
215-
```typescript
216-
// Save a platform-scope overlay
217-
await metadataService.saveOverlay({
218-
id: 'overlay-platform-1',
219-
baseType: 'object',
220-
baseName: 'task',
221-
scope: 'platform',
222-
patch: { label: 'Work Item' },
223-
});
203+
## Overlay Management — removed
224204

225-
// Read the merged (effective) definition with overlays applied
226-
const effective = await metadataService.getEffective('object', 'task', {
227-
userId: 'user-123',
228-
});
229-
```
230-
231-
| Property | Type | Description |
232-
|:---|:---|:---|
233-
| `baseType` | `string` | Metadata type being customized |
234-
| `baseName` | `string` | Metadata name being customized |
235-
| `scope` | `'platform' \| 'user'` | Customization scope (default `platform`) |
236-
| `owner` | `string` | Owner user ID, for `user`-scope overlays |
237-
| `patch` | `object` | JSON Merge Patch (changed fields only) |
205+
The optional `getOverlay` / `saveOverlay` / `removeOverlay` / `getEffective`
206+
members and their `MetadataOverlay` record were removed in #13135 (ADR-0049
207+
enforce-or-remove): they belonged to a paper customization protocol no route
208+
ever served, and ADR-0126 supersedes it on the record. Org-scoped
209+
customization is [ADR-0005's metadata overlay](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0005-metadata-customization-overlay.md)
210+
— opt-in per type via `allowOrgOverride`, written through the REST meta write
211+
doors, and read back through the layered read (`code` / `overlay` /
212+
`effective`).
238213

239214
---
240215

@@ -293,7 +268,6 @@ console.log(result.failed); // failed
293268
| Type | Description |
294269
|:---|:---|
295270
| `MetadataQuery` / `MetadataQueryResult` | Query parameters and paginated result for `query()` |
296-
| `MetadataOverlay` | Runtime customization layer (`baseType`, `baseName`, `scope`, `patch`) |
297271
| `MetadataExportOptions` | `{ types?, namespaces?, format? }` for `exportMetadata` |
298272
| `MetadataImportOptions` | `{ conflictResolution?, validate?, dryRun? }` for `importMetadata` |
299273
| `MetadataImportResult` | `{ total, imported, skipped, failed, errors? }` |
@@ -321,36 +295,14 @@ const views = await metadataService.listViews('account');
321295
const dashboard = await metadataService.get('dashboard', 'sales_overview');
322296
```
323297

324-
### User-Level Customization
298+
### Org-Level Customization
325299

326-
Users can customize views via the overlay system:
327-
328-
```typescript
329-
// Admin customizes a view for all users
330-
await metadataService.saveOverlay({
331-
id: 'overlay-platform-1',
332-
baseType: 'view',
333-
baseName: 'account.default',
334-
scope: 'platform',
335-
patch: { columns: ['name', 'email', 'status', 'created_at'] },
336-
});
337-
338-
// A specific user saves personal column preferences
339-
await metadataService.saveOverlay({
340-
id: 'overlay-user-123',
341-
baseType: 'view',
342-
baseName: 'account.default',
343-
scope: 'user',
344-
owner: 'user-123',
345-
patch: { columns: ['name', 'status'] }, // user only wants 2 columns
346-
});
347-
348-
// Resolve effective view for a specific user
349-
const effectiveView = await metadataService.getEffective('view', 'account.default', {
350-
userId: 'user-123',
351-
});
352-
// Result: base view ← platform overlay ← user-123 overlay
353-
```
300+
Per-org view customization rides ADR-0005's metadata overlay (opt-in per type
301+
via `allowOrgOverride`, `view` among the overlay types): an org-scoped write
302+
through the REST meta doors stores a `sys_metadata` row, and the layered read
303+
returns `code` / `overlay` / `effective` for it. The per-user, per-field patch
304+
overlay a previous revision of this page taught here was removed in #13135 —
305+
it was never served by any route.
354306

355307
### Permission-Based UI Filtering
356308

‎content/docs/protocol/kernel/metadata-service.mdx‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,21 +227,25 @@ instance goes through `@objectstack/cloud-connection` (`os package install`).
227227
A still-configured `artifact-api` source fails loudly at `start()` rather
228228
than silently falling back to the filesystem scan.
229229

230-
### 2. Persistence Write Gates
230+
### 2. Persistence Write Gate
231231

232-
`MetadataManagerConfigSchema.persistence` is a two-axis runtime freeze. Both flags default to `true`.
232+
`MetadataManagerConfigSchema.persistence` is a runtime freeze. The flag defaults to `true`.
233233

234234
| Flag | Effect when `false` |
235235
| :--- | :--- |
236236
| `persistence.writable` | `register()` becomes a no-op (or throws when `validation.throwOnError`). |
237-
| `persistence.overlayWritable` | `saveOverlay()` is rejected. Disables Studio overlays in sealed deployments. |
238237

239238
```typescript
240239
new MetadataManager({
241-
persistence: { writable: false, overlayWritable: false },
240+
persistence: { writable: false },
242241
});
243242
```
244243

244+
(`persistence.overlayWritable` was removed in #13135 with the paper
245+
metadata-customization protocol — the `saveOverlay()` it gated was never
246+
reachable from any served surface. Authoring it is now a compile-time and
247+
parse-time error carrying the prescription.)
248+
245249
### 3. DatabaseLoader Read-Through Cache
246250

247251
`DatabaseLoader` wraps `load` / `loadMany` / `list` / `stat` results in a generic LRU cache (lazy TTL, promote-on-get, write invalidation). Reads always observe writes performed through the same loader instance; out-of-band SQL writes are honored within `ttl` milliseconds.

0 commit comments

Comments
 (0)