Skip to content

Commit ba0084e

Browse files
feat(spec): one override declaration for code and Studio, with the .custom pairing helpers and the closed overridable-type list (ADR-0146) (#28467)
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V Fixes #28404 Clause-②: yes (widening) Metadata-protocol: added ## 维护者速读 - **改了什么**:spec 里新增「覆盖声明」`overrides: { package, basedOn }`,加在 13 种可覆盖的元数据上;另加 `.custom` 配对包的命名、命名空间、依赖范围规则,可覆盖类型的封闭清单,`__c` 扩展字段后缀和基础字段可改的显示属性白名单。 - **为什么改**:开发者在仓库里写 `hotcrm.custom`,管理员在 Studio 里定制,两边要写出同一份声明,后面的写入关、读取、导出和 `os validate` 才能读同一样东西,不会各自拼一套「这一项覆盖了 HotCRM 的哪一项」。 - **风险与回滚**:只是新增可选字段、常量和纯函数,没有任何入口开始按它放行或拒绝;现有元数据不受影响。回滚就是 revert 本 PR。 - **先看**:① `packages/spec/src/kernel/customization-package.ts` 的 `OVERRIDE_TYPE_CLASSIFICATION`(哪些类型能覆盖,有 4 个待您定);② `customizationNamespace`(基础命名空间超过 13 个字符就没有配对命名空间);③ `packages/spec/src/data/field-customization.ts` 的白名单。 - **两分钟验证**:`pnpm --filter @objectstack/spec exec vitest run src/kernel/override-declaration.test.ts src/kernel/customization-package.test.ts src/data/field-customization.test.ts`,预期三个文件全绿。 - **需要你做的**:无。下面 "For the maintainer" 的 7 条您已同意(2026-10-31 20:44Z「同意」);契约复审 PASS、CI 全绿后由席位落地。 ## For the maintainer ADR-0146 does not decide these. **Ruled 2026-10-31 20:44Z: the maintainer agreed to all seven as built (「同意」)**, together with the follow-ups recorded after the list: 1. **Namespace past 13 characters.** `customizationNamespace(baseNs)` answers `<base_ns>_custom`; a base namespace longer than 13 characters gets no pair namespace (`{ ok: false, reason: 'base_namespace_too_long' }`) and the ensure door will refuse that base with a sentence. No truncation, no hashing. Nothing in the repository is affected today (the longest base namespace is `feature_flags`, 13). 2. **`seed`** — runtime-creatable and shipped by packages, so D2's first sentence admits it, but ADR-0126 §3 puts it outside ("one-shot"). Classified `excluded`, basis `adr_0126_one_shot` (ruled). 3. **`datasource`** — the same reading (ADR-0126 §3: "origin-gated"); D2 names it on neither side. Classified `excluded`, basis `setup_owned_connection` (ADR-0146 D1 gives datasources to Setup; ADR-0126 §3 origin-gated; ruled). The card's census listed four undecided types and missed this one: its registry entry is the only multi-line entry, which a line-wise reading cannot see (the registry's own comment warns about this). 4. **`tool`** and **`skill`** — behavioural (ADR-0126 §3 pre-charts them for Regime C), with no ledger switch and no D9-style conditions written for them. Classified `excluded`, basis `behavioural_switch_first`: `action`'s path, the ledger switch first and an override door later (ruled). 5. **`external_catalog`** — decided by D2's first sentence (packages never ship one), classified `excluded`, basis `not_package_shipped`. Listed so you can confirm the reading. 6. **Prerelease base versions.** `customizationDependencyRange('2.0.0-beta.1')` answers `^2` (the major of the numeric core, the ADR's words read literally). The ADR-0141 D2 install check cannot read a prerelease installed version, so a pair whose base sits at a prerelease is re-installable once the base reaches a release of that major. 7. **`basedOn` is required inside the block.** The ADR's shape names both keys; the PR requires both to be non-empty strings when `overrides` is present. Follow-ups the ruling also agreed: - No row of `OVERRIDE_TYPE_CLASSIFICATION` stays `undecided`; the basis value is removed and a test pins that no row carries it (commit `5d79f48d2f`). - `basedOn` records the **base package's `manifest.version`** the override was taken from, not a history version of the item; its `.describe()` says so. The ADR-0146 D2 wording is aligned in the #28390 text amendment. - A separate low-priority card: `os lint` / `os validate` warn when a manifest namespace is longer than 13 characters (that package cannot be customized). ## What changes - **`overrides: { package, basedOn }`** (`packages/spec/src/kernel/override-declaration.zod.ts`, `OverrideDeclarationFields`): one closed field set, spread on the 13 types `OVERRIDE_TYPE_CLASSIFICATION` classifies `overridable`: `view` (the ViewItem record and the flattened overlay; not the `defineView` container, see the acceptance notes), `dashboard`, `report`, `translation` (the item), `email_template`, `page`, `app`, `dataset`, `doc`, `book`, `mapping`, `flow`, `action`. It is a separate spread, not part of `MetadataProtectionFields`: that envelope is spread into excluded types and is what registration and clone strip as loader-stamped state. `package` refuses a `.custom` id. `basedOn`'s `.describe()` says it is recorded and never judged. - **Pairing helpers** (`packages/spec/src/kernel/customization-package.ts`): `CUSTOMIZATION_PACKAGE_SUFFIX`, `customizationPackageId`, `customizationBasePackageId`, `isCustomizationPackage`, `isCustomizationPairOf` (ruling A: only the paired `<base>.custom` declares), `customizationNamespace`, `customizationDependencyRange`. Each derivation answers `{ ok: true, value }` or `{ ok: false, reason }`. Dotted ids are covered. The namespace answer is judged against `ManifestSchema.shape.namespace` and the id against `MANIFEST_ID_PATTERN`, so the helpers restate neither grammar. - **The closed type list**: `OVERRIDE_TYPE_CLASSIFICATION` is total over `MetadataType` at compile time and pinned to `DEFAULT_METADATA_TYPE_REGISTRY` in both directions by a test. `object` is `extend_only`; `permission`, `position`, `capability` are excluded (D6); the code-only types are excluded; `flow` and `action` carry their D9 conditions as data. `OVERRIDABLE_METADATA_TYPES`, `getOverrideTypeClassification`, `isOverridableMetadataType` read it. - **`__c` and the display whitelist** (`packages/spec/src/data/field-customization.ts`): `EXTENSION_FIELD_SUFFIX`, `isExtensionFieldName`, `BASE_FIELD_DISPLAY_WHITELIST` = field `label`, `description`, `inlineHelpText`, `placeholder`, and option `label`, each measured against D10's test. Only the class D10 names; other display-only keys may pass the same test and enter by decision. - **The per-field patch slot on `ObjectExtensionSchema` rides N-23a.** A slot that the merge (`mergeObjectDefinitions`) does not apply would be an authorable key that nothing reads, so it lands together with that merge. - Liveness rows (`planned`) for `overrides` on the 12 ledgers that walk it, and the count shards; the `llms.txt` schema counts (kernel 31, total 213); regenerated spec artefacts. - The served `/meta/types` property counts in `packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts` move by one on the seven overridable types that suite pins (`action`, `app`, `dashboard`, `dataset`, `flow`, `page`, `report`); the excluded types keep theirs (seat commit `e55a4e7c6b`, after `Test Core (7/8)` went red on `e0253ce45c`). - Ledger rows in two existing pins the key reaches: a root `omit` row for `overrides` on the eight types with a hand-written metadata form (`metadata-form-zod-reconciliation.test.ts`; Studio writes the declaration on a customization, so no hand-filled control is offered while it is `planned`), and a `noReader` row for `flow.overrides` in the flows translation face. ## Tests - `packages/spec/src/kernel/override-declaration.test.ts`: the shape accepts and refuses (missing, empty, unknown key, `.custom` package); every overridable type keeps `overrides` through its metadata schema (the save door's parse) and refuses a malformed block; the excluded types with a create seed refuse the key by name; `defineStack()` (strict) keeps it on all 11 item collections; the export's `viewItems:` channel keeps it for a ViewItem and an overlay. - `packages/spec/src/kernel/customization-package.test.ts`: table tests for the id helpers (dotted ids, a pair of a pair, a dependent package that is not the pair), the namespace (13 and 14 characters, pinned against the grammar), the range, and the type list pinned to the registry. - `packages/spec/src/data/field-customization.test.ts`: every whitelist member exists on `FieldSchema` (or its option schema) and takes text; none is in the structural list, and every structural key named is a real `FieldSchema` key. - `packages/runtime/src/domains/packages-export-override-declaration.test.ts`: `GET /packages/<base>.custom/export` carries `overrides` on a flow and a view item and strips provenance; both re-parse with the block intact. - Full `@objectstack/spec` suite on `e0253ce45c`: 865 files passed, 24491 tests passed, 1 todo. An earlier run named three existing pins the key reaches (the form reconciliation, the flows translation face, the description renderer's level-1 count); they are recorded in their ledgers above and the module header opens at level 2. - Reverse check: with the spread removed from `flow.zod.ts`, the strict `defineStack()` in the first file refuses `overrides` on `flows.0` and the file goes red; restored from `HEAD`. ## Acceptance notes - **`view` via `defineStack()` and `translation` via the stack.** The stack authors views as `defineView` containers (`views:`) and translations as locale bundles (`translations:`); both are aggregates that are expanded or merged into items at load. The declaration is on the item shapes the `view` and `translation` metadata types parse, so it survives the metadata save and the export, but a repository author cannot write it through those two stack collections today. Whether a container-level declaration fans out to its expanded items is a loader decision for the read and Studio cards, not taken here. - **The export skips `emailTemplates`** (`assemblePackageManifest` excludes it with `datasources`), so an overriding email template in `.custom` does not travel through `GET /packages/:id/export` today. Pre-existing; the effective-read card carries it. - The `app` schema now carries the landing refinement from main (#28449); the override spread sits inside it, resolved in the merge. - No `view` liveness row: the `view` ledger governs the `defineView` container (`ViewSchema`), which does not carry the key. ## Verification Independent review: none run (quota); the PR waits for the seat's contract-review hand-off. **Gates** (head `e0253ce45c`): `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 112 commands. All 112 ran in one pass and exited 0, each exit code recorded before any pipe. `--ran` reconciliation: 112 derived, 112 run, 0 not measured, 0 unrun. The full farm is CI's. Also run: - `pnpm --filter @objectstack/spec check:generated`: all 14 artefacts current after regeneration on each merge. - `pnpm --filter @objectstack/spec typecheck`: exit 0. - `pnpm --filter @objectstack/runtime typecheck`: exit 0. - The runtime export test: 3 of 3 passed, on `0f9936f445`. The later merge brought only the app landing, components and docs. Earlier passes found two real fixes, both in this PR: the `llms.txt` schema counts, and the three ledger pins. Gates that refused with exit 3 because a prerequisite build was missing were re-run after the build and are not counted as passes. --- _Generated by [Claude Code](https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2bba35a commit ba0084e

89 files changed

Lines changed: 1945 additions & 25 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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): one override declaration for code and Studio — `overrides: { package, basedOn }`, the `.custom` pairing helpers, the closed overridable-type list, the `__c` reservation and the base-field display whitelist (ADR-0146)
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
A customer's change to an installed package lives in its paired `<base>.custom` package (ADR-0146). This release declares the shared definitions every later door reads, so a developer's `hotcrm.custom` in a repository and an administrator's Studio customization write the same thing:
11+
12+
- **The override declaration.** `view`, `dashboard`, `report`, `translation`, `email_template`, `page`, `app`, `dataset`, `doc`, `book`, `mapping`, `flow` and `action` items accept an optional `overrides: { package, basedOn }`: this item replaces the base package's same-named item whole. `package` names the base (never a `.custom` id); `basedOn` is recorded and never judged. The key survives `defineStack()`, the metadata save and the package export. No door acts on it yet: the write gate and the effective read are separate changes.
13+
- **The pair's identity** (`@objectstack/spec/kernel`): `CUSTOMIZATION_PACKAGE_SUFFIX` (`.custom`), `customizationPackageId`, `customizationBasePackageId`, `isCustomizationPackage`, `isCustomizationPairOf`, `customizationNamespace` (`<base_ns>_custom`; a base namespace longer than 13 characters gets none, with the named reason `base_namespace_too_long`, never a truncation) and `customizationDependencyRange` (`^<major>` of the base version). Each derivation answers `{ ok: true, value }` or `{ ok: false, reason }`.
14+
- **The overridable-type list.** `OVERRIDE_TYPE_CLASSIFICATION` classifies every metadata type as `overridable`, `extend_only` (`object`) or `excluded` (`permission`, `position`, `capability`, the code-only types and others), with `flow`'s and `action`'s preconditions carried as data. `OVERRIDABLE_METADATA_TYPES` and `isOverridableMetadataType` read it. `seed`, `datasource`, `tool` and `skill` are `excluded` pending a ruling.
15+
- **Customer extension fields** (`@objectstack/spec/data`): `EXTENSION_FIELD_SUFFIX` (`__c`), `isExtensionFieldName`, and `BASE_FIELD_DISPLAY_WHITELIST`: the base-field properties an extension may change (`label`, `description`, `inlineHelpText`, `placeholder`, and an option's `label`). Enforcing both is a separate change.

‎content/docs/references/api/automation-api.mdx‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ const result = AutomationApiErrorCode.parse(data);
129129
| **_packageId** | `string` | optional | Owning package machine id. |
130130
| **_packageVersion** | `string` | optional | Owning package version. |
131131
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
132+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
132133

133134
### Nested Shape: `CreateFlowRequest.variables[number]`
134135

@@ -186,6 +187,13 @@ const result = AutomationApiErrorCode.parse(data);
186187
| **reason** | `string` | ✅ | User-visible reason shown when the lock blocks an action. |
187188
| **docsUrl** | `string` | optional | Optional URL the Studio banner links to for more context. |
188189

190+
### Nested Shape: `CreateFlowRequest.overrides`
191+
192+
| Property | Type | Required | Description |
193+
| :--- | :--- | :--- | :--- |
194+
| **package** | `string` | ✅ | The base package id whose same-named item this item replaces whole (e.g. "com.acme.crm"). Only an item of the base's paired "`<base>`.custom" package declares an override. |
195+
| **basedOn** | `string` | ✅ | Recorded, never judged: the base package's `manifest.version` this override was taken from (e.g. "3.2.0"), not a history version of the item. No door compares it with the installed base, no upgrade diff is produced from it, and nothing is refused on it; it tells a person re-basing the override where it started (ADR-0146 D2). |
196+
189197

190198
---
191199

@@ -249,6 +257,7 @@ const result = AutomationApiErrorCode.parse(data);
249257
| **_packageId** | `string` | optional | Owning package machine id. |
250258
| **_packageVersion** | `string` | optional | Owning package version. |
251259
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
260+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
252261

253262

254263
---
@@ -789,6 +798,7 @@ const result = AutomationApiErrorCode.parse(data);
789798
| **_packageId** | `string` | optional | Owning package machine id. |
790799
| **_packageVersion** | `string` | optional | Owning package version. |
791800
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
801+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
792802

793803

794804
---
@@ -1098,6 +1108,7 @@ const result = AutomationApiErrorCode.parse(data);
10981108
| **_packageId** | `string` | optional | Owning package machine id. |
10991109
| **_packageVersion** | `string` | optional | Owning package version. |
11001110
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
1111+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
11011112

11021113

11031114
---
@@ -1162,6 +1173,7 @@ const result = AutomationApiErrorCode.parse(data);
11621173
| **_packageId** | `string` | optional | Owning package machine id. |
11631174
| **_packageVersion** | `string` | optional | Owning package version. |
11641175
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
1176+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
11651177

11661178

11671179
---

‎content/docs/references/api/metadata.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ const result = AppDefinitionResponseSchema.parse(data);
106106
| **_packageId** | `string` | optional | Owning package machine id. |
107107
| **_packageVersion** | `string` | optional | Owning package version. |
108108
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
109+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
109110

110111

111112
---

‎content/docs/references/automation/flow.mdx‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const result = FlowSchema.parse(data);
5050
| **_packageId** | `string` | optional | Owning package machine id. |
5151
| **_packageVersion** | `string` | optional | Owning package version. |
5252
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
53+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
5354

5455
### Nested Shape: `Flow.variables[number]`
5556

@@ -107,6 +108,13 @@ const result = FlowSchema.parse(data);
107108
| **reason** | `string` | ✅ | User-visible reason shown when the lock blocks an action. |
108109
| **docsUrl** | `string` | optional | Optional URL the Studio banner links to for more context. |
109110

111+
### Nested Shape: `Flow.overrides`
112+
113+
| Property | Type | Required | Description |
114+
| :--- | :--- | :--- | :--- |
115+
| **package** | `string` | ✅ | The base package id whose same-named item this item replaces whole (e.g. "com.acme.crm"). Only an item of the base's paired "`<base>`.custom" package declares an override. |
116+
| **basedOn** | `string` | ✅ | Recorded, never judged: the base package's `manifest.version` this override was taken from (e.g. "3.2.0"), not a history version of the item. No door compares it with the installed base, no upgrade diff is produced from it, and nothing is refused on it; it tells a person re-basing the override where it started (ADR-0146 D2). |
117+
110118

111119
---
112120

@@ -264,6 +272,7 @@ const result = FlowSchema.parse(data);
264272
| **_packageId** | `string` | optional | Owning package machine id. |
265273
| **_packageVersion** | `string` | optional | Owning package version. |
266274
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
275+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
267276

268277

269278
---

‎content/docs/references/data/mapping.mdx‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const result = ImportFieldMappingSchema.parse(data);
5757
| **_packageId** | `string` | optional | Owning package machine id. |
5858
| **_packageVersion** | `string` | optional | Owning package version. |
5959
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
60+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
6061

6162
### Nested Shape: `Mapping.fieldMapping[number]`
6263

@@ -77,6 +78,13 @@ const result = ImportFieldMappingSchema.parse(data);
7778
| **recordsPath** | `string` | optional | Dot path, within the action's result, to the array of records (e.g. `body.results`); omitted ⇒ the result's `body` is the array |
7879
| **watermark** | `{ field: string; param: string }` | optional | Timestamp-incremental pull; omitted ⇒ every pull reads the full set. Either way a pull makes ONE action call and reads ONE response: the connector's paging is not followed, so a paged endpoint yields its first page only — and an incremental pull over a newest-first paged endpoint moves its starting point past the pages it never read |
7980

81+
### Nested Shape: `Mapping.overrides`
82+
83+
| Property | Type | Required | Description |
84+
| :--- | :--- | :--- | :--- |
85+
| **package** | `string` | ✅ | The base package id whose same-named item this item replaces whole (e.g. "com.acme.crm"). Only an item of the base's paired "`<base>`.custom" package declares an override. |
86+
| **basedOn** | `string` | ✅ | Recorded, never judged: the base package's `manifest.version` this override was taken from (e.g. "3.2.0"), not a history version of the item. No door compares it with the installed base, no upgrade diff is produced from it, and nothing is refused on it; it tells a person re-basing the override where it started (ADR-0146 D2). |
87+
8088

8189
---
8290

‎content/docs/references/data/object.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ const result = ApiMethod.parse(data);
525525
| **_packageId** | `string` | optional | Owning package machine id. |
526526
| **_packageVersion** | `string` | optional | Owning package version. |
527527
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
528+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
528529

529530
### Nested Shape: `Object.protection`
530531

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Protocol reference — every schema by module
33
navTitle: Protocol Reference
4-
description: Every schema published by @objectstack/spec — 1577 schemas across 14 protocol modules
4+
description: Every schema published by @objectstack/spec — 1578 schemas across 14 protocol modules
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -26,15 +26,15 @@ counts are sums of the rows they head. Regenerate with
2626
| [Data Protocol](/docs/references/data) | 31 | 174 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
2828
| [Integration Protocol](/docs/references/integration) | 1 | 23 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
29-
| [Kernel Protocol](/docs/references/kernel) | 29 | 151 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
29+
| [Kernel Protocol](/docs/references/kernel) | 30 | 152 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
3030
| [Marketplace Protocol](/docs/references/marketplace) | 4 | 30 | The package & marketplace format — package identity and versions, listing, publish, review, search, install, template manifests. |
3131
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
3232
| [Security Protocol](/docs/references/security) | 6 | 37 | Permission sets, row-level security, sharing rules, tenancy posture. |
3333
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3434
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3535
| [System Protocol](/docs/references/system) | 37 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3636
| [UI Protocol](/docs/references/ui) | 16 | 175 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
37-
| **Total** | **204** | **1577** | 14 protocol modules |
37+
| **Total** | **205** | **1578** | 14 protocol modules |
3838

3939
---
4040

@@ -204,7 +204,7 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo
204204

205205
## Kernel Protocol
206206

207-
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **29 pages, 151 schemas**
207+
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **30 pages, 152 schemas**
208208

209209
Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry.
210210

@@ -225,6 +225,7 @@ Plugin lifecycle and manifests, capabilities and security, metadata loading, ser
225225
| [`metadata-loader.zod.ts`](/docs/references/kernel/metadata-loader) | `MetadataFallbackStrategy`, `MetadataManagerConfig` |
226226
| [`metadata-plugin.zod.ts`](/docs/references/kernel/metadata-plugin) | `MetadataBulkResult`, `MetadataDependency`, `MetadataPluginConfig`, `MetadataPluginManifest`, `MetadataQuery`, `MetadataQueryResult`, `MetadataType`, `MetadataTypeRegistryEntry`, `MetadataValidationResult` |
227227
| [`metadata-protection.zod.ts`](/docs/references/kernel/metadata-protection) | `MetadataLock`, `MetadataLockSource`, `MetadataProvenance` |
228+
| [`override-declaration.zod.ts`](/docs/references/kernel/override-declaration) | `OverrideDeclaration` |
228229
| [`package-artifact.zod.ts`](/docs/references/kernel/package-artifact) | `ArtifactChecksum`, `ArtifactFileEntry`, `ArtifactSignature`, `MetadataCategoryEnum`, `PackageArtifact` |
229230
| [`package-registry.zod.ts`](/docs/references/kernel/package-registry) | `DisablePackageRequest`, `DisablePackageResponse`, `EnablePackageRequest`, `EnablePackageResponse`, `GetPackageRequest`, `GetPackageResponse`, `InstallPackageRequest`, `InstallPackageResponse`, `InstalledPackage`, `InstalledPackageSource`, `ListPackagesRequest`, `ListPackagesResponse`, `NamespaceConflictError`, `NamespaceRegistryEntry`, `PackageStatusEnum`, `UninstallPackageRequest`, `UninstallPackageResponse` |
230231
| [`plugin.zod.ts`](/docs/references/kernel/plugin) | `Plugin` |

‎content/docs/references/kernel/index.mdx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Kernel Protocol — complete schema reference
33
navTitle: Kernel Protocol
4-
description: "The ObjectStack Kernel Protocol in 29 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
4+
description: "The ObjectStack Kernel Protocol in 30 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -24,6 +24,7 @@ This section contains all protocol schemas for the kernel layer of ObjectStack.
2424
<Card href="/docs/references/kernel/metadata-loader" title="Metadata Loader" description="Source: packages/spec/src/kernel/metadata-loader.zod.ts" />
2525
<Card href="/docs/references/kernel/metadata-plugin" title="Metadata Plugin" description="Source: packages/spec/src/kernel/metadata-plugin.zod.ts" />
2626
<Card href="/docs/references/kernel/metadata-protection" title="Metadata Protection" description="Source: packages/spec/src/kernel/metadata-protection.zod.ts" />
27+
<Card href="/docs/references/kernel/override-declaration" title="Override Declaration" description="Source: packages/spec/src/kernel/override-declaration.zod.ts" />
2728
<Card href="/docs/references/kernel/package-artifact" title="Package Artifact" description="Source: packages/spec/src/kernel/package-artifact.zod.ts" />
2829
<Card href="/docs/references/kernel/package-registry" title="Package Registry" description="Source: packages/spec/src/kernel/package-registry.zod.ts" />
2930
<Card href="/docs/references/kernel/plugin" title="Plugin" description="Source: packages/spec/src/kernel/plugin.zod.ts" />

‎content/docs/references/kernel/meta.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"events-dlq",
3535
"events-handlers",
3636
"events-integrations",
37-
"events-queue"
37+
"events-queue",
38+
"override-declaration"
3839
]
3940
}

‎content/docs/references/kernel/metadata-plugin.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ const result = MetadataBulkResultSchema.parse(data);
368368
| **_packageId** | `string` | optional | Owning package machine id. |
369369
| **_packageVersion** | `string` | optional | Owning package version. |
370370
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
371+
| **overrides** | `{ package: string; basedOn: string }` | optional | Override declaration (ADR-0146 D2): `{ package, basedOn }` — this item of "`<base>`.custom" replaces the base package's same-named item whole. `basedOn` is recorded and never judged. |
371372

372373

373374
---

0 commit comments

Comments
 (0)