From 7390bef4d67963533b28c1979cf4a92bafdcac8b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:02:49 +0000 Subject: [PATCH 1/2] docs(skills,docs): an email-template override names a locale its base ships; a new locale carries no overrides Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc --- .changeset/28778-email-template-override-locale.md | 11 +++++++++++ content/docs/concepts/metadata-lifecycle.mdx | 8 +++++++- skills/objectstack-platform/SKILL.md | 11 ++++------- 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 .changeset/28778-email-template-override-locale.md diff --git a/.changeset/28778-email-template-override-locale.md b/.changeset/28778-email-template-override-locale.md new file mode 100644 index 00000000000..992f1ac0b6c --- /dev/null +++ b/.changeset/28778-email-template-override-locale.md @@ -0,0 +1,11 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): objectstack-platform says an email-template override names a locale its base ships; a new locale carries no `overrides` + +Clause-②: no +Metadata-protocol: none + +- **Before.** The ADR-0146 customization paragraph taught that a same-named item in the paired `.custom` package with `overrides` replaces the base's item, and listed `email_template` among the honoured types, but said nothing about the locale. An email template is one item per `(name, locale)`, and the override write gate refuses a declared override of a locale the base does not ship (`declared_package_ships_no_such_item`), so an author following the skill could declare `overrides` on a locale the base never shipped and be refused. +- **Now.** One sentence in that paragraph: an email template is one item per (name, locale), an override names a locale the base ships, and a locale the base does not ship is added without `overrides`. The file stays at its token ceiling: the sentence is paid for by naming the not-yet-honoured types as the rest of `OVERRIDABLE_METADATA_TYPES` instead of enumerating them, and by dropping four filler intro clauses. No rule is removed, no frontmatter, schema or behaviour changes. diff --git a/content/docs/concepts/metadata-lifecycle.mdx b/content/docs/concepts/metadata-lifecycle.mdx index cc99db9e390..ab0f3949039 100644 --- a/content/docs/concepts/metadata-lifecycle.mdx +++ b/content/docs/concepts/metadata-lifecycle.mdx @@ -115,7 +115,8 @@ later ships. See [ADR-0070](https://github.com/objectstack-ai/objectstack/blob/m A customer's change to an installed base package — HotCRM, id `hotcrm` — is an item of the base's paired writable package **`hotcrm.custom`**, which depends on the base; the -base's own items stay read-only. An item of the pair that bears a base item's name and +base's own items stay read-only. An item of the pair that bears a base item's name — for an +`email_template`, one item per `(name, locale)`, the name **and** a locale the base ships — and declares in its body `overrides: { package: 'hotcrm', basedOn: '3.2.0' }` (the base's `manifest.id` and `manifest.version`; `basedOn` is recorded and never judged; schema: [Override Declaration](/docs/references/kernel/override-declaration)) replaces the base's @@ -131,6 +132,11 @@ document **whole** for every read that serves the base's name or `translation`: the rest of `OVERRIDABLE_METADATA_TYPES` (`@objectstack/spec/kernel`) — `page`, `app`, `dataset`, `doc`, `book`, `mapping`, `flow` and `action` — waits for its doors and is not served yet. A declaration from any other package is ignored. +- **A new locale is not an override.** An `email_template` the pair saves at a locale the + base does not ship is the pair's own member, served under `hotcrm.custom`, and carries no + `overrides`; declaring one there is refused (`403 NOT_OVERRIDABLE` at the save, publish + and install doors, a row from `os validate`, the sentence naming the locale), because the + base ships no such member to replace. - **The environment-wide list** serves one item per name that exactly one code package ships. A dependent package's same-named item is not listed beside it and stays under its own `?package=` address. A package-unbound row — the overlay row of the table diff --git a/skills/objectstack-platform/SKILL.md b/skills/objectstack-platform/SKILL.md index bc3d05b51b6..bc1ed3481de 100644 --- a/skills/objectstack-platform/SKILL.md +++ b/skills/objectstack-platform/SKILL.md @@ -55,8 +55,7 @@ ownerless row into a queue — parked, `round_robin` or `least_loaded` — or to user. Both need the ownership-queue service `@objectstack/plugin-sharing` registers (`sharing` in `requires`): a stack declaring either without it is refused at boot. -Undeclared keys are handled per surface, and the two postures are worth -keeping straight: +Undeclared keys are handled per surface, in two postures: - **Refused** — `defineStack()`'s top level, each `objects[]` entry (`ObjectSchema`), and each field (`FieldSchema`). The parse throws, naming @@ -108,7 +107,7 @@ OS_ARTIFACT_PATH=./dist/objectstack.json objectstack dev ## Manifest Reference -Every stack needs a `manifest` to identify itself in the ecosystem: +Every stack needs a `manifest`: ```typescript manifest: { @@ -131,7 +130,7 @@ to a new protocol major — never to silence a mismatch. **`manifest.namespace` (ADR-0048):** Optional, but **enforced once set**. When a package declares `manifest.namespace: 'crm'`, every `object.name` must start with `crm_` or `defineStack` errors (`validateNamespacePrefix` in `@objectstack/spec`); the legacy `__` double-underscore form is rejected, and `sys_`-prefixed names are platform-reserved and exempt. The namespace is also a package-ownership key — installing two packages that both claim `crm` fails with `NamespaceConflictError` (downgrade to a warning with `OS_METADATA_COLLISION=warn`). `os lint` additionally emits a non-fatal `naming/namespace-prefix` warning for bare-named UI/automation items (app, page, dashboard, flow, action, report, dataset) when a namespace is set. -**Customizing an installed package (ADR-0146):** Customize an installed base `hotcrm` in its paired writable package `hotcrm.custom`, which depends on it: a same-named item there with `overrides: { package: 'hotcrm', basedOn: '3.2.0' }` (its `manifest.id`/`manifest.version`) replaces the base's document whole. The read honours it only from the pair (the row's `package_id`), today only for a view, dashboard, report, email_template or translation (page, app, dataset, doc, book, mapping, flow and action wait for their doors), never judges `basedOn` and ignores any other package's declaration. Then no `?package`, `?package=hotcrm`, the layered read and both lists serve it; the env-wide list holds one item per name one code package ships (a dependent's stays under its own `?package=`); a package-unbound Setup row still wins over the base's until D11. An undeclared pair row or a dependent's row is an orphan in the layered read's `overlay`, `effective` = the base's. An override may narrow a public form's anonymous intake, never re-open one any honoured body withdrew. `GET /packages/hotcrm.custom/export` carries it with `overrides`; the base's is unchanged. +**Customizing an installed package (ADR-0146):** Customize an installed base `hotcrm` in its paired writable package `hotcrm.custom`, which depends on it: a same-named item there with `overrides: { package: 'hotcrm', basedOn: '3.2.0' }` (its `manifest.id`/`manifest.version`) replaces the base's document whole. The read honours it only from the pair (the row's `package_id`), today only for a view, dashboard, report, email_template or translation (the rest of `OVERRIDABLE_METADATA_TYPES` wait for their doors), never judges `basedOn` and ignores any other package's declaration. An email template is one item per (name, locale): an override names a locale the base ships; a locale the base does not ship is added without `overrides`. Then no `?package`, `?package=hotcrm`, the layered read and both lists serve it; the env-wide list holds one item per name one code package ships (a dependent's stays under its own `?package=`); a package-unbound Setup row still wins over the base's until D11. An undeclared pair row or a dependent's row is an orphan in the layered read's `overlay`, `effective` = the base's. An override may narrow a public form's anonymous intake, never re-open one any honoured body withdrew. `GET /packages/hotcrm.custom/export` carries it with `overrides`; the base's is unchanged. --- @@ -227,7 +226,7 @@ my-app/ ## Driver Selection Guide -Drivers are the storage layer. Pick based on your environment: +Drivers are the storage layer: | Driver | Package | Best For | Notes | |:-------|:--------|:---------|:------| @@ -291,8 +290,6 @@ routes added to HttpDispatcher work automatically**. ## Runtime Boot Sequence -Understanding how ObjectStack starts helps debug and customize: - ``` objectstack.config.ts └── defineStack({ manifest, objects, views, ... }) From dd9e43d1e5650d6c761501d7ea4646bd5cd21ed7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:42:43 +0000 Subject: [PATCH 2/2] docs(skills,docs): flow is an honoured override today; name it beside the other five Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc --- content/docs/concepts/metadata-lifecycle.mdx | 6 +++--- skills/objectstack-platform/SKILL.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/docs/concepts/metadata-lifecycle.mdx b/content/docs/concepts/metadata-lifecycle.mdx index ab0f3949039..3bc2be9ada2 100644 --- a/content/docs/concepts/metadata-lifecycle.mdx +++ b/content/docs/concepts/metadata-lifecycle.mdx @@ -128,9 +128,9 @@ document **whole** for every read that serves the base's name override, `effective` = the override), the list scoped to the base and the environment-wide list all serve the override, stamped `_packageId: 'hotcrm'`. The declaration counts only from the pair — the stored row's own `package_id` must be - `hotcrm.custom` — and today only for a `view`, `dashboard`, `report`, `email_template` - or `translation`: the rest of `OVERRIDABLE_METADATA_TYPES` (`@objectstack/spec/kernel`) - — `page`, `app`, `dataset`, `doc`, `book`, `mapping`, `flow` and `action` — waits for + `hotcrm.custom` — and today only for a `view`, `dashboard`, `report`, `email_template`, + `translation` or `flow`: the rest of `OVERRIDABLE_METADATA_TYPES` (`@objectstack/spec/kernel`) + — `page`, `app`, `dataset`, `doc`, `book`, `mapping` and `action` — waits for its doors and is not served yet. A declaration from any other package is ignored. - **A new locale is not an override.** An `email_template` the pair saves at a locale the base does not ship is the pair's own member, served under `hotcrm.custom`, and carries no diff --git a/skills/objectstack-platform/SKILL.md b/skills/objectstack-platform/SKILL.md index bc1ed3481de..b7081c4c39f 100644 --- a/skills/objectstack-platform/SKILL.md +++ b/skills/objectstack-platform/SKILL.md @@ -130,7 +130,7 @@ to a new protocol major — never to silence a mismatch. **`manifest.namespace` (ADR-0048):** Optional, but **enforced once set**. When a package declares `manifest.namespace: 'crm'`, every `object.name` must start with `crm_` or `defineStack` errors (`validateNamespacePrefix` in `@objectstack/spec`); the legacy `__` double-underscore form is rejected, and `sys_`-prefixed names are platform-reserved and exempt. The namespace is also a package-ownership key — installing two packages that both claim `crm` fails with `NamespaceConflictError` (downgrade to a warning with `OS_METADATA_COLLISION=warn`). `os lint` additionally emits a non-fatal `naming/namespace-prefix` warning for bare-named UI/automation items (app, page, dashboard, flow, action, report, dataset) when a namespace is set. -**Customizing an installed package (ADR-0146):** Customize an installed base `hotcrm` in its paired writable package `hotcrm.custom`, which depends on it: a same-named item there with `overrides: { package: 'hotcrm', basedOn: '3.2.0' }` (its `manifest.id`/`manifest.version`) replaces the base's document whole. The read honours it only from the pair (the row's `package_id`), today only for a view, dashboard, report, email_template or translation (the rest of `OVERRIDABLE_METADATA_TYPES` wait for their doors), never judges `basedOn` and ignores any other package's declaration. An email template is one item per (name, locale): an override names a locale the base ships; a locale the base does not ship is added without `overrides`. Then no `?package`, `?package=hotcrm`, the layered read and both lists serve it; the env-wide list holds one item per name one code package ships (a dependent's stays under its own `?package=`); a package-unbound Setup row still wins over the base's until D11. An undeclared pair row or a dependent's row is an orphan in the layered read's `overlay`, `effective` = the base's. An override may narrow a public form's anonymous intake, never re-open one any honoured body withdrew. `GET /packages/hotcrm.custom/export` carries it with `overrides`; the base's is unchanged. +**Customizing an installed package (ADR-0146):** Customize an installed base `hotcrm` in its paired writable package `hotcrm.custom`, which depends on it: a same-named item there with `overrides: { package: 'hotcrm', basedOn: '3.2.0' }` (its `manifest.id`/`manifest.version`) replaces the base's document whole. The read honours it only from the pair (the row's `package_id`), today only for a view, dashboard, report, email_template, translation or flow (the rest of `OVERRIDABLE_METADATA_TYPES` wait for their doors), never judges `basedOn` and ignores any other package's declaration. An email template is one item per (name, locale): an override names a locale the base ships; a locale the base does not ship is added without `overrides`. Then no `?package`, `?package=hotcrm`, the layered read and both lists serve it; the env-wide list holds one item per name one code package ships (a dependent's stays under its own `?package=`); a package-unbound Setup row still wins over the base's until D11. An undeclared pair row or a dependent's row is an orphan in the layered read's `overlay`, `effective` = the base's. An override may narrow a public form's anonymous intake, never re-open one any honoured body withdrew. `GET /packages/hotcrm.custom/export` carries it with `overrides`; the base's is unchanged. ---