Skip to content

Commit c0a65fc

Browse files
committed
docs(skills,docs): a locale added in the .custom pair is served on the base's address; a dependent's added locale stays under its own package
The ADR-0146 customization paragraph of skills/objectstack-platform/SKILL.md and the ADR-0146 section of content/docs/concepts/metadata-lifecycle.mdx state, beside the override-locale rule, where an added email template locale is served: the pair's on the base's address (both lists, the execution list, a send in that locale), a dependent's only under its own package. The skill's new sentence is paid for by tightening four restating clauses elsewhere in the file; no rule is dropped. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
1 parent bf3e9fe commit c0a65fc

3 files changed

Lines changed: 25 additions & 8 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-platform says a locale added in the `.custom` pair is served on the base's address, and a locale a dependent adds under the base's name stays under its own package
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The ADR-0146 customization paragraph said a locale the base does not ship is added in the pair without `overrides`, and stopped there: nothing said where that added locale is served, and nothing said what a dependent package's added locale under the base's name reaches. The reads of a base's address now decide an email template member by member, so an author could expect the pair's added locale to stay under `hotcrm.custom` only, or a dependent's to answer on the base's address.
11+
- **Now.** One sentence beside that rule: the base's address serves the pair's added locale too (both lists, the execution list, a send in that locale), and a locale a dependent adds under the base's name is served only under its own `?package=`, never by a base-named or package-less read. The file stays under its token ceiling: the sentence is paid for by tightening four restating clauses elsewhere in the file. No rule is removed, no frontmatter, schema or behaviour changes.

‎content/docs/concepts/metadata-lifecycle.mdx‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ document **whole** for every read that serves the base's name
137137
`overrides`; declaring one there is refused (`403 NOT_OVERRIDABLE` at the save, publish
138138
and install doors, a row from `os validate`, the sentence naming the locale), because the
139139
base ships no such member to replace.
140+
- **The pair's added locale is served on the base's address; a dependent's is not.** The
141+
member `hotcrm.custom` adds is listed by `GET /meta/email_template` with no `?package`
142+
and with `?package=hotcrm`, is in the execution list the email service reads, and so is
143+
what a send in that locale renders — stamped `_packageId: 'hotcrm.custom'`, as the
144+
by-name read already served it. A locale another package that depends on `hotcrm`
145+
adds under the base template's name is that package's own item, served only under
146+
`?package=` naming it and in the list scoped to it: the read naming `hotcrm` and the read
147+
naming no package answer `404`, and the base's lists leave it out.
140148
- **The environment-wide list** serves one item per name that exactly one code package
141149
ships. A dependent package's same-named item is not listed beside it and stays under
142150
its own `?package=` address. A package-unbound row — the overlay row of the table

‎skills/objectstack-platform/SKILL.md‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ are `manifest`, `packages`, `objects`, `objectExtensions`, `picklists`,
4444
There is deliberately **no** top-level `workflows` or `approvals` collection:
4545
an approval is a flow with Approval nodes, and record
4646
state machines are a `state_machine` validation rule on each object.
47-
A phantom key like `roles:` or `policies:` is **not** a silent
48-
no-op — the top level refuses it and the stack fails to load.
47+
A phantom key like `roles:` or `policies:` is refused at the top level.
4948

5049
`queues` declares ownership queues (`defineQueue`, `@objectstack/spec/security`;
5150
objectstack-data): named sets of people a new record of an `ownership: 'user'`
@@ -65,9 +64,8 @@ Undeclared keys are handled per surface, in two postures:
6564
been closed yet (`connectors` is one). `defineStack()` prints the warning
6665
before the parse, and the value does not survive it:
6766
`defineStack: connectors.stripe.bogusKey: 'bogusKey' is not a declared connector key, so its value is dropped at load.`
68-
Treat these as errors-in-waiting — closing the remaining shapes is a
69-
scheduled migration, so a key that only warns today is expected to be
70-
refused later.
67+
Treat these as errors-in-waiting: a key that only warns today is expected
68+
to be refused later.
7169

7270
Exact shape: `ObjectStackDefinitionSchema` in
7371
`node_modules/@objectstack/spec/src/stack.zod.ts`; each collection's item
@@ -130,7 +128,7 @@ to a new protocol major — never to silence a mismatch.
130128

131129
**`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.
132130

133-
**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.
131+
**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`. The base's address serves that added locale too (both lists, the execution list, a send in that locale); a locale a dependent adds under the base's name is served only under its own `?package=`, never by a base-named or package-less read. 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.
134132

135133
---
136134

@@ -183,7 +181,7 @@ The five retired remote templates (`todo`, `compliance`, `content`,
183181

184182
## Project Structure Conventions
185183

186-
Every ObjectStack project follows this directory structure:
184+
Project layout:
187185

188186
```
189187
my-app/
@@ -242,7 +240,7 @@ Under `os dev` / `os serve` / `os start` the CLI **resolves the driver itself**
242240
from the database URL and registers `DriverPlugin` for you. Do **not** put a
243241
driver in your config's `plugins:` array: no example app does, and the
244242
`plugins:` key is for plugins the CLI cannot infer (connector executors, your
245-
own plugins). Pick a driver by setting the DB URL, not by writing code.
243+
own plugins).
246244

247245
Construct `DriverPlugin` yourself only when **you** own the runtime — embedding
248246
via `Runtime` / `ObjectKernel`, or a test that boots a kernel directly:

0 commit comments

Comments
 (0)