@adobe/data-ai 0.10.21 → 0.10.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.claude/rules/README.md +26 -31
  2. package/.claude/rules/app.md +46 -0
  3. package/.claude/rules/archetypes.md +5 -3
  4. package/.claude/rules/data-modelling.md +8 -7
  5. package/.claude/rules/ecs.md +11 -7
  6. package/.claude/rules/element.md +6 -5
  7. package/.claude/rules/features/data/components.md +51 -0
  8. package/.claude/rules/features/data/entities.md +37 -0
  9. package/.claude/rules/features/data/index.md +18 -70
  10. package/.claude/rules/features/data/resources.md +34 -0
  11. package/.claude/rules/features/data/values.md +36 -0
  12. package/.claude/rules/features/ecs/actions.md +31 -0
  13. package/.claude/rules/features/ecs/computed.md +31 -0
  14. package/.claude/rules/features/ecs/conformance.md +85 -0
  15. package/.claude/rules/features/ecs/core.md +43 -0
  16. package/.claude/rules/features/ecs/index.md +72 -0
  17. package/.claude/rules/features/{services/main-service → ecs}/indexes.md +6 -7
  18. package/.claude/rules/features/ecs/services.md +36 -0
  19. package/.claude/rules/features/{services/main-service → ecs}/systems.md +61 -47
  20. package/.claude/rules/features/ecs/transactions.md +32 -0
  21. package/.claude/rules/features/index.md +104 -150
  22. package/.claude/rules/features/services/index.md +52 -84
  23. package/.claude/rules/features/spec/index.md +161 -0
  24. package/.claude/rules/features/ui/binding-element.md +5 -5
  25. package/.claude/rules/features/ui/index.md +8 -3
  26. package/.claude/rules/features/ui/lazy-wrapper.md +2 -2
  27. package/.claude/rules/features/ui/presentation-test.md +1 -0
  28. package/.claude/rules/features/ui/presentation.md +1 -0
  29. package/.claude/rules/global/function-references.md +2 -2
  30. package/.claude/rules/global/namespace.md +3 -3
  31. package/.claude/rules/global/react.md +4 -4
  32. package/.claude/rules/lit.md +2 -2
  33. package/.claude/rules/plugin-modelling.md +2 -0
  34. package/.claude/rules/service.md +2 -0
  35. package/.claude/rules/versioning.md +8 -6
  36. package/.claude-plugin/plugin.json +1 -1
  37. package/package.json +3 -3
  38. package/skills/build-actions/SKILL.md +7 -8
  39. package/skills/build-app-entry/SKILL.md +12 -8
  40. package/skills/build-computed/SKILL.md +8 -7
  41. package/skills/build-core-database/SKILL.md +18 -15
  42. package/skills/build-data/SKILL.md +14 -13
  43. package/skills/build-feature/SKILL.md +2 -1
  44. package/skills/build-game/SKILL.md +6 -4
  45. package/skills/build-indexes/SKILL.md +6 -7
  46. package/skills/build-service-database/SKILL.md +8 -8
  47. package/skills/build-services/SKILL.md +9 -7
  48. package/skills/build-spec/SKILL.md +23 -0
  49. package/skills/build-systems/SKILL.md +12 -18
  50. package/skills/build-transactions/SKILL.md +8 -8
  51. package/skills/build-ui/SKILL.md +5 -4
  52. package/skills/review/SKILL.md +3 -3
  53. package/.claude/rules/features/data/state.md +0 -209
  54. package/.claude/rules/features/services/main-service/actions.md +0 -70
  55. package/.claude/rules/features/services/main-service/archetypes.md +0 -49
  56. package/.claude/rules/features/services/main-service/components.md +0 -41
  57. package/.claude/rules/features/services/main-service/computed.md +0 -82
  58. package/.claude/rules/features/services/main-service/conformance.md +0 -95
  59. package/.claude/rules/features/services/main-service/core-database.md +0 -48
  60. package/.claude/rules/features/services/main-service/index.md +0 -187
  61. package/.claude/rules/features/services/main-service/resources.md +0 -28
  62. package/.claude/rules/features/services/main-service/services.md +0 -25
  63. package/.claude/rules/features/services/main-service/transactions.md +0 -59
@@ -1,187 +0,0 @@
1
- ---
2
- paths:
3
- - '**/features/*/services/main-service/main-service.ts'
4
- - '**/features/*/services/main-service/*-database/*-database.ts'
5
- ---
6
-
7
- # services/main-service/ — the feature service (ECS implementation)
8
-
9
- `main-service/` is the feature's single entrypoint service (see `../index.md`),
10
- implemented as a reactive Entity-Component-System — an implementation detail
11
- behind the service. Everything ECS-specific lives here: the schema that stores `data/` types as
12
- entity state (`core-database/`), the derived and mutating logic over it
13
- (`indexes/`, `transactions/`, `computed/`), the database-bound service factories
14
- (`services/`), the async orchestrators (`actions/`), and the layered plugins
15
- that compose it all.
16
-
17
- The distinction from `data/`: a `data/` type is ECS-agnostic (a plain
18
- serializable value); an ECS construct is *how that type is stored and operated
19
- on* inside a database. `mark: PlayerMark.schema` is not a data-model fact — it
20
- is "the ECS keeps a PlayerMark here".
21
-
22
- ## The core schema — `core-database/`
23
-
24
- A feature's whole schema — every component, resource, and archetype — lives in
25
- one `core-database/` folder: `core-database.ts` plus one file per facet the
26
- feature uses (all three facets are optional on `Database.Plugin.create`):
27
-
28
- ```
29
- core-database/
30
- core-database.ts # Database.Plugin.create({ components?, resources?, archetypes? }) → CoreDatabase
31
- components.ts # export const components = Database.components({ … })
32
- resources.ts # export const resources = Database.resources({ … })
33
- archetypes.ts # export const archetypes = Database.archetypes(components, { … })
34
- ```
35
-
36
- `core-database.ts` is a thin assembler; each facet file exports one typed object
37
- built by a `Database.*` helper. No per-item files, no barrels. Create only the
38
- facet files the feature needs — a singleton feature with no entities is just
39
- `resources.ts` + `core-database.ts` (no empty `components({})` boilerplate).
40
-
41
- ### Schema scopes
42
-
43
- State divides on two orthogonal axes — **scope** (shared with peers vs. local to
44
- this client) and **lifetime** (durable across reloads vs. ephemeral) — giving
45
- four scopes, each stamping a flag pair:
46
-
47
- | scope | shared? | durable? | flags |
48
- |-------|:--:|:--:|-------|
49
- | **document** | yes | yes | — |
50
- | **settings** | no | yes | `nonShared` |
51
- | **presence** | yes | no | `nonPersistent` |
52
- | **session** | no | no | `nonPersistent` + `nonShared` |
53
-
54
- `Database.components` / `Database.resources` group declarations by scope and
55
- stamp the flags. Every scope key is optional — declare only the ones you use
56
- (most features are document-only):
57
-
58
- ```ts
59
- // components.ts
60
- export const components = Database.components({
61
- document: { todo: True.schema, name: Name.schema, complete: Boolean.schema },
62
- session: { dragPosition: DragPosition.schema }, // omit settings / presence
63
- });
64
-
65
- // resources.ts — entries must carry a `default` (enforced at the call)
66
- export const resources = Database.resources({
67
- settings: { displayCompleted: Boolean.schema },
68
- });
69
- ```
70
-
71
- A component/resource *value* is a plain `data/`-schema re-export (or a small
72
- inline literal); its scope — and therefore its flags — is stated only by which
73
- group it sits in. Hover a scope key for its meaning. The database does not yet
74
- act on these flags (see the `nonShared` schema note); they model
75
- shared/local × durable/ephemeral now, and the durable-shared subset
76
- (`document`) is the set a bare store could be rebuilt from for reconciliation.
77
-
78
- **Shared columns live in `data/`.** A column two features share (e.g. a `name`
79
- on both todos and users) is a `data/` type both reference by identity
80
- (`Name.schema`), so `combinePlugins` dedupes it. Only the `document` scope
81
- preserves schema identity (it applies no flags) — never share a
82
- settings/presence/session column across features by identity.
83
-
84
- ### Archetypes
85
-
86
- `archetypes.ts` declares the feature's entity shapes with
87
- `Database.archetypes(components, { … })`, which validates every key against the
88
- component map and preserves each archetype's literal tuple — no per-archetype
89
- `as const`. Archetypes are a packing convenience, not serialized data; one may
90
- span scopes (a document entity with an ephemeral `dragPosition` slot). An
91
- archetype mirroring a `data/` row type carries a private compile-time
92
- drift-guard (see `archetypes.md`).
93
-
94
- ## Layered composition (behaviour)
95
-
96
- Above the schema, behaviour is a chain of `Database.Plugin`s, each its own
97
- **namespace folder** — `<layer>-database/` holding the eponymous
98
- `<layer>-database.ts` (the plugin, which `extends` the previous layer) plus the
99
- facet folder it adds. **Name each layer for the facet it adds — never for the
100
- feature or app.** A feature creates only the layers it uses:
101
-
102
- ```
103
- main-service/
104
- core-database/ # the schema (above)
105
- index-database/ # extends core; indexes/
106
- transaction-database/ # extends index; transactions/
107
- service-database/ # extends transaction; services/
108
- computed-database/ # extends service; computed/
109
- action-database/ # extends computed; actions/
110
- ```
111
-
112
- Each exports a namespace mirroring the plugin — the composed object is a
113
- `Database` (the `@adobe/data` term) even though the folder is `services/main-service/`:
114
-
115
- ```ts
116
- export type IndexDatabase = Database.Plugin.ToDatabase<typeof indexDatabasePlugin>;
117
- export namespace IndexDatabase {
118
- export const plugin = indexDatabasePlugin;
119
- export type Store = Database.Plugin.ToStore<typeof indexDatabasePlugin>;
120
- }
121
- ```
122
-
123
- ### The assembled database — `MainService`
124
-
125
- *Which* layer is topmost varies with the facets a feature uses (service /
126
- computed / action / system). Consumers — `ui/`, the app entry, `services/main-service/conformance/`
127
- — need the *whole* feature database but must not name that layer, or adding or
128
- dropping a layer rewrites them all. Alias it once from the **folder-eponymous
129
- `main-service.ts`** (per `global/namespace.md`, a folder's primary export lives in
130
- its same-named file), as a namespace so consumers reach `MainService.plugin` /
131
- `MainService.Store`:
132
-
133
- ```ts
134
- // services/main-service/main-service.ts
135
- export { SystemDatabase as MainService } from "./system-database/system-database.js";
136
- ```
137
-
138
- Every consumer references **`MainService`** (`.plugin`, `.Store`) — never the
139
- topmost layer. Add or drop a layer → change only this one line.
140
-
141
- **Cross-feature naming.** Inside a feature the core and assembled databases are
142
- the bare `CoreDatabase` / `MainService`. When *another* feature or package
143
- imports one — a peer built on it, the base `imports` a peer's `core-database`, a
144
- downstream package — reference it **feature-qualified**: `<Feature>CoreDatabase`
145
- / `<Feature>MainService`, re-exported so from the feature's barrel
146
- (`export { CoreDatabase as TodoCoreDatabase } from "…/core-database.js"`) — so
147
- two features' `CoreDatabase`s never collide.
148
-
149
- ## What each layer takes as input
150
-
151
- - **`plugin`** — every layer `extends` the previous layer's `plugin`; that is
152
- the one universal thread up the chain.
153
- - **`Store`** (via `ToStore`) — used *only* by **transactions** (`t:
154
- <Layer>.Store`). A store carries the index handles and the initiating
155
- `userId`, so a store *is* the transaction context; there is no separate
156
- transaction-context type. Use **`CoreDatabase.Store`** for a transaction that
157
- reads/writes entities, resources, or archetypes; **`IndexDatabase.Store`** the
158
- moment it reads an index.
159
- - **the whole `Database`** — **services**, **computed**, and **actions** each
160
- take `service: <Layer>` (the lowest layer whose database exposes what they
161
- read/call): services read observables and call transactions; computed reads
162
- `service.observe.*` and, once it needs a service's own reactive surface,
163
- `service.services.*`; actions call `service.services.*` then
164
- `service.transactions.*`.
165
-
166
- Each subfolder has its own rule. Modelling a plugin's authored vs. derived
167
- surface is covered by `plugin-modelling.md`.
168
-
169
- ## Facets are flat until they aren't
170
-
171
- A **behavioural** facet folder (`indexes/`, `transactions/`, `computed/`,
172
- `actions/`) holds one file per item plus an `index.ts` barrel — flat while that
173
- reads well, roughly up to half a dozen items. When a cohesive cluster grows past
174
- that, promoting it into a themed subfolder with its own `index.ts` (re-exported
175
- by the facet barrel) is a standard move — only once the folder actually feels
176
- crowded:
177
-
178
- ```
179
- transactions/
180
- order/ # a cohesive cluster: fractional-ordering helpers
181
- index.ts next-order.ts normalize-order.ts select-ordered-todos.ts
182
- create-todo.ts drag-todo.ts …
183
- ```
184
-
185
- The **schema** facets (components / resources / archetypes) are *single files*
186
- in `core-database/`, not folders. A whole feature should stay small enough that
187
- subfoldering rarely bites — grow by adding features, not by bloating one.
@@ -1,28 +0,0 @@
1
- ---
2
- paths:
3
- - '**/features/*/services/main-service/**/core-database/resources.ts'
4
- ---
5
-
6
- # core-database/resources.ts — the feature's resources
7
-
8
- A resource is a singleton component — one instance held on a single entity. One
9
- file, one export: `resources`, built with `Database.resources(...)`. It is
10
- identical to `Database.components` except **every entry must carry a `default`**
11
- — a missing `default` is a compile error here, at the declaration site.
12
-
13
- ```ts
14
- import { Database } from "@adobe/data/ecs";
15
- import { Boolean } from "@adobe/data/schema";
16
-
17
- export const resources = Database.resources({
18
- settings: { displayCompleted: Boolean.schema }, // per-device view toggle
19
- });
20
- ```
21
-
22
- - Give each resource a `default` — spread the data-type's schema and add one, or
23
- bind a `data/` / primitive schema that already carries a sensible default.
24
- - **Scope = which group it sits in** (see `services/main-service/index.md`). A per-device
25
- preference is `settings` (durable, `nonShared`); a synced-but-unsaved value is
26
- `presence`; a saved shared value is `document`.
27
- - Because there is only ever one instance, linear-memory packing buys nothing —
28
- never choose a struct schema for efficiency here.
@@ -1,25 +0,0 @@
1
- ---
2
- paths:
3
- - '**/features/*/services/main-service/service-database/services/**/*.ts'
4
- ---
5
-
6
- # database/services/ — database-bound service factories
7
-
8
- One factory per file: `create<Name>Service(service, …)` that binds a service
9
- implementation to a live database — reading its observables and invoking
10
- its transactions. This is where the async `services/` contracts (or
11
- framework services like `AgenticService`) are wired to ECS state.
12
-
13
- ```ts
14
- export const createAgentService = (service: TransactionDatabase, mark: PlayerMark): AgenticService =>
15
- /* … reads service.observe.*, calls service.transactions.* … */;
16
- ```
17
-
18
- Type `service` on the lowest layer exposing what the factory reads/calls (never
19
- the `service-database` itself — that would be a cycle).
20
-
21
- An `index.ts` barrel re-exports the factories; `service-database.ts`
22
- registers them under the `services` facet, each keyed by the name
23
- consumers read it as (`service.services.agent`). Defining a standalone
24
- service contract (not yet bound to a database) belongs in the feature
25
- `services/` layer.
@@ -1,59 +0,0 @@
1
- ---
2
- paths:
3
- - '**/features/*/services/main-service/transaction-database/transactions/**/*.ts'
4
- ---
5
-
6
- # database/transactions/ — atomic mutations
7
-
8
- One mutation per file: a function taking the transaction store as its
9
- first argument and pure `data/` args, returning `void`. It reads and
10
- writes `t.resources` / entities; all its writes commit or roll back
11
- together.
12
-
13
- ```ts
14
- import { PlayMoveArgs } from "../../../data/play-move-args/play-move-args.js";
15
- import type { CoreDatabase } from "../../core-database/core-database.js";
16
-
17
- export const playMove = (t: CoreDatabase.Store, { index }: PlayMoveArgs) => {
18
- // guard, then mutate t.resources / entities / archetypes
19
- };
20
- ```
21
-
22
- - Type the store parameter as `<Layer>.Store`: **`CoreDatabase.Store`** for a
23
- transaction that reads/writes entities, resources, or archetypes (the common
24
- case — all schema lives in `core-database`); **`IndexDatabase.Store`** the
25
- moment it reads an index. A store *is* the transaction context: it carries the
26
- index handles and the initiating `t.userId`. There is no separate
27
- transaction-context type.
28
- - Keep transactions idempotent under replay where sync/P2P applies —
29
- validate and silently return on illegal or out-of-turn input rather
30
- than throwing.
31
- - Decisions come from pure `data/` helpers; the transaction only applies the
32
- result — read the touched slice, call the `data/` transform, write the diff.
33
- - Keep transaction files **single-export** (the `transactions/` barrel is
34
- `export *`-ed into the plugin facet, so a second export would pollute it).
35
- - **ECS-based feature (no `data/state/`):** there are no conformance cases, so
36
- **every transaction carries its own unit test** — `Store.create(plugin)`, run the
37
- transaction, assert the resulting resources/entities/archetypes. That test is the
38
- direct substitute for the conformance oracle (see `../../index.md`, Two modes). The
39
- bullet below applies only to **state-based** features.
40
- - **Conformance is wired once, centrally, and auto-paired** — not per-file. The
41
- feature's single `conformance/conformance.test.ts` `Conformance.checkFeature(spec)`
42
- call conforms transactions: it pulls them off **`plugin.transactions`** (the
43
- registered facet), pairs each to the **same-named** `data/state` transition, and
44
- conforms it — seed `fromState(before)`, apply, `Match.assert` `toState ≡ after`
45
- (state only; service effects are asserted through the action). There is **no**
46
- `define`/`conforms` adapter and **no** `covers` guard. A transaction taking
47
- **entity ids** takes them under the transition's own arg key (`{ id }`); the runner
48
- resolves each id the case's `args` schema marks via the id→entity map `fromState` returns. A
49
- transaction with **no same-named transition** is infrastructure (`setInput`,
50
- `setBounds`) or the drag UI op (`dragTodo`) or system-dispatched — it is simply
51
- **skipped**, no guard needed. Don't add a per-item adapter for a renamed op; add a
52
- thin same-named transaction instead (see `conformance.md`).
53
- - An `index.ts` barrel feeds the `transactions` plugin facet — so it must
54
- re-export **only** the mutations. A read/query helper shared by several
55
- transactions (`readShip`, `readBoard` — a `(t) => value` function) may live
56
- beside them in `transactions/`, but keep it **out of the barrel**, or it gets
57
- registered as a dispatchable transaction. (These are related helpers, so they
58
- belong here, not in a separate folder — `global/cohesion.md`'s peer-level caution
59
- doesn't apply.)