Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/28778-email-template-override-locale.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 10 additions & 4 deletions content/docs/concepts/metadata-lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -127,10 +128,15 @@ 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
`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
Expand Down
11 changes: 4 additions & 7 deletions skills/objectstack-platform/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand All @@ -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 `<ns>__<short>` 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, 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.

---

Expand Down Expand Up @@ -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 |
|:-------|:--------|:---------|:------|
Expand Down Expand Up @@ -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, ... })
Expand Down
Loading