@adobe/data-ai 0.9.89 → 0.9.90
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.
- package/.claude/rules/README.md +11 -10
- package/.claude/rules/element.md +1 -1
- package/.claude/rules/features/data/state.md +2 -2
- package/.claude/rules/features/index.md +55 -39
- package/.claude/rules/features/services/index.md +31 -18
- package/.claude/rules/features/{ecs → services/main-service}/actions.md +2 -2
- package/.claude/rules/features/{ecs → services/main-service}/archetypes.md +1 -1
- package/.claude/rules/features/{ecs → services/main-service}/components.md +2 -2
- package/.claude/rules/features/{ecs → services/main-service}/computed.md +1 -1
- package/.claude/rules/features/{ecs → services/main-service}/conformance.md +3 -3
- package/.claude/rules/features/{ecs → services/main-service}/index.md +10 -8
- package/.claude/rules/features/{ecs → services/main-service}/indexes.md +1 -1
- package/.claude/rules/features/{ecs → services/main-service}/resources.md +2 -2
- package/.claude/rules/features/{ecs → services/main-service}/services.md +1 -1
- package/.claude/rules/features/{ecs → services/main-service}/systems.md +3 -3
- package/.claude/rules/features/{ecs → services/main-service}/transactions.md +2 -2
- package/.claude/rules/features/ui/index.md +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +4 -5
- package/skills/build-actions/SKILL.md +4 -4
- package/skills/build-app-entry/SKILL.md +1 -1
- package/skills/build-computed/SKILL.md +3 -3
- package/skills/build-core-database/SKILL.md +3 -3
- package/skills/build-game/SKILL.md +1 -1
- package/skills/build-indexes/SKILL.md +3 -3
- package/skills/build-service-database/SKILL.md +3 -3
- package/skills/build-services/SKILL.md +1 -1
- package/skills/build-systems/SKILL.md +4 -4
- package/skills/build-transactions/SKILL.md +3 -3
- package/skills/build-ui/SKILL.md +1 -1
package/.claude/rules/README.md
CHANGED
|
@@ -14,16 +14,17 @@ child gets its own file (recursing wherever the source tree does):
|
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
features/
|
|
17
|
-
index.md # the feature layering as a whole (ui →
|
|
17
|
+
index.md # the feature layering as a whole (ui → services → data)
|
|
18
18
|
data/
|
|
19
19
|
index.md # data-type namespaces (foundation)
|
|
20
20
|
state.md # the State aggregate: pure transforms & derivations
|
|
21
|
-
services/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
services/
|
|
22
|
+
index.md # the services layer: main-service + capability contracts
|
|
23
|
+
main-service/ # the feature service (ECS implementation)
|
|
24
|
+
index.md # the service + its layered database plugins
|
|
25
|
+
components.md resources.md archetypes.md
|
|
26
|
+
computed.md indexes.md transactions.md services.md actions.md
|
|
27
|
+
systems.md conformance.md
|
|
27
28
|
ui/index.md # UI (points to element / presentation / lazy-element file rules)
|
|
28
29
|
```
|
|
29
30
|
|
|
@@ -37,6 +38,6 @@ symlinks them into its own `.claude/rules/` (as it does `features/`).
|
|
|
37
38
|
Each rule's `paths:` glob is scoped to `**/features/*/<layer>/…`, so these
|
|
38
39
|
rules apply only once an application opts into the feature-folder pattern
|
|
39
40
|
(source organized under `features/<name>/`). Editing
|
|
40
|
-
`features/<name>/
|
|
41
|
-
`
|
|
42
|
-
at creation and at every later edit.
|
|
41
|
+
`features/<name>/services/main-service/core-database/components.ts` pulls in both
|
|
42
|
+
`main-service/index.md` (the layer overview) and `main-service/components.md`
|
|
43
|
+
(the specifics) — at creation and at every later edit.
|
package/.claude/rules/element.md
CHANGED
|
@@ -124,7 +124,7 @@ Lit-only: `static styles = styles` from a sibling `*.css.ts`. `@property`
|
|
|
124
124
|
exists *only* for locator inputs — never for state, never for derived
|
|
125
125
|
values, never for parent-forwarded flags. The `P` in `DatabaseElement<P>` (and
|
|
126
126
|
the `get plugin()` it returns) is the surface the element *consumes* — never the
|
|
127
|
-
varying topmost-layer name (see `features/
|
|
127
|
+
varying topmost-layer name (see `features/services/main-service/index.md`): `typeof
|
|
128
128
|
FeatureDatabase.plugin` for a self-contained feature. **Exception:** an element
|
|
129
129
|
meant to be *extended* — injected with various databases that build on this
|
|
130
130
|
feature (a peer app that adds an agent, a p2p/presence build) — types on the
|
|
@@ -43,8 +43,8 @@ export * as State from "./public.js";
|
|
|
43
43
|
- Each transform has a sibling `*.test.ts`; performance is irrelevant here,
|
|
44
44
|
correctness is everything. Keep its cases in a sibling `<transform>.cases.ts`
|
|
45
45
|
typed `ConformanceCase<Args>[]` (the shared `conformance-case.ts` type) —
|
|
46
|
-
spec-owned truth the matching
|
|
47
|
-
`
|
|
46
|
+
spec-owned truth the matching main-service conformance test imports unchanged (see
|
|
47
|
+
`services/main-service/conformance.md`). Author `before`/`after` as full `State`
|
|
48
48
|
(`{ ...State.create(), …overrides }`); the generic-slice signature lets them
|
|
49
49
|
flow through. Tolerant full-`State` equality is the shared
|
|
50
50
|
`expect-state-matches.ts`, alongside the cases here.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- '**/features/*/data/**/*.ts'
|
|
4
4
|
- '**/features/*/services/**/*.ts'
|
|
5
|
-
- '**/features/*/ecs/**/*.ts'
|
|
6
5
|
- '**/features/*/ui/**/*.ts'
|
|
7
6
|
---
|
|
8
7
|
|
|
@@ -14,34 +13,39 @@ trust, and an **efficient implementation** proven equivalent to it.
|
|
|
14
13
|
- **`data/` is the specification.** The whole feature modelled as one immutable
|
|
15
14
|
`State` with pure transformations over it — correct by construction, fully
|
|
16
15
|
unit-tested, performance irrelevant. This is the source of truth.
|
|
17
|
-
- **`
|
|
18
|
-
mutation efficiency
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
- **`services/main-service/` is the implementation.** The same model physically
|
|
17
|
+
arranged for mutation efficiency — a reactive Entity-Component-System, an
|
|
18
|
+
implementation detail behind the service. Its reads and writes wrap the
|
|
19
|
+
`data/` functions where practical; where they must be hand-optimized, unit
|
|
20
|
+
tests verify they still agree with the `data/` precedent.
|
|
21
21
|
|
|
22
|
-
Because the optimized `
|
|
23
|
-
it can be generated and kept honest by AI rules, with the conformance
|
|
24
|
-
the safety net. Net result: write a slow-but-verifiable app, then
|
|
25
|
-
one that provably behaves the same.
|
|
22
|
+
Because the optimized `main-service` is largely mechanical given the `data/`
|
|
23
|
+
spec, it can be generated and kept honest by AI rules, with the conformance
|
|
24
|
+
tests as the safety net. Net result: write a slow-but-verifiable app, then
|
|
25
|
+
derive a fast one that provably behaves the same.
|
|
26
26
|
|
|
27
|
-
## The
|
|
27
|
+
## The layers
|
|
28
28
|
|
|
29
|
-
`ui →
|
|
29
|
+
`ui → services → data` — higher imports lower, never the reverse. `data/`
|
|
30
30
|
depends on nothing but `@adobe/data` and other `data/` declarations. A feature
|
|
31
31
|
creates only the layers it uses.
|
|
32
32
|
|
|
33
33
|
| Layer | Role |
|
|
34
34
|
|-------|------|
|
|
35
35
|
| `data/` | The spec: `State`, pure transforms & derivations, entity sub-types. Pure, tested. |
|
|
36
|
-
| `services/` |
|
|
37
|
-
| `
|
|
36
|
+
| `services/main-service/` | The implementation: the ECS materialisation + reactive reads/writes. The **sole entrypoint** — the only service the `ui/` binds to. |
|
|
37
|
+
| `services/<name>-service/` | Async capability contracts (ports to the outside world). Reached only *through* `main-service` (its actions/services wire them in), never by the UI. Optional. |
|
|
38
38
|
| `ui/` | Presentation. |
|
|
39
39
|
|
|
40
|
+
`services/` holds two kinds of service: the one `main-service/` (ECS-backed
|
|
41
|
+
state, its own subtree of rules) and any async capability contracts. The UI, and
|
|
42
|
+
every external consumer, sees only `main-service`.
|
|
43
|
+
|
|
40
44
|
## One app, many features
|
|
41
45
|
|
|
42
46
|
An application is a set of **features**, each its own `features/<name>/` folder
|
|
43
|
-
with the same
|
|
44
|
-
|
|
47
|
+
with the same layers. One base feature (`features/main/`) is the host; the rest
|
|
48
|
+
are peers that load lazily.
|
|
45
49
|
|
|
46
50
|
**Keep each feature small; grow by adding features, not by bloating one.** A
|
|
47
51
|
feature is meant to fit in the head — a handful of files per layer. When a
|
|
@@ -53,16 +57,17 @@ its own peer feature rather than let one feature's folders balloon.
|
|
|
53
57
|
base must not depend on its children — with one sanctioned exception below.
|
|
54
58
|
- **The base `imports` every peer's *schema* plugin** —
|
|
55
59
|
`Database.Plugin.create({ imports })`, not `extends`. `imports` merges the
|
|
56
|
-
peer's `core-database` (components / resources /
|
|
57
|
-
store at runtime **without** pulling its types or
|
|
58
|
-
or bundle (`extends` would do both, and cost
|
|
59
|
-
every feature's schema — data coexists,
|
|
60
|
-
stays decoupled. Import the peer's
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
peer's `services/main-service/core-database` (components / resources /
|
|
61
|
+
archetypes) into the shared store at runtime **without** pulling its types or
|
|
62
|
+
behavior into the base's type or bundle (`extends` would do both, and cost
|
|
63
|
+
quadratically). So one store knows every feature's schema — data coexists,
|
|
64
|
+
persists, and syncs — while the base stays decoupled. Import the peer's
|
|
65
|
+
`services/main-service/core-database.ts` plugin (schema only) — under its
|
|
66
|
+
feature-qualified name `<Peer>CoreDatabase` (see the cross-feature naming rule
|
|
67
|
+
in `services/main-service/index.md`): its indexes, transactions, computed,
|
|
68
|
+
services, and UI stay out until the feature loads. A column two features share
|
|
69
|
+
(e.g. `name`) lives in `data/` and is referenced by identity, so
|
|
70
|
+
`combinePlugins` dedupes it.
|
|
66
71
|
- **Peers load lazily by being used.** `DatabaseElement`, on connect, walks up to
|
|
67
72
|
the nearest ancestor database and `extend`s it with its own plugin — so the
|
|
68
73
|
first time a feature element renders, its full plugin (indexes, transactions,
|
|
@@ -71,25 +76,36 @@ its own peer feature rather than let one feature's folders balloon.
|
|
|
71
76
|
load is genuinely on-demand.
|
|
72
77
|
- **The base reaches a child only through a lazy element wrapper** — a tiny
|
|
73
78
|
`Foo()` that `void import()`s the child element. That dynamic import is the one
|
|
74
|
-
allowed core→child seam; the heavy element and its service
|
|
79
|
+
allowed core→child seam; the heavy element and its main-service stay in the
|
|
75
80
|
child's own chunk.
|
|
76
81
|
|
|
77
82
|
## Spec and implementation, kept honest
|
|
78
83
|
|
|
79
|
-
The tie between `data/` (spec) and `
|
|
80
|
-
one property —
|
|
81
|
-
|
|
84
|
+
The tie between `data/` (spec) and `main-service` (implementation) is
|
|
85
|
+
**conformance**, one property —
|
|
86
|
+
`toState(apply(fromState(before), args)) ≡ transform(before, args)`: each
|
|
87
|
+
main-service mutation, seeded and read back through a test-only store↔`State`
|
|
82
88
|
projection, equals the pure `data/` transform it stands for. The projection and
|
|
83
|
-
its runner live in `
|
|
84
|
-
`{ before, args, after }`
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
below.
|
|
89
|
+
its runner live in `services/main-service/conformance/` (see
|
|
90
|
+
`services/main-service/conformance.md`); the shared `{ before, args, after }`
|
|
91
|
+
cases are spec-owned (`data/state/<transform>.cases.ts`), so conforming the
|
|
92
|
+
implementation is "substitute the implementation, reuse the expectations." This
|
|
93
|
+
lets `main-service` be largely mechanical and agent-generated, with the spec as
|
|
94
|
+
oracle. *How* to author each layer lives in the per-folder rules below.
|
|
95
|
+
|
|
96
|
+
## Reference implementations
|
|
97
|
+
|
|
98
|
+
Working samples ship inside `@adobe/data` at
|
|
99
|
+
`node_modules/@adobe/data/references/<sample>/src/` — read them for concrete,
|
|
100
|
+
current examples of this structure. `data-lit-todo` is the most complete
|
|
101
|
+
(multi-feature, capability services, indexes, conformance tests);
|
|
102
|
+
`data-lit-tictactoe` is the minimal turn-based reference.
|
|
89
103
|
|
|
90
104
|
## Per-layer detail
|
|
91
105
|
|
|
92
|
-
See the rules under each folder: `data/`, `services
|
|
93
|
-
resources, archetypes, computed, indexes, transactions
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
See the rules under each folder: `data/`, `services/` — the `main-service/`
|
|
107
|
+
subtree (components, resources, archetypes, computed, indexes, transactions,
|
|
108
|
+
systems, conformance) and capability-contract services — and `ui/`.
|
|
109
|
+
Cross-cutting patterns live at the rules root — `namespace.md`,
|
|
110
|
+
`data-modelling.md`, `archetypes.md` (row iteration), `type-casts.md`,
|
|
111
|
+
`cohesion.md`.
|
|
@@ -3,32 +3,45 @@ paths:
|
|
|
3
3
|
- '**/features/*/services/**/*.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# services/ —
|
|
6
|
+
# services/ — the feature's services
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
is the boundary between pure feature code and the outside world, which is
|
|
10
|
-
why its members are async — enabling cross-process portability and lazy
|
|
11
|
-
loading (`AsyncDataService.createLazy`).
|
|
8
|
+
Everything a feature exposes as a service lives here. Two kinds:
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
- **`main-service/`** — the one entrypoint. The ECS-backed reactive state
|
|
11
|
+
service (materialisation + reads/writes over it); the only service the `ui/`
|
|
12
|
+
and external consumers bind to. It has its own subtree of rules
|
|
13
|
+
(`main-service/index.md` and the layer files under it). Every feature with
|
|
14
|
+
state has exactly one.
|
|
15
|
+
- **`<name>-service/`** — async **capability contracts**: ports to the outside
|
|
16
|
+
world (processing, persistence, observation, generation). Reached only
|
|
17
|
+
*through* `main-service` (its service/action layers wire them in), never by the
|
|
18
|
+
UI. A feature has zero or more.
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
The rest of this rule governs the capability contracts; `main-service` follows
|
|
21
|
+
its own subtree.
|
|
22
|
+
|
|
23
|
+
## Capability contracts
|
|
24
|
+
|
|
25
|
+
Each is a namespace folder (`namespace.md`); the export and folder both carry the
|
|
26
|
+
`-service` suffix. A service is the boundary between pure feature code and the
|
|
27
|
+
outside world, which is why its members are async — enabling cross-process
|
|
28
|
+
portability and lazy loading (`AsyncDataService.createLazy`).
|
|
29
|
+
|
|
30
|
+
- The contract is an `interface` — the only place `interface` is used in the
|
|
31
|
+
codebase — validated immediately with
|
|
19
32
|
`Assert<AsyncDataService.IsValid<typeof MyService>>`.
|
|
20
33
|
- Members are async only: `void | Promise<T> | AsyncGenerator<T> | Observe<T>`.
|
|
21
34
|
- Provide `create*` factories.
|
|
22
35
|
|
|
23
36
|
## Where the I/O types live
|
|
24
37
|
|
|
25
|
-
Most input/output types belong to a single service — declare them **on
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
Most input/output types belong to a single service — declare them **on that
|
|
39
|
+
service's namespace** (`MyService.SomeInput`) and expose them only when something
|
|
40
|
+
external actually references them; not everything does.
|
|
28
41
|
|
|
29
|
-
A **service type** may be non-serializable (callbacks, function
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
A **service type** may be non-serializable (callbacks, function signatures) —
|
|
43
|
+
that's what distinguishes it from a `data/` type. If an I/O value is a plain
|
|
44
|
+
serializable value, prefer a `data/` type instead.
|
|
32
45
|
|
|
33
|
-
A non-service type sits *directly* in `services/` **only** when it is
|
|
34
|
-
|
|
46
|
+
A non-service type sits *directly* in `services/` **only** when it is genuinely
|
|
47
|
+
shared across more than one service — rare, but it happens.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/actions/**/*.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# services/main-service/actions/ — async orchestration
|
|
7
7
|
|
|
8
8
|
One action per file: a function taking the **whole database** as its first
|
|
9
9
|
argument and pure `data/` args. Actions orchestrate anything *outside* a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/core-database/components.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# core-database/components.ts — the feature's components
|
|
@@ -28,7 +28,7 @@ export const components = Database.components({
|
|
|
28
28
|
- A component name may differ from the type it stores (`position` of a `Vec3`,
|
|
29
29
|
`mark` of a `PlayerMark`) — the field name describes the role, the schema the
|
|
30
30
|
shape.
|
|
31
|
-
- **Scope = which group it sits in** (see `
|
|
31
|
+
- **Scope = which group it sits in** (see `services/main-service/index.md` for the four scopes and
|
|
32
32
|
their flags). `document` is the common case; hover a scope key for its
|
|
33
33
|
meaning. Never write `nonShared` / `nonPersistent` by hand — the group applies
|
|
34
34
|
them.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/conformance/**/*.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# services/main-service/conformance/ — spec↔implementation projection (test-only)
|
|
7
7
|
|
|
8
8
|
The bridge that keeps the ecs implementation honest against the `data/` spec.
|
|
9
9
|
**Test-only**: imported solely by `*.test.ts` and in no facet barrel, so it never
|
|
@@ -19,7 +19,7 @@ The property, per `{ before, args, after }` case:
|
|
|
19
19
|
|
|
20
20
|
Conformance test-support splits by concern along the layer boundary:
|
|
21
21
|
|
|
22
|
-
- **Store-side, here in `
|
|
22
|
+
- **Store-side, here in `services/main-service/conformance/`:**
|
|
23
23
|
- `from-state.ts` → `fromState(store: CoreDatabase.Store, state: State): void`
|
|
24
24
|
seeds the store (clear tail→head → insert entities → set resources) to a `State`.
|
|
25
25
|
- `to-state.ts` → `toState(store): State` reads the store back — each kind via
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/*.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# services/main-service/ — the feature service (ECS implementation)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
`main-service/` is the feature's single entrypoint service (see `../index.md`),
|
|
9
|
+
implemented as a reactive Entity-Component-System — an implementation detail
|
|
10
|
+
behind the service. Everything ECS-specific lives here: the schema that stores `data/` types as
|
|
9
11
|
entity state (`core-database/`), the derived and mutating logic over it
|
|
10
12
|
(`indexes/`, `transactions/`, `computed/`), the database-bound service factories
|
|
11
13
|
(`services/`), the async orchestrators (`actions/`), and the layered plugins
|
|
@@ -23,7 +25,7 @@ one `core-database/` folder: `core-database.ts` plus one file per facet the
|
|
|
23
25
|
feature uses (all three facets are optional on `Database.Plugin.create`):
|
|
24
26
|
|
|
25
27
|
```
|
|
26
|
-
|
|
28
|
+
core-database/
|
|
27
29
|
core-database.ts # Database.Plugin.create({ components?, resources?, archetypes? }) → CoreDatabase
|
|
28
30
|
components.ts # export const components = Database.components({ … })
|
|
29
31
|
resources.ts # export const resources = Database.resources({ … })
|
|
@@ -97,7 +99,7 @@ facet folder it adds. **Name each layer for the facet it adds — never for the
|
|
|
97
99
|
feature or app.** A feature creates only the layers it uses:
|
|
98
100
|
|
|
99
101
|
```
|
|
100
|
-
|
|
102
|
+
main-service/
|
|
101
103
|
core-database/ # the schema (above)
|
|
102
104
|
index-database/ # extends core; indexes/
|
|
103
105
|
transaction-database/ # extends index; transactions/
|
|
@@ -107,7 +109,7 @@ ecs/
|
|
|
107
109
|
```
|
|
108
110
|
|
|
109
111
|
Each exports a namespace mirroring the plugin — the composed object is a
|
|
110
|
-
`Database` (the `@adobe/data` term) even though the folder is `
|
|
112
|
+
`Database` (the `@adobe/data` term) even though the folder is `services/main-service/`:
|
|
111
113
|
|
|
112
114
|
```ts
|
|
113
115
|
export type IndexDatabase = Database.Plugin.ToDatabase<typeof indexDatabasePlugin>;
|
|
@@ -120,12 +122,12 @@ export namespace IndexDatabase {
|
|
|
120
122
|
### The assembled database — `FeatureDatabase`
|
|
121
123
|
|
|
122
124
|
*Which* layer is topmost varies with the facets a feature uses (computed /
|
|
123
|
-
service / action / system). Consumers — `ui/`, the app entry, `
|
|
125
|
+
service / action / system). Consumers — `ui/`, the app entry, `services/main-service/conformance/`
|
|
124
126
|
— need the *whole* feature database but must not name that layer, or adding or
|
|
125
127
|
dropping a layer rewrites them all. Alias it once:
|
|
126
128
|
|
|
127
129
|
```ts
|
|
128
|
-
//
|
|
130
|
+
// services/main-service/feature-database.ts
|
|
129
131
|
export { SystemDatabase as FeatureDatabase } from "./system-database/system-database.js";
|
|
130
132
|
```
|
|
131
133
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/core-database/resources.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# core-database/resources.ts — the feature's resources
|
|
@@ -21,7 +21,7 @@ export const resources = Database.resources({
|
|
|
21
21
|
|
|
22
22
|
- Give each resource a `default` — spread the data-type's schema and add one, or
|
|
23
23
|
bind a `data/` / primitive schema that already carries a sensible default.
|
|
24
|
-
- **Scope = which group it sits in** (see `
|
|
24
|
+
- **Scope = which group it sits in** (see `services/main-service/index.md`). A per-device
|
|
25
25
|
preference is `settings` (durable, `nonShared`); a synced-but-unsaved value is
|
|
26
26
|
`presence`; a saved shared value is `document`.
|
|
27
27
|
- Because there is only ever one instance, linear-memory packing buys nothing —
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/systems/**/*.ts'
|
|
4
4
|
- '**/systems/*.ts'
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -119,7 +119,7 @@ with no rAF and no rendering attached.
|
|
|
119
119
|
|
|
120
120
|
## Layer
|
|
121
121
|
|
|
122
|
-
`system-database.ts` extends the previous
|
|
122
|
+
`system-database.ts` extends the previous main-service layer (usually `ComputedDatabase`),
|
|
123
123
|
combined with `scheduler`, and declares the `systems` map **inline** (see above —
|
|
124
124
|
this is the one facet not split one-per-file, because `create`'s `db` is only typed
|
|
125
125
|
inline and standalone declarations break name inference). A `systems/` folder is
|
|
@@ -127,7 +127,7 @@ optional: it holds extracted per-frame body helpers (`(db) => () => void`) only
|
|
|
127
127
|
an inline body grows too large — the schedule and registration still live inline in
|
|
128
128
|
`system-database.ts`. The headless conformance test drives one frame via
|
|
129
129
|
`db.system.order` / `db.system.functions` and compares to the `data/` `step` oracle,
|
|
130
|
-
**reusing the feature projection** (`
|
|
130
|
+
**reusing the feature projection** (`services/main-service/conformance.md`); it lives at
|
|
131
131
|
`system-database/tick-loop.test.ts`. Test **selection/detection** logic (which
|
|
132
132
|
entities collide, which pair resolves) separately, with seeded edge-case geometries
|
|
133
133
|
— it's a system concern, not a transaction, and where subtle bugs hide.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- '**/features/*/
|
|
3
|
+
- '**/features/*/services/main-service/**/transactions/**/*.ts'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# database/transactions/ — atomic mutations
|
|
@@ -32,7 +32,7 @@ export const playMove = (t: CoreDatabase.Store, { index }: PlayMoveArgs) => {
|
|
|
32
32
|
result — read the touched slice, call the `data/` transform, write the diff.
|
|
33
33
|
- **Conform every transaction to its `data/` transform** in its sibling
|
|
34
34
|
`*.test.ts`, via `expectConforms` over the spec's shared cases (see
|
|
35
|
-
`
|
|
35
|
+
`services/main-service/conformance.md`): seed `fromState(before)`, dispatch, assert `toState ≡
|
|
36
36
|
after`, covering every branch and edge case. A transaction taking **entity
|
|
37
37
|
ids** resolves them from the seeded store in the `apply` closure; one with no
|
|
38
38
|
`data/` analogue (`setBounds`, `setInput`) gets a direct resource assertion.
|
|
@@ -5,7 +5,7 @@ paths:
|
|
|
5
5
|
|
|
6
6
|
# ui/ — user interface
|
|
7
7
|
|
|
8
|
-
The top layer: UI that binds the `
|
|
8
|
+
The top layer: UI that binds the `services/main-service/` database to what the user sees. It
|
|
9
9
|
owns no business logic — it subscribes to state, hands it to a pure
|
|
10
10
|
presentation, and wires actions back to transactions.
|
|
11
11
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/data-ai",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.90",
|
|
4
4
|
"description": "Cross-agent architecture skills for @adobe/data — installable as a Claude Code plugin or copied into any Agent-Skills-compatible agent (Cursor, Codex).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -29,18 +29,17 @@
|
|
|
29
29
|
"skills"
|
|
30
30
|
],
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@adobe/data": "0.9.
|
|
32
|
+
"@adobe/data": "0.9.90"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"typescript": "^5.8.3",
|
|
36
36
|
"vitest": "^1.6.0",
|
|
37
|
-
"@adobe/data": "0.9.
|
|
37
|
+
"@adobe/data": "0.9.90"
|
|
38
38
|
},
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "cp ../../LICENSE .",
|
|
42
42
|
"typecheck": "true",
|
|
43
|
-
"test": "vitest --run --passWithNoTests"
|
|
44
|
-
"publish-public": "pnpm build && pnpm publish --no-git-checks --access public"
|
|
43
|
+
"test": "vitest --run --passWithNoTests"
|
|
45
44
|
}
|
|
46
45
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-actions
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's action-database — async orchestration over services and transactions. If the feature has async flows or side effects outside of the main store.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Skip if this feature doesn't contain or require actions.
|
|
9
9
|
|
|
10
|
-
Create `
|
|
10
|
+
Create `services/main-service/action-database/`: `action-database.ts` (extends `ServiceDatabase`, adds `actions`
|
|
11
11
|
from `./actions/index.js`) plus an `actions/` folder — one async orchestrator per file. An action
|
|
12
12
|
takes `db: ServiceDatabase`, awaits `services/` ports, then commits **exactly one** transaction.
|
|
13
13
|
|
|
14
|
-
The top
|
|
14
|
+
The top main-service layer; comes after `service-database`.
|
|
15
15
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
16
|
+
The how is in the auto-loading `features/services/main-service/actions.md` rule.
|
|
@@ -10,6 +10,6 @@ output: app
|
|
|
10
10
|
- The base reaches a peer only through a lazy element wrapper (dynamic import), so peers
|
|
11
11
|
load on demand.
|
|
12
12
|
- The entry point creates the live database from `FeatureDatabase.plugin` (the base
|
|
13
|
-
feature's assembled database — see `features/
|
|
13
|
+
feature's assembled database — see `features/services/main-service/index.md`) and mounts the root UI.
|
|
14
14
|
|
|
15
15
|
See `features/index.md` (one-app-many-features) for the wiring rules.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-computed
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's computed-database — derived observable values.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Skip if this feature doesn't contain or require computed values.
|
|
9
9
|
|
|
10
|
-
Create `
|
|
10
|
+
Create `services/main-service/computed-database/`: `computed-database.ts` (extends the previous layer, adds
|
|
11
11
|
`computed` from `./computed/index.js`) plus a `computed/` folder — one `cached` derived value
|
|
12
12
|
per file + barrel. Derivation logic lives in `data/`; a computed only wires a db observable to it.
|
|
13
13
|
|
|
14
14
|
Comes after `transaction-database`.
|
|
15
15
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
16
|
+
The how is in the auto-loading `features/services/main-service/computed.md` rule.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-core-database
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's schema — the core-database (components, resources, archetypes). The first main-service layer.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Create `
|
|
8
|
+
Create `services/main-service/core-database/` — `core-database.ts` plus one single-export file per facet
|
|
9
9
|
the feature actually uses (`components`, `resources`, `archetypes` are each optional on
|
|
10
10
|
`Database.Plugin.create`). Create only the facet files you need; omit the rest rather than
|
|
11
11
|
shipping empty `Database.components({})` boilerplate. A feature with **no entities** (a
|
|
@@ -20,5 +20,5 @@ The schema root; comes after `data/` (and `services/`). Bind each schema from it
|
|
|
20
20
|
type — don't author shapes here. Components use JSON schemas; resources use the
|
|
21
21
|
`{ default }` convention (a schemaless aggregate binds via `{ default: State.create() }`).
|
|
22
22
|
|
|
23
|
-
The how is in the auto-loading `features/
|
|
23
|
+
The how is in the auto-loading `features/services/main-service/index.md` + `components.md` / `resources.md` /
|
|
24
24
|
`archetypes.md` rules.
|
|
@@ -16,6 +16,6 @@ transactions = moves/spawns, computed = score/status/winner, ui = view + input.
|
|
|
16
16
|
- **Real-time** adds the `build-systems` phase: a per-frame tick loop (movement,
|
|
17
17
|
collision, lifetime) driven by the scheduler, over many entities in distinct
|
|
18
18
|
archetypes. Systems must stay O(1) per touched entity — never project the whole
|
|
19
|
-
`State` into/out of the store each frame (see `features/
|
|
19
|
+
`State` into/out of the store each frame (see `features/services/main-service/systems.md`).
|
|
20
20
|
This is where components, archetypes, indexes, and systems all carry real
|
|
21
21
|
weight.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-indexes
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's index-database — component/archetype indexes for O(1) lookup. As needed.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Skip if this feature doesn't contain or require indexes.
|
|
9
9
|
|
|
10
|
-
Create `
|
|
10
|
+
Create `services/main-service/index-database/`: `index-database.ts` (extends `CoreDatabase`, adds the `indexes`
|
|
11
11
|
facet from `./indexes/index.js`) plus an `indexes/` folder — one index per file + an `index.ts`
|
|
12
12
|
barrel.
|
|
13
13
|
|
|
14
14
|
Only if the feature needs indexed lookups. Comes after `core-database`.
|
|
15
15
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
16
|
+
The how is in the auto-loading `features/services/main-service/indexes.md` rule.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-service-database
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's service-database — db-bound service factories. If the feature exposes services.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Create `
|
|
8
|
+
Create `services/main-service/service-database/`: `service-database.ts` (extends the previous layer, registers the
|
|
9
9
|
`services` facet). Two forms:
|
|
10
10
|
|
|
11
11
|
- db-bound factories `create<Name>Service(db, …)` in `service-database/services/` (one per file + barrel), for services that read `db.observe.*` / call `db.transactions.*`; or
|
|
@@ -13,4 +13,4 @@ Create `ecs/service-database/`: `service-database.ts` (extends the previous laye
|
|
|
13
13
|
|
|
14
14
|
Comes after `computed-database`.
|
|
15
15
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
16
|
+
The how is in the auto-loading `features/services/main-service/services.md` rule.
|
|
@@ -11,6 +11,6 @@ Create the feature's `services/` layer: one `<name>-service/` namespace folder p
|
|
|
11
11
|
contract — an `interface` (validated with `AsyncDataService.IsValid`), async-only members,
|
|
12
12
|
and `create*` factories.
|
|
13
13
|
|
|
14
|
-
Only if the feature talks to the outside world. Comes after `data/`, before `
|
|
14
|
+
Only if the feature talks to the outside world. Comes after `data/`, before `services/main-service/`.
|
|
15
15
|
|
|
16
16
|
The how is in the auto-loading `features/services/index.md` rule (and `namespace.md`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-systems
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's system-database — the real-time tick loop (systems the scheduler runs each frame). For real-time features only.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
@@ -8,9 +8,9 @@ output: feature
|
|
|
8
8
|
Skip if this feature doesn't contain or require systems.
|
|
9
9
|
Most games that use a canvas will use systems but most applications will not.
|
|
10
10
|
|
|
11
|
-
Create `
|
|
11
|
+
Create `services/main-service/system-database/system-database.ts`: `Database.Plugin.create({ extends:
|
|
12
12
|
Database.Plugin.combine(ComputedDatabase.plugin, scheduler), systems })` where the `systems`
|
|
13
|
-
map is declared **inline** (see `features/
|
|
13
|
+
map is declared **inline** (see `features/services/main-service/systems.md` — inline is required for `db` to be
|
|
14
14
|
typed and for system-name inference; a `systems/` folder is optional, only for extracted
|
|
15
15
|
per-frame body helpers).
|
|
16
16
|
|
|
@@ -21,4 +21,4 @@ per-frame body helpers).
|
|
|
21
21
|
combining `scheduler` (rAF), gated by the `schedulerState` resource.
|
|
22
22
|
|
|
23
23
|
**Only for real-time features** (games, sims) — turn-based features skip this phase entirely.
|
|
24
|
-
Comes after `build-computed`. The how is in the auto-loading `features/
|
|
24
|
+
Comes after `build-computed`. The how is in the auto-loading `features/services/main-service/systems.md` rule.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-transactions
|
|
3
|
-
description: Build a feature's
|
|
3
|
+
description: Build a feature's transaction-database — atomic mutations over the store.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Create `
|
|
8
|
+
Create `services/main-service/transaction-database/`: `transaction-database.ts` (extends `IndexDatabase`, adds
|
|
9
9
|
`transactions` from `./transactions/index.js`) plus a `transactions/` folder — one mutation per
|
|
10
10
|
file + barrel. Type the store param `CoreDatabase.Store` (entities/resources/archetypes) or
|
|
11
11
|
`IndexDatabase.Store` (reads an index).
|
|
12
12
|
|
|
13
13
|
Comes after `index-database` (or `core-database` if no indexes).
|
|
14
14
|
|
|
15
|
-
The how is in the auto-loading `features/
|
|
15
|
+
The how is in the auto-loading `features/services/main-service/transactions.md` rule.
|
package/skills/build-ui/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-ui
|
|
3
|
-
description: Build a feature's ui/ layer — presentation bound to the
|
|
3
|
+
description: Build a feature's ui/ layer — presentation bound to the main-service database. The final, top layer.
|
|
4
4
|
input: feature
|
|
5
5
|
output: feature
|
|
6
6
|
---
|