Status: Accepted (2026-09-02).
The 14 data-model interfaces previously living in packages/client/src/models/*.ts
move into a new root package, @localess/model, with zero production
dependencies, plus a new Space model (previously only defined, with a
boundary violation, inside @localess/cli). @localess/client becomes a
dependent of @localess/model rather than a root. @localess/richtext and
@localess/schema also gain a dependency on @localess/model, in exchange
for deleting their own structural duplicates of the same shapes
(ContentRichTextLike and content-types.ts respectively).
This is the fourth package added to the client/richtext/schema family, triggering ADR 005's "no fourth shared package is created without explicit discussion" rule — this ADR is that discussion.
Root tier after this change: @localess/model, @localess/richtext,
@localess/schema — three packages depending on nothing outside that tier
(@localess/model on nothing at all; @localess/richtext and
@localess/schema on @localess/model alone).
@localess/client moves to the dependent tier, depending on
@localess/model alone. Framework packages (react, vue, svelte,
angular, astro) and @localess/cli depend on @localess/model directly
for the types that moved, rather than transitively through
@localess/client.
Two confirmed structural duplicates existed because @localess/richtext and
@localess/schema are deliberately root packages that cannot depend on
@localess/client (ADR 007, ADR 008):
@localess/schema/src/content-types.tswas an exact structural copy ofContentAsset/ContentLink/ContentReference/ContentRichText, guarded only by a parity type test keeping the two copies in sync.@localess/richtext/src/model.ts'sContentRichTextLikewas a smaller structural copy ofContentRichText.
Both were symptoms of the same root cause: no package existed that
@localess/client, @localess/richtext, and @localess/schema could all
depend on without reintroducing a dependency on the client SDK itself
(which carries a secret-bearing API token and an I/O surface — the actual
thing ADR 007 and ADR 008 were protecting against, not "any dependency
whatsoever"). @localess/model resolves this: it is itself zero-dependency,
so depending on it preserves ADR 007/008's real intent while eliminating
the duplication.
@localess/client,@localess/richtext, and@localess/schemaare no longer zero-internal-dependency (each now depends on@localess/model), though all three remain zero-external-dependency — no npm package outside this monorepo. Only@localess/modelitself has zero dependencies of any kind (nodependencieskey at all). CLAUDE.md's zero-production-dependency rule (ADR 002) is updated to track this distinction.@localess/richtext's public API losesContentRichTextLikeand gainsContentRichText(re-exported from@localess/model) in its place — a rename, consistent with this repo's pre-1.0 clean-break migration philosophy (see ADR 007's own "Migration" section for precedent).@localess/schema's public API losesSchemaContentAsset/SchemaContentLink/SchemaContentReference/SchemaContentRichTextand gains the canonicalContentAsset/ContentLink/ContentReference/ContentRichText(re-exported from@localess/model) in their place — same rationale; nothing outside this repo depended on the prefixed names yet.- The CLI's documented boundary gap (
src/models/space.tsimportingLocalefrom@localess/clientdirectly) is resolved as a side effect —Spacenow lives in@localess/modelalongsideLocale. - No runtime behavior changes anywhere. This is a type/interface relocation only.
In a follow-up to the original change, the schema wire model previously
declared in packages/schema/src/models.ts — SchemaType,
SchemaFieldKind, AssetFileType, SchemaEnumValue, SchemaFieldBase,
the 18 per-kind SchemaField* interfaces, SchemaField,
SchemaComponentExport, SchemaEnumExport, SchemaExport — moved to
packages/model/src/schema.ts and is exported from @localess/model.
packages/schema/src/models.ts became export * from '@localess/model',
and @localess/schema's public API keeps re-exporting all of those names,
so nothing changed for its consumers. The motivation is the same as above:
@localess/cli consumes SchemaExport for its schema pull/push/diff
HTTP payloads, and a wire shape used by more than one package belongs in the
shared model package rather than in the authoring package.
@localess/model is meant to be where the next shared model goes, not
just the ones moved in this change — see packages/model/CONTRIBUTING.md
for how to add one.