@adobe/data-ai 0.9.90 → 0.9.92
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 -6
- package/.claude/rules/data-modelling.md +4 -3
- package/.claude/rules/ecs.md +1 -1
- package/.claude/rules/element.md +1 -1
- package/.claude/rules/features/data/index.md +49 -10
- package/.claude/rules/features/data/state.md +10 -7
- package/.claude/rules/features/index.md +5 -4
- package/.claude/rules/features/services/index.md +1 -1
- package/.claude/rules/features/services/main-service/actions.md +5 -5
- package/.claude/rules/features/services/main-service/computed.md +3 -3
- package/.claude/rules/features/services/main-service/conformance.md +4 -3
- package/.claude/rules/features/services/main-service/index.md +14 -10
- package/.claude/rules/features/services/main-service/services.md +8 -7
- package/.claude/rules/features/services/main-service/systems.md +6 -2
- package/.claude/rules/features/services/main-service/transactions.md +1 -1
- package/.claude/rules/features/ui/binding-element.md +6 -6
- package/.claude/rules/{namespace.md → global/namespace.md} +38 -10
- package/.claude/rules/{react.md → global/react.md} +1 -1
- package/.claude/rules/lit.md +3 -3
- package/.claude/rules/observe.md +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/bin/cli.mjs +51 -3
- package/package.json +3 -3
- package/skills/build-app-entry/SKILL.md +1 -1
- package/skills/build-data/SKILL.md +3 -3
- package/skills/build-services/SKILL.md +1 -1
- package/skills/build-systems/SKILL.md +4 -1
- package/skills/build-transactions/SKILL.md +2 -1
- package/skills/meta-build/SKILL.md +1 -0
- package/skills/review/SKILL.md +1 -0
- /package/.claude/rules/{cohesion.md → global/cohesion.md} +0 -0
- /package/.claude/rules/{function-references.md → global/function-references.md} +0 -0
- /package/.claude/rules/{type-casts.md → global/type-casts.md} +0 -0
package/.claude/rules/README.md
CHANGED
|
@@ -13,6 +13,8 @@ folder's own `index.md` holds just enough to understand that folder; each
|
|
|
13
13
|
child gets its own file (recursing wherever the source tree does):
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
+
global/ # always-on conventions — deliberately repo-wide globs
|
|
17
|
+
namespace.md cohesion.md type-casts.md function-references.md react.md
|
|
16
18
|
features/
|
|
17
19
|
index.md # the feature layering as a whole (ui → services → data)
|
|
18
20
|
data/
|
|
@@ -28,12 +30,15 @@ features/
|
|
|
28
30
|
ui/index.md # UI (points to element / presentation / lazy-element file rules)
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
The `global/` rules (`namespace.md`, `cohesion.md`, `type-casts.md`,
|
|
34
|
+
`function-references.md`, `react.md`) are **intentionally repo-wide** — their
|
|
35
|
+
`paths:` globs (`**/*.ts` etc.) apply everywhere, by design, so the folder name
|
|
36
|
+
makes that explicit. Alongside `features/`, the remaining rules-root `.md` files
|
|
37
|
+
hold the other **cross-cutting** patterns the feature rules reference —
|
|
38
|
+
`data-modelling.md`, `archetypes.md` (row iteration), `plugin-modelling.md`, and
|
|
39
|
+
the UI file rules (`element.md`, `lazy-element.md`, `presentation.md`). They live
|
|
40
|
+
here so the bundle is self-contained; this repo symlinks them into its own
|
|
41
|
+
`.claude/rules/` (as it does `features/`).
|
|
37
42
|
|
|
38
43
|
Each rule's `paths:` glob is scoped to `**/features/*/<layer>/…`, so these
|
|
39
44
|
rules apply only once an application opts into the feature-folder pattern
|
|
@@ -15,9 +15,10 @@ discriminated unions.
|
|
|
15
15
|
## Example
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
// types/http-method/
|
|
19
|
-
export
|
|
20
|
-
|
|
18
|
+
// types/http-method/http-method.ts — the hand-authored type owns the member identity
|
|
19
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
20
|
+
// (a schema.ts, if/when a runtime boundary needs one, is written to match this
|
|
21
|
+
// type and pinned to it — see global/namespace.md and features/data/index.md)
|
|
21
22
|
|
|
22
23
|
// In an unrelated request-counter plugin:
|
|
23
24
|
|
package/.claude/rules/ecs.md
CHANGED
|
@@ -143,7 +143,7 @@ Synchronous, deterministic atomic mutations. Receive `store` and a payload.
|
|
|
143
143
|
|
|
144
144
|
### actions
|
|
145
145
|
|
|
146
|
-
UI components that call actions must never consume returned values — see
|
|
146
|
+
UI components that call actions must never consume returned values — see `features/ui/binding-element.md` (Actions are fire-and-forget).
|
|
147
147
|
|
|
148
148
|
**At most one transaction per action.** Multiple transactions in a single action corrupt the undo/redo stack.
|
|
149
149
|
|
package/.claude/rules/element.md
CHANGED
|
@@ -125,7 +125,7 @@ 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
127
|
varying topmost-layer name (see `features/services/main-service/index.md`): `typeof
|
|
128
|
-
|
|
128
|
+
MainService.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
|
|
131
131
|
minimal base layer it consumes (e.g. `ComputedDatabase.plugin`) so every
|
|
@@ -11,24 +11,63 @@ communication over the wire — no functions, no handles, just plain data.
|
|
|
11
11
|
It depends on nothing but `@adobe/data` and other `data/` declarations, and
|
|
12
12
|
needs no knowledge of anything built on top of it.
|
|
13
13
|
|
|
14
|
-
Each data type is its own namespace folder (see `namespace.md`), holding
|
|
15
|
-
its
|
|
14
|
+
Each data type is its own namespace folder (see `global/namespace.md`), holding
|
|
15
|
+
its hand-authored type, its (optional, matching) schema, and its pure
|
|
16
|
+
synchronous helpers together:
|
|
16
17
|
|
|
17
18
|
```
|
|
18
19
|
data/player-mark/
|
|
19
|
-
player-mark.ts # type
|
|
20
|
-
schema.ts #
|
|
20
|
+
player-mark.ts # HAND-AUTHORED type + `export * as PlayerMark from "./public.js"`
|
|
21
|
+
schema.ts # (optional / later) schema matching the type, pinned via Assert<Equal<…>>
|
|
21
22
|
public.ts # re-exports schema + helpers
|
|
22
23
|
is.ts values.ts opponent.ts … # one pure helper per file
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
- The **type is
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
- The **type is authored by hand** in `<type>.ts` — the readable source of
|
|
27
|
+
truth. Write it with the richest types available (`F32`, `Vec3`, unions,
|
|
28
|
+
branded aliases) so members carry their semantic meaning and get strong
|
|
29
|
+
IntelliSense / hover docs.
|
|
30
|
+
- The **schema is derived from the type, not the reverse.** It is written to
|
|
31
|
+
match the type and **pinned** to it with a compile-time assertion so the two
|
|
32
|
+
cannot drift (see below). A field typed as a branded primitive reuses that
|
|
33
|
+
type's schema — `F32` → `F32.schema`, `Vec3` → `Vec3.schema`.
|
|
34
|
+
- **Schemas are optional until a runtime boundary needs one.** A pure `data/`
|
|
35
|
+
type is complete with `<type>.ts` alone. The schema is added only when
|
|
36
|
+
persistence, the wire, or an ECS component/resource requires it — and is
|
|
37
|
+
typically written by the agent in a later phase (e.g. when implementing the
|
|
38
|
+
ECS), not by the human up front.
|
|
29
39
|
- Helpers are synchronous and pure; each has a sibling `*.test.ts`.
|
|
30
|
-
- Objects
|
|
31
|
-
|
|
40
|
+
- Objects of only 32-bit numeric values / sub-structs: the schema uses
|
|
41
|
+
`Schema.fromStructProperties` (guarantees a valid struct schema, stored in
|
|
42
|
+
linear memory).
|
|
43
|
+
|
|
44
|
+
## Pinning the schema to the type
|
|
45
|
+
|
|
46
|
+
The type is written first and by hand; the schema is written to match it and
|
|
47
|
+
is locked to it at compile time, so schema authoring (whenever it happens)
|
|
48
|
+
cannot silently drift from the type readers rely on.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// player-mark/player-mark.ts — HAND-AUTHORED, the source of truth
|
|
52
|
+
export type PlayerMark = 'X' | 'O';
|
|
53
|
+
export * as PlayerMark from './public.js';
|
|
54
|
+
|
|
55
|
+
// player-mark/schema.ts — written to MATCH the type (often later, by the agent)
|
|
56
|
+
import { Schema } from '@adobe/data/schema';
|
|
57
|
+
import type { Assert, Equal } from '@adobe/data/types';
|
|
58
|
+
import type { PlayerMark } from './player-mark.js';
|
|
59
|
+
|
|
60
|
+
export const schema = { type: 'string', enum: ['X', 'O'] } as const satisfies Schema;
|
|
61
|
+
|
|
62
|
+
// Compile-time pin: fails to build if schema and type diverge.
|
|
63
|
+
type _Pin = Assert<Equal<Schema.ToType<typeof schema>, PlayerMark>>;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `Assert<Equal<…>>` line is the whole safety net: `Schema.ToType<typeof
|
|
67
|
+
schema>` must be exactly the hand-authored type (including readonly/optional),
|
|
68
|
+
or the file does not compile. `Schema.ToType` is used **only** inside the
|
|
69
|
+
assertion — never as the exported type. Consumers always import the
|
|
70
|
+
hand-authored `PlayerMark`.
|
|
32
71
|
|
|
33
72
|
One folder is special: **`data/state/`** holds the feature's single `State`
|
|
34
73
|
aggregate and its pure transforms/derivations (its own rule, `state.md`);
|
|
@@ -41,13 +41,16 @@ export * as State from "./public.js";
|
|
|
41
41
|
- Guard and **return `state` unchanged** on a no-op / illegal input rather
|
|
42
42
|
than throwing — this keeps transforms idempotent under repeated application.
|
|
43
43
|
- Each transform has a sibling `*.test.ts`; performance is irrelevant here,
|
|
44
|
-
correctness is everything.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
correctness is everything. The test file **exports** its cases —
|
|
45
|
+
`export const cases: ConformanceCase<Args>[]` (the shared `conformance-case.ts`
|
|
46
|
+
type) — right alongside the `describe`/`it` that exercise them. This is
|
|
47
|
+
spec-owned truth the matching main-service conformance test imports **from the
|
|
48
|
+
`<transform>.test.js` file** unchanged (see `services/main-service/conformance.md`).
|
|
49
|
+
Keeping the cases in the test file rather than a separate `<transform>.cases.ts`
|
|
50
|
+
removes a file per transform — less folder clutter, same reuse. Author
|
|
51
|
+
`before`/`after` as full `State` (`{ ...State.create(), …overrides }`); the
|
|
52
|
+
generic-slice signature lets them flow through. Tolerant full-`State` equality is
|
|
53
|
+
the shared `expect-state-matches.ts`.
|
|
51
54
|
|
|
52
55
|
## Derivations — `(state) => value`
|
|
53
56
|
|
|
@@ -88,7 +88,7 @@ main-service mutation, seeded and read back through a test-only store↔`State`
|
|
|
88
88
|
projection, equals the pure `data/` transform it stands for. The projection and
|
|
89
89
|
its runner live in `services/main-service/conformance/` (see
|
|
90
90
|
`services/main-service/conformance.md`); the shared `{ before, args, after }`
|
|
91
|
-
cases are spec-owned (`data/state/<transform>.
|
|
91
|
+
cases are spec-owned (exported from `data/state/<transform>.test.ts`), so conforming the
|
|
92
92
|
implementation is "substitute the implementation, reuse the expectations." This
|
|
93
93
|
lets `main-service` be largely mechanical and agent-generated, with the spec as
|
|
94
94
|
oracle. *How* to author each layer lives in the per-folder rules below.
|
|
@@ -106,6 +106,7 @@ current examples of this structure. `data-lit-todo` is the most complete
|
|
|
106
106
|
See the rules under each folder: `data/`, `services/` — the `main-service/`
|
|
107
107
|
subtree (components, resources, archetypes, computed, indexes, transactions,
|
|
108
108
|
systems, conformance) and capability-contract services — and `ui/`.
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
`
|
|
109
|
+
Always-on conventions live in `global/` (`namespace.md`, `cohesion.md`,
|
|
110
|
+
`type-casts.md`, `function-references.md`, `react.md` — deliberately repo-wide);
|
|
111
|
+
other cross-cutting patterns at the rules root — `data-modelling.md`,
|
|
112
|
+
`archetypes.md` (row iteration).
|
|
@@ -22,7 +22,7 @@ its own subtree.
|
|
|
22
22
|
|
|
23
23
|
## Capability contracts
|
|
24
24
|
|
|
25
|
-
Each is a namespace folder (`namespace.md`); the export and folder both carry the
|
|
25
|
+
Each is a namespace folder (`global/namespace.md`); the export and folder both carry the
|
|
26
26
|
`-service` suffix. A service is the boundary between pure feature code and the
|
|
27
27
|
outside world, which is why its members are async — enabling cross-process
|
|
28
28
|
portability and lazy loading (`AsyncDataService.createLazy`).
|
|
@@ -13,19 +13,19 @@ timing — and then commit the result through a transaction.
|
|
|
13
13
|
```ts
|
|
14
14
|
import type { ServiceDatabase } from "../../service-database/service-database.js";
|
|
15
15
|
|
|
16
|
-
export const addRandomTodo = async (
|
|
17
|
-
const name = await
|
|
18
|
-
|
|
16
|
+
export const addRandomTodo = async (service: ServiceDatabase) => {
|
|
17
|
+
const name = await service.services.nameGenerator.generateName(); // await a services/ port
|
|
18
|
+
service.transactions.createTodo({ name }); // then exactly one commit
|
|
19
19
|
};
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
- Type the `
|
|
22
|
+
- Type the `service` parameter on the lowest database layer exposing what the
|
|
23
23
|
action touches — usually `ServiceDatabase` (services **and**
|
|
24
24
|
transactions). Never the action layer itself (that would be a cycle).
|
|
25
25
|
- **Call at most one transaction** per action, so undo/redo stays one step
|
|
26
26
|
per operation.
|
|
27
27
|
- **Fire-and-forget.** An action's return value is not consumed; results flow
|
|
28
|
-
back through observables. `
|
|
28
|
+
back through observables. `service.services.*` calls are `void` or
|
|
29
29
|
awaited-internally, never surfaced to the caller.
|
|
30
30
|
- Do the outside-world work here: await/sequence `services/` calls, and if a
|
|
31
31
|
slow call needs timing, compute it here around the call.
|
|
@@ -7,7 +7,7 @@ paths:
|
|
|
7
7
|
|
|
8
8
|
One derived value per file: a `cached` function of a database layer that
|
|
9
9
|
returns an `Observe` of state projected through pure `data/` helpers.
|
|
10
|
-
Derivation logic itself lives in `data/`; a computed only wires a
|
|
10
|
+
Derivation logic itself lives in `data/`; a computed only wires a service
|
|
11
11
|
observable to it.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
@@ -16,8 +16,8 @@ import { Observe } from "@adobe/data/observe";
|
|
|
16
16
|
import { BoardState } from "../../../data/board-state/board-state.js";
|
|
17
17
|
import type { IndexDatabase } from "../../index-database/index-database.js";
|
|
18
18
|
|
|
19
|
-
export const status = cached((
|
|
20
|
-
Observe.withFilter(
|
|
19
|
+
export const status = cached((service: IndexDatabase) =>
|
|
20
|
+
Observe.withFilter(service.observe.resources.board, BoardState.deriveStatus),
|
|
21
21
|
);
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -29,8 +29,9 @@ Conformance test-support splits by concern along the layer boundary:
|
|
|
29
29
|
`fromState(before)` → `apply(store, args)` → `toState ≡ after`.
|
|
30
30
|
- **Spec-side, in `data/state/`** (State values, no store — so both the `data/`
|
|
31
31
|
transform tests and this runner import them without a layer violation):
|
|
32
|
-
`conformance-case.ts` (`ConformanceCase<Args>`), the
|
|
33
|
-
cases
|
|
32
|
+
`conformance-case.ts` (`ConformanceCase<Args>`), the cases **exported from each
|
|
33
|
+
`<transform>.test.ts`** (`import { cases } from "…/<transform>.test.js"` — no
|
|
34
|
+
separate `.cases.ts` file), and `expect-state-matches.ts` (State equality).
|
|
34
35
|
|
|
35
36
|
## No cast — build on `Store.create`, not a `Database`
|
|
36
37
|
|
|
@@ -39,7 +40,7 @@ A **transaction is `(store, args) => void`**, so transaction conformance needs n
|
|
|
39
40
|
`CoreDatabase.Store` — pass the plugin directly, `Store.create` reads its schema
|
|
40
41
|
facets. Source it from the **lowest layer that declares all the schema** —
|
|
41
42
|
`IndexDatabase`, or `CoreDatabase` if the feature has no indexes — **not**
|
|
42
|
-
`
|
|
43
|
+
`MainService`: the store needs only schema, and the behaviour layers
|
|
43
44
|
(transactions / computed / systems) add none.
|
|
44
45
|
`fromState`/`toState`/`apply` all operate on it, and `apply` calls the **raw
|
|
45
46
|
transaction function** directly:
|
|
@@ -119,26 +119,29 @@ export namespace IndexDatabase {
|
|
|
119
119
|
}
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
### The assembled database — `
|
|
122
|
+
### The assembled database — `MainService`
|
|
123
123
|
|
|
124
124
|
*Which* layer is topmost varies with the facets a feature uses (computed /
|
|
125
125
|
service / action / system). Consumers — `ui/`, the app entry, `services/main-service/conformance/`
|
|
126
126
|
— need the *whole* feature database but must not name that layer, or adding or
|
|
127
|
-
dropping a layer rewrites them all. Alias it once
|
|
127
|
+
dropping a layer rewrites them all. Alias it once from the **folder-eponymous
|
|
128
|
+
`main-service.ts`** (per `global/namespace.md`, a folder's primary export lives in
|
|
129
|
+
its same-named file), as a namespace so consumers reach `MainService.plugin` /
|
|
130
|
+
`MainService.Store`:
|
|
128
131
|
|
|
129
132
|
```ts
|
|
130
|
-
// services/main-service/
|
|
131
|
-
export { SystemDatabase as
|
|
133
|
+
// services/main-service/main-service.ts
|
|
134
|
+
export { SystemDatabase as MainService } from "./system-database/system-database.js";
|
|
132
135
|
```
|
|
133
136
|
|
|
134
|
-
Every consumer references **`
|
|
137
|
+
Every consumer references **`MainService`** (`.plugin`, `.Store`) — never the
|
|
135
138
|
topmost layer. Add or drop a layer → change only this one line.
|
|
136
139
|
|
|
137
140
|
**Cross-feature naming.** Inside a feature the core and assembled databases are
|
|
138
|
-
the bare `CoreDatabase` / `
|
|
141
|
+
the bare `CoreDatabase` / `MainService`. When *another* feature or package
|
|
139
142
|
imports one — a peer built on it, the base `imports` a peer's `core-database`, a
|
|
140
143
|
downstream package — reference it **feature-qualified**: `<Feature>CoreDatabase`
|
|
141
|
-
/ `<Feature>
|
|
144
|
+
/ `<Feature>MainService`, re-exported so from the feature's barrel
|
|
142
145
|
(`export { CoreDatabase as TodoCoreDatabase } from "…/core-database.js"`) — so
|
|
143
146
|
two features' `CoreDatabase`s never collide.
|
|
144
147
|
|
|
@@ -153,9 +156,10 @@ two features' `CoreDatabase`s never collide.
|
|
|
153
156
|
reads/writes entities, resources, or archetypes; **`IndexDatabase.Store`** the
|
|
154
157
|
moment it reads an index.
|
|
155
158
|
- **the whole `Database`** — **computed**, **services**, and **actions** each
|
|
156
|
-
take `
|
|
157
|
-
read/call): computed reads `
|
|
158
|
-
transactions; actions call `
|
|
159
|
+
take `service: <Layer>` (the lowest layer whose database exposes what they
|
|
160
|
+
read/call): computed reads `service.observe.*`; services read observables and
|
|
161
|
+
call transactions; actions call `service.services.*` then
|
|
162
|
+
`service.transactions.*`.
|
|
159
163
|
|
|
160
164
|
Each subfolder has its own rule. Modelling a plugin's authored vs. derived
|
|
161
165
|
surface is covered by `plugin-modelling.md`.
|
|
@@ -5,20 +5,21 @@ paths:
|
|
|
5
5
|
|
|
6
6
|
# database/services/ — database-bound service factories
|
|
7
7
|
|
|
8
|
-
One factory per file: `create<Name>Service(
|
|
8
|
+
One factory per file: `create<Name>Service(service, …)` that binds a service
|
|
9
9
|
implementation to a live database — reading its observables and invoking
|
|
10
10
|
its transactions. This is where the async `services/` contracts (or
|
|
11
11
|
framework services like `AgenticService`) are wired to ECS state.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
export const createAgentService = (
|
|
15
|
-
/* … reads
|
|
14
|
+
export const createAgentService = (service: ComputedDatabase, mark: PlayerMark): AgenticService =>
|
|
15
|
+
/* … reads service.observe.*, calls service.transactions.* … */;
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Type `
|
|
19
|
-
`service-database` itself — that would be a cycle).
|
|
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
20
|
|
|
21
21
|
An `index.ts` barrel re-exports the factories; `service-database.ts`
|
|
22
22
|
registers them under the `services` facet, each keyed by the name
|
|
23
|
-
consumers read it as (`
|
|
24
|
-
contract (not yet bound to a
|
|
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.
|
|
@@ -32,6 +32,8 @@ import { Database, scheduler } from "@adobe/data/ecs";
|
|
|
32
32
|
import { Motion } from "../../data/motion/motion.js";
|
|
33
33
|
|
|
34
34
|
const plugin = Database.Plugin.create({
|
|
35
|
+
// combine scheduler with the CURRENT TOP layer — ComputedDatabase here, but
|
|
36
|
+
// ActionDatabase / ServiceDatabase if the feature built those (systems come last).
|
|
35
37
|
extends: Database.Plugin.combine(ComputedDatabase.plugin, scheduler),
|
|
36
38
|
systems: {
|
|
37
39
|
control: { create: (db) => () => { /* input → ship; db.transactions.fireBullet() */ } },
|
|
@@ -119,8 +121,10 @@ with no rAF and no rendering attached.
|
|
|
119
121
|
|
|
120
122
|
## Layer
|
|
121
123
|
|
|
122
|
-
`system-database.ts` extends the
|
|
123
|
-
|
|
124
|
+
`system-database.ts` extends the feature's **current top** main-service layer (`ActionDatabase` /
|
|
125
|
+
`ServiceDatabase` / `ComputedDatabase` — whichever it built; systems come last in the pipeline, so
|
|
126
|
+
they must sit atop any service/action layers, not a hardcoded `ComputedDatabase`), combined with
|
|
127
|
+
`scheduler`, and declares the `systems` map **inline** (see above —
|
|
124
128
|
this is the one facet not split one-per-file, because `create`'s `db` is only typed
|
|
125
129
|
inline and standalone declarations break name inference). A `systems/` folder is
|
|
126
130
|
optional: it holds extracted per-frame body helpers (`(db) => () => void`) only when
|
|
@@ -41,5 +41,5 @@ export const playMove = (t: CoreDatabase.Store, { index }: PlayMoveArgs) => {
|
|
|
41
41
|
transactions (`readShip`, `readBoard` — a `(t) => value` function) may live
|
|
42
42
|
beside them in `transactions/`, but keep it **out of the barrel**, or it gets
|
|
43
43
|
registered as a dispatchable transaction. (These are related helpers, so they
|
|
44
|
-
belong here, not in a separate folder — `cohesion.md`'s peer-level caution
|
|
44
|
+
belong here, not in a separate folder — `global/cohesion.md`'s peer-level caution
|
|
45
45
|
doesn't apply.)
|
|
@@ -32,15 +32,15 @@ Presentation files (`*-presentation.ts`), CSS files (`*.css.ts`), tests (`*.test
|
|
|
32
32
|
A binding element is a thin wire between a service and a presentation. The class body contains:
|
|
33
33
|
|
|
34
34
|
1. Optional static `styles` property definition
|
|
35
|
-
2. Zero or more `@property` declarations (
|
|
35
|
+
2. Zero or more `@property` declarations (allowed below).
|
|
36
36
|
3. One `render()` method whose body is: one `useObservableValues(...)` call, another lifecycle hook call (in rare cases), a null-guard, and
|
|
37
37
|
`return presentation.render({ ...values, ...callbacks })`.
|
|
38
38
|
|
|
39
39
|
No other members. No `@state`. No private handler fields. No lifecycle methods (`connectedCallback`, `updated`, `firstUpdated`,
|
|
40
40
|
`disconnectedCallback`) except on app-entrypoint classes where the lifecycle concern is app-boot shaped but cannot be expressed as a hook
|
|
41
|
-
(see
|
|
41
|
+
(see `hooks.md` for hook-first lifecycle).
|
|
42
42
|
|
|
43
|
-
## `@property`
|
|
43
|
+
## `@property` allowlist
|
|
44
44
|
|
|
45
45
|
The allowed set depends on **class role**:
|
|
46
46
|
|
|
@@ -114,7 +114,7 @@ one-liners** above.
|
|
|
114
114
|
## Lifecycle goes through hooks
|
|
115
115
|
|
|
116
116
|
Any need for mount/unmount/update behavior routes through a hook. **Reuse order, one hook per file, presentation vs binding placement, and
|
|
117
|
-
testing notes:**
|
|
117
|
+
testing notes:** `hooks.md`.
|
|
118
118
|
|
|
119
119
|
Never inline lifecycle logic in the binding. App-entrypoint classes may override lifecycle methods when the concern is app-boot shaped
|
|
120
120
|
(service wiring, ancestor lookup); even then, prefer a hook.
|
|
@@ -158,14 +158,14 @@ expose them by design; reaching for them means a missing computed.
|
|
|
158
158
|
|
|
159
159
|
A binding element imports a sibling `*-presentation.ts` file that exports exactly `render` (required) and optionally `unlocalized`. Nothing
|
|
160
160
|
else — no types, no styles, no helpers. Consumers derive the props type via `Parameters<typeof render>[0]`. Presentation stays hook-free by
|
|
161
|
-
default; element hooks live here or in shared modules per
|
|
161
|
+
default; element hooks live here or in shared modules per `hooks.md` and `features/ui/presentation.md`.
|
|
162
162
|
|
|
163
163
|
**For refactors whose stated scope does not include presentations**, the matching `*-presentation.ts` file must not change. Verify with
|
|
164
164
|
`git diff --name-only | grep presentation`. If a scope-limited refactor requires a presentation signature change, stop and reconsider the
|
|
165
165
|
binding-side helper — the scope fence exists because presentations are the public contract between binding and render output.
|
|
166
166
|
|
|
167
167
|
Callback prop names use **verb** or **verbNoun** form. Never `on*` prefix. The binding-side variable matches the presentation prop name 1:1.
|
|
168
|
-
See
|
|
168
|
+
See `features/ui/presentation.md` for full examples.
|
|
169
169
|
|
|
170
170
|
## Size budget
|
|
171
171
|
|
|
@@ -32,8 +32,10 @@ patterns (e.g., inline `export namespace` in shared files) that do not conform
|
|
|
32
32
|
- `<type-name>/<type-name>.ts` is the only public import surface
|
|
33
33
|
- Export a single type alias: `export type <type-name> = <type>` (or `export type * from "./<type-name>-types.js"` when the type lives in a
|
|
34
34
|
separate file)
|
|
35
|
-
-
|
|
36
|
-
`
|
|
35
|
+
- The type alias is **hand-authored** in `<type-name>.ts`. When a schema is needed it lives in `<type-name>-schema.ts` (or
|
|
36
|
+
`schema.ts` inside the folder), named `schema` (not `<type-name>Schema`), and is **written to match the hand-authored type and pinned
|
|
37
|
+
to it** with `Assert<Equal<Schema.ToType<typeof schema>, <TypeName>>>` — never the other way around. `Schema.ToType` appears only in
|
|
38
|
+
that assertion, never as the exported type.
|
|
37
39
|
- Export namespace: `export * as <type-name> from "./public.js"`
|
|
38
40
|
- In public.ts, re-export every public constant file
|
|
39
41
|
- Filenames map deterministically to single export:
|
|
@@ -43,6 +45,26 @@ patterns (e.g., inline `export namespace` in shared files) that do not conform
|
|
|
43
45
|
- Function files: name by purpose only (e.g., `add.ts`, `sub.ts`). **Do not prefix** with the type name — the folder provides the namespace
|
|
44
46
|
context.
|
|
45
47
|
|
|
48
|
+
## Constants file
|
|
49
|
+
|
|
50
|
+
Constants are the **one exception** to one-declaration-per-file. A namespace folder MAY have a single `constants.ts` exporting several
|
|
51
|
+
plain `const` literals (no functions, no types), re-exported through `public.ts` so callers reach them as `<TypeName>.<constant>`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// duration/constants.ts
|
|
55
|
+
export const minSeconds = 5;
|
|
56
|
+
export const maxSeconds = 300;
|
|
57
|
+
export const defaultSeconds = 30;
|
|
58
|
+
|
|
59
|
+
// duration/public.ts
|
|
60
|
+
export * from './constants.js';
|
|
61
|
+
|
|
62
|
+
// consumers: Duration.minSeconds, Duration.maxSeconds, Duration.defaultSeconds
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Function declarations remain **one per file** (each in its own purpose-named file). If a value needs logic rather than being a plain
|
|
66
|
+
literal, it is a function and does not belong in `constants.ts`.
|
|
67
|
+
|
|
46
68
|
## Anti-patterns (do not copy)
|
|
47
69
|
|
|
48
70
|
- **`export namespace <TypeName> { ... }`** — Use `export * as <TypeName> from "./public.js"` instead.
|
|
@@ -63,18 +85,24 @@ patterns (e.g., inline `export namespace` in shared files) that do not conform
|
|
|
63
85
|
|
|
64
86
|
## Schema pattern example
|
|
65
87
|
|
|
88
|
+
The type is authored first and by hand; the schema (if/when needed) is written
|
|
89
|
+
to match it and pinned so it cannot drift.
|
|
90
|
+
|
|
66
91
|
```ts
|
|
67
|
-
// src/types/player-mark/player-mark
|
|
68
|
-
export
|
|
92
|
+
// src/types/player-mark/player-mark.ts — HAND-AUTHORED, the source of truth
|
|
93
|
+
export type PlayerMark = 'X' | 'O';
|
|
94
|
+
export * as PlayerMark from './public.js';
|
|
95
|
+
|
|
96
|
+
// src/types/player-mark/player-mark-schema.ts — written to MATCH the type
|
|
97
|
+
import { Schema } from '@adobe/data/schema';
|
|
98
|
+
import type { Assert, Equal } from '@adobe/data/types';
|
|
99
|
+
import type { PlayerMark } from './player-mark.js';
|
|
100
|
+
export const schema = { type: 'string', enum: ['X', 'O'] } as const satisfies Schema;
|
|
101
|
+
// Compile-time pin: fails to build if schema and type diverge.
|
|
102
|
+
type _Pin = Assert<Equal<Schema.ToType<typeof schema>, PlayerMark>>;
|
|
69
103
|
|
|
70
104
|
// src/types/player-mark/public.ts
|
|
71
105
|
export { schema } from './player-mark-schema.js';
|
|
72
|
-
|
|
73
|
-
// src/types/player-mark/player-mark.ts
|
|
74
|
-
import { Schema } from '@adobe/data/schema';
|
|
75
|
-
import { schema } from './player-mark-schema.js';
|
|
76
|
-
export type PlayerMark = Schema.ToType<typeof schema>;
|
|
77
|
-
export * as PlayerMark from './public.js';
|
|
78
106
|
```
|
|
79
107
|
|
|
80
108
|
## Example: `Point` type refactor
|
|
@@ -76,7 +76,7 @@ use `Parameters<typeof render>[0]`.
|
|
|
76
76
|
|
|
77
77
|
## Action callbacks (not events)
|
|
78
78
|
|
|
79
|
-
Use **verb** or **verbNoun** names — never `on*` prefix. See
|
|
79
|
+
Use **verb** or **verbNoun** names — never `on*` prefix. See `features/ui/presentation.md` for full examples.
|
|
80
80
|
|
|
81
81
|
**Pass function references directly** when the signature matches; wrap in an arrow function only to supply arguments:
|
|
82
82
|
|
package/.claude/rules/lit.md
CHANGED
|
@@ -76,7 +76,7 @@ there.
|
|
|
76
76
|
|
|
77
77
|
## Action callbacks (not events)
|
|
78
78
|
|
|
79
|
-
Use **verb** or **verbNoun** names — never `on*` prefix. See
|
|
79
|
+
Use **verb** or **verbNoun** names — never `on*` prefix. See `features/ui/presentation.md` for full examples and rationale.
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
@@ -91,7 +91,7 @@ view hosts. Multiple instances of the same element need a property to identify w
|
|
|
91
91
|
|
|
92
92
|
## Hooks
|
|
93
93
|
|
|
94
|
-
Canonical rules (reuse order, one hook per file, binding vs presentation):
|
|
94
|
+
Canonical rules (reuse order, one hook per file, binding vs presentation): `hooks.md`.
|
|
95
95
|
|
|
96
96
|
Use hooks from `src/elements/hooks/` and `@adobe/data-lit` instead of `connectedCallback`/`disconnectedCallback`. Use `useEffect` for
|
|
97
97
|
setup/teardown, `useKeyboardEvent` for single key events.
|
|
@@ -107,7 +107,7 @@ When creating or modifying a Lit element:
|
|
|
107
107
|
3. Use a single `useObservableValues` in the binding element. Observe only minimal values; use `Observe.withDefault` for slow-resolving
|
|
108
108
|
values.
|
|
109
109
|
4. Pass observed values and action callbacks to presentation.
|
|
110
|
-
5. Keep presentation pure — no hooks (see
|
|
110
|
+
5. Keep presentation pure — no hooks (see `hooks.md` for exceptions).
|
|
111
111
|
6. Add `@property` only when entity binding requires it (multiple instances).
|
|
112
112
|
7. Presentation exports only `render` (and localization bundles where appropriate). Binding element sets `static styles` from `*.css.ts`.
|
|
113
113
|
8. Add `*-presentation.test.ts` for presentation when appropriate; do not unit test binding elements.
|
package/.claude/rules/observe.md
CHANGED
|
@@ -147,7 +147,7 @@ Observe.withMap(rows$, rows => rows.filter(r => r.selectedByUsers?.includes(user
|
|
|
147
147
|
`observeSelectDeep` re-emits on **any** change to **any** listed component on **any** matched entity — so list only the fields actually
|
|
148
148
|
read, and when the matched set is large pair it with a structural deduplicate downstream so identical-looking emissions don't propagate
|
|
149
149
|
(`@adobe/data/observe`'s `withDeduplicate` is reference-equality only). `observeSelectDeep` from a UI element is banned — let each element
|
|
150
|
-
observe its own entity (see
|
|
150
|
+
observe its own entity (see `ecs.md`).
|
|
151
151
|
|
|
152
152
|
---
|
|
153
153
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
AI architecture skills + rules for building applications with
|
|
4
4
|
[`@adobe/data`](https://www.npmjs.com/package/@adobe/data) — a layered,
|
|
5
|
-
spec-first feature structure (`data →
|
|
5
|
+
spec-first feature structure (`data → services → ui`) kept honest by conformance
|
|
6
6
|
tests. Versioned in lockstep with the library: `@adobe/data-ai@x.y.z` targets
|
|
7
7
|
`@adobe/data@x.y.z`.
|
|
8
8
|
|
package/bin/cli.mjs
CHANGED
|
@@ -50,8 +50,8 @@ function notice(extra) {
|
|
|
50
50
|
|
|
51
51
|
The files in this folder are installed and managed by the \`${PKG_NAME}\`
|
|
52
52
|
package. This entire directory is **deleted and rebuilt** on every
|
|
53
|
-
\`npx ${PKG_NAME} install\`, so any local edit here is silently discarded on
|
|
54
|
-
next reinstall or version upgrade.
|
|
53
|
+
\`npx ${PKG_NAME}@${VERSION} install\`, so any local edit here is silently discarded on
|
|
54
|
+
the next reinstall or version upgrade.
|
|
55
55
|
|
|
56
56
|
To change them, edit upstream in the \`${PKG_NAME}\` package and publish a new
|
|
57
57
|
version, then reinstall — do not modify these files in place.
|
|
@@ -66,10 +66,21 @@ function writeMeta(bundleDir, noticeBody, extraMeta) {
|
|
|
66
66
|
);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Skills that author/evolve the bundle itself (the self-improvement loop and its
|
|
70
|
+
// rule review). They mutate or critique the managed files, so they belong only to
|
|
71
|
+
// the source repo — never a consumer install, where those files are read-only
|
|
72
|
+
// managed copies. Both also carry `disable-model-invocation: true`.
|
|
73
|
+
const MAINTAINER_ONLY = new Set(["meta-build", "review"]);
|
|
74
|
+
|
|
69
75
|
function discoverSkills() {
|
|
70
76
|
if (!existsSync(skillsSrc)) return [];
|
|
71
77
|
return readdirSync(skillsSrc, { withFileTypes: true })
|
|
72
|
-
.filter(
|
|
78
|
+
.filter(
|
|
79
|
+
(d) =>
|
|
80
|
+
d.isDirectory() &&
|
|
81
|
+
!MAINTAINER_ONLY.has(d.name) &&
|
|
82
|
+
existsSync(join(skillsSrc, d.name, "SKILL.md")),
|
|
83
|
+
)
|
|
73
84
|
.map((d) => d.name)
|
|
74
85
|
.sort();
|
|
75
86
|
}
|
|
@@ -111,6 +122,41 @@ function installRules(base) {
|
|
|
111
122
|
return bundleDir;
|
|
112
123
|
}
|
|
113
124
|
|
|
125
|
+
// A single managed rule (namespaced by its `adobe-data-ai-` filename prefix) telling
|
|
126
|
+
// an agent to (re)install the bundle when the skills are absent — pinned to THIS
|
|
127
|
+
// reviewed version, never `@latest`, so a drifted or compromised release cannot execute
|
|
128
|
+
// during install. Rewritten with the matching version on every install. It sits beside
|
|
129
|
+
// (not inside) the wiped bundle dir, so it survives and is simply overwritten.
|
|
130
|
+
function installBootstrap(base) {
|
|
131
|
+
const file = join(base, ".claude", "rules", `${BUNDLE}-bootstrap.md`);
|
|
132
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
133
|
+
writeFileSync(
|
|
134
|
+
file,
|
|
135
|
+
`---
|
|
136
|
+
paths:
|
|
137
|
+
- '**/features/*/data/**/*.ts'
|
|
138
|
+
- '**/features/*/services/**/*.ts'
|
|
139
|
+
- '**/features/*/ui/**/*.ts'
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# ${PKG_NAME} skills — ensure installed
|
|
143
|
+
|
|
144
|
+
Before using an \`@adobe/data\` build workflow, confirm the skills are present: look for
|
|
145
|
+
a skill named \`build-feature\` (or \`build-application\`). If it is missing, install the
|
|
146
|
+
pinned, reviewed bundle, then re-check:
|
|
147
|
+
|
|
148
|
+
\`\`\`sh
|
|
149
|
+
npx ${PKG_NAME}@${VERSION} install
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
Pin the version to the reviewed bundle (never \`@latest\`) so a compromised or drifted
|
|
153
|
+
release cannot execute during install. This file is regenerated with the matching
|
|
154
|
+
version on every install. Package: <https://www.npmjs.com/package/${PKG_NAME}>
|
|
155
|
+
`,
|
|
156
|
+
);
|
|
157
|
+
return file;
|
|
158
|
+
}
|
|
159
|
+
|
|
114
160
|
function parseArgs(argv) {
|
|
115
161
|
const positional = [];
|
|
116
162
|
const flags = new Set();
|
|
@@ -176,11 +222,13 @@ function main() {
|
|
|
176
222
|
const base = flags.has("global") ? homedir() : dir ? resolve(dir) : process.cwd();
|
|
177
223
|
const skillsDir = installSkills(base, skills);
|
|
178
224
|
const rulesDir = installRules(base);
|
|
225
|
+
const bootstrapFile = installBootstrap(base);
|
|
179
226
|
const ruleCount = countRules(rulesSrc);
|
|
180
227
|
|
|
181
228
|
process.stdout.write(`Installed ${PKG_NAME} v${VERSION}\n`);
|
|
182
229
|
process.stdout.write(` ${skills.length} skills → ${skillsDir}\n`);
|
|
183
230
|
process.stdout.write(` ${ruleCount} rules → ${rulesDir}\n`);
|
|
231
|
+
process.stdout.write(` bootstrap → ${bootstrapFile}\n`);
|
|
184
232
|
}
|
|
185
233
|
|
|
186
234
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/data-ai",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.92",
|
|
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,12 +29,12 @@
|
|
|
29
29
|
"skills"
|
|
30
30
|
],
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@adobe/data": "0.9.
|
|
32
|
+
"@adobe/data": "0.9.92"
|
|
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.92"
|
|
38
38
|
},
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"scripts": {
|
|
@@ -9,7 +9,7 @@ output: app
|
|
|
9
9
|
plugin, so all schemas coexist, persist, and sync while the base type stays decoupled.
|
|
10
10
|
- The base reaches a peer only through a lazy element wrapper (dynamic import), so peers
|
|
11
11
|
load on demand.
|
|
12
|
-
- The entry point creates the live database from `
|
|
12
|
+
- The entry point creates the live database from `MainService.plugin` (the base
|
|
13
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.
|
|
@@ -8,15 +8,15 @@ output: feature
|
|
|
8
8
|
Create the feature's `data/` layer (pure spec; depends on nothing but `@adobe/data`
|
|
9
9
|
and other `data/` declarations):
|
|
10
10
|
|
|
11
|
-
- one namespace folder per data type — `<type>/{<type>.ts,
|
|
11
|
+
- one namespace folder per data type — `<type>/{<type>.ts, public.ts, <helper>.ts}` (+ `schema.ts` only when a runtime boundary needs it);
|
|
12
12
|
- `data/state/` — the `State` aggregate plus its pure transforms/derivations.
|
|
13
13
|
|
|
14
14
|
**Always create `data/state/`** — even for a shell feature whose only field is a
|
|
15
15
|
boolean or enum. A feature with ECS resources/transactions but no `State` is
|
|
16
16
|
incomplete: the transaction must wrap a pure transform, not invent the logic. In rare circumstances, there may be no real state in a feature in which case the state type = {}
|
|
17
17
|
|
|
18
|
-
The
|
|
18
|
+
The hand-authored type in `<type>.ts` is the source of truth. The schema is optional up front — add `schema.ts` only when a runtime boundary (persistence, wire, or an ECS component/resource) needs one, write it to match the type, and pin it with `type _Pin = Assert<Equal<Schema.ToType<typeof schema>, <Type>>>` so it cannot drift. `Schema.ToType` is used only in that assertion, never as the exported type.
|
|
19
19
|
Helpers are pure and unit-tested. Run this first — every other layer imports `data/`.
|
|
20
20
|
|
|
21
21
|
The how is in the auto-loading rules: `features/data/index.md`, `features/data/state.md`, and
|
|
22
|
-
`namespace.md`.
|
|
22
|
+
`global/namespace.md`.
|
|
@@ -13,4 +13,4 @@ and `create*` factories.
|
|
|
13
13
|
|
|
14
14
|
Only if the feature talks to the outside world. Comes after `data/`, before `services/main-service/`.
|
|
15
15
|
|
|
16
|
-
The how is in the auto-loading `features/services/index.md` rule (and `namespace.md`).
|
|
16
|
+
The how is in the auto-loading `features/services/index.md` rule (and `global/namespace.md`).
|
|
@@ -9,7 +9,10 @@ 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
11
|
Create `services/main-service/system-database/system-database.ts`: `Database.Plugin.create({ extends:
|
|
12
|
-
Database.Plugin.combine(
|
|
12
|
+
Database.Plugin.combine(<currentTop>.plugin, scheduler), systems })` — combine `scheduler` with the
|
|
13
|
+
feature's **current top** main-service layer (`ActionDatabase` / `ServiceDatabase` / `ComputedDatabase`,
|
|
14
|
+
whichever it built), never a hardcoded `ComputedDatabase`, so systems *and* any services/actions
|
|
15
|
+
compose into the one `MainService`. The `systems`
|
|
13
16
|
map is declared **inline** (see `features/services/main-service/systems.md` — inline is required for `db` to be
|
|
14
17
|
typed and for system-name inference; a `systems/` folder is optional, only for extracted
|
|
15
18
|
per-frame body helpers).
|
|
@@ -5,7 +5,8 @@ input: feature
|
|
|
5
5
|
output: feature
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Create `services/main-service/transaction-database/`: `transaction-database.ts` (extends
|
|
8
|
+
Create `services/main-service/transaction-database/`: `transaction-database.ts` (extends the
|
|
9
|
+
preceding layer — `IndexDatabase` if the feature built indexes, else `CoreDatabase` — adds
|
|
9
10
|
`transactions` from `./transactions/index.js`) plus a `transactions/` folder — one mutation per
|
|
10
11
|
file + barrel. Type the store param `CoreDatabase.Store` (entities/resources/archetypes) or
|
|
11
12
|
`IndexDatabase.Store` (reads an index).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: meta-build
|
|
3
|
+
disable-model-invocation: true
|
|
3
4
|
description: Iteratively build an application or game phase-by-phase in clean subagents, reviewing each phase and refining the build-* skills / rules until each is optimal, then summarizing.
|
|
4
5
|
input: a prompt describing the application or game to build
|
|
5
6
|
options:
|
package/skills/review/SKILL.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review
|
|
3
|
+
disable-model-invocation: true
|
|
3
4
|
description: Review built feature/application output against the @adobe/data feature rules; report whether it is optimal and where any fix belongs. Read-only.
|
|
4
5
|
input: a path to built output (a layer, a feature, or a whole app)
|
|
5
6
|
output: a review — OPTIMAL, or a list of issues each tagged code | skill | rule
|
|
File without changes
|
|
File without changes
|
|
File without changes
|