@adobe/data-ai 0.10.21 → 0.10.23

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 (65) 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/README.md +8 -0
  38. package/bin/cli.mjs +24 -8
  39. package/package.json +3 -3
  40. package/skills/build-actions/SKILL.md +7 -8
  41. package/skills/build-app-entry/SKILL.md +12 -8
  42. package/skills/build-computed/SKILL.md +8 -7
  43. package/skills/build-core-database/SKILL.md +18 -15
  44. package/skills/build-data/SKILL.md +14 -13
  45. package/skills/build-feature/SKILL.md +2 -1
  46. package/skills/build-game/SKILL.md +6 -4
  47. package/skills/build-indexes/SKILL.md +6 -7
  48. package/skills/build-service-database/SKILL.md +8 -8
  49. package/skills/build-services/SKILL.md +9 -7
  50. package/skills/build-spec/SKILL.md +23 -0
  51. package/skills/build-systems/SKILL.md +12 -18
  52. package/skills/build-transactions/SKILL.md +8 -8
  53. package/skills/build-ui/SKILL.md +5 -4
  54. package/skills/review/SKILL.md +3 -3
  55. package/.claude/rules/features/data/state.md +0 -209
  56. package/.claude/rules/features/services/main-service/actions.md +0 -70
  57. package/.claude/rules/features/services/main-service/archetypes.md +0 -49
  58. package/.claude/rules/features/services/main-service/components.md +0 -41
  59. package/.claude/rules/features/services/main-service/computed.md +0 -82
  60. package/.claude/rules/features/services/main-service/conformance.md +0 -95
  61. package/.claude/rules/features/services/main-service/core-database.md +0 -48
  62. package/.claude/rules/features/services/main-service/index.md +0 -187
  63. package/.claude/rules/features/services/main-service/resources.md +0 -28
  64. package/.claude/rules/features/services/main-service/services.md +0 -25
  65. package/.claude/rules/features/services/main-service/transactions.md +0 -59
@@ -1,13 +1,18 @@
1
1
  ---
2
2
  paths:
3
3
  - '**/features/*/ui/**/*.ts'
4
+ - '**/features/*/ui/**/*.tsx'
4
5
  ---
5
6
 
6
7
  # ui/ — user interface
7
8
 
8
- The top layer: UI that binds the `services/main-service/` database to what the user sees. It
9
- owns no business logic — it subscribes to state, hands it to a pure
10
- presentation, and wires actions back to transactions.
9
+ The top layer: UI that binds the feature's `MainService` database (`ecs/main-service.ts`)
10
+ to what the user sees. It owns no business logic — it subscribes to state, hands it to
11
+ a pure presentation, and wires user input to `service.actions.*`.
12
+
13
+ Each feature has one base element (Lit: `ui/<feature>-element.ts`, a
14
+ `DatabaseElement` typed on its `MainService.plugin`; React: a `use-main-service.ts`
15
+ hook) that every element of the feature builds on.
11
16
 
12
17
  The folder name is framework-neutral; its contents are framework-specific
13
18
  (Lit elements, React components, …). Each UI unit is its own folder, named
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  paths:
3
- - 'src/**/elements/**/*.ts'
4
- - 'src/**/ui/**/*.ts'
3
+ - '**/src/**/elements/**/*.ts'
4
+ - '**/src/**/ui/**/*.ts'
5
5
  ---
6
6
 
7
7
  # Lazy Wrapper
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  paths:
3
3
  - '**/*presentation.test.ts'
4
+ - '**/*presentation.test.tsx'
4
5
  ---
5
6
 
6
7
  ## Testing
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  paths:
3
3
  - '**/*presentation.ts'
4
+ - '**/*presentation.tsx'
4
5
  ---
5
6
 
6
7
  # Presentation authoring
@@ -11,12 +11,12 @@ When a value is already a function with the signature the callee wants, pass it
11
11
 
12
12
  ```ts
13
13
  // ❌ identity wrappers — the arrow adds nothing
14
- expectConforms({ spec: (s, dt) => State.resolveBulletHits(s, dt), apply: (store, a) => hitAsteroid(store, a) });
14
+ cached((db) => Observe.withFilter(db.observe.resources.board, (b) => BoardState.deriveStatus(b)));
15
15
  onClick={() => save()}
16
16
  todos.map((t) => render(t))
17
17
 
18
18
  // ✅ pass the function itself
19
- expectConforms({ spec: State.resolveBulletHits, apply: hitAsteroid });
19
+ cached((db) => Observe.withFilter(db.observe.resources.board, BoardState.deriveStatus));
20
20
  onClick={save}
21
21
  todos.map(render)
22
22
  ```
@@ -6,7 +6,8 @@ paths:
6
6
  # Type namespace pattern
7
7
 
8
8
  Single import surface per type: `<type-name>/<type-name>.ts`. Schema in `-schema.ts`, namespace re-export from `public.js`. Types live in
9
- the `types/` layer.
9
+ the `types/` layer. **In a feature**, Data types live in `features/<name>/data/values/<type>/` with the schema in `schema.ts`
10
+ (`features/data/values.md`).
10
11
 
11
12
  ## Authority
12
13
 
@@ -161,5 +162,4 @@ Example — a type merged with one value export and any number of type-only memb
161
162
  }
162
163
 
163
164
  The canonical use is a feature's `core-database.ts` / layered `*-database.ts` (a
164
- `plugin` value plus `Store` / `Index` / `Component` / `Archetype` types) — see
165
- `features/services/main-service/core-database.md`.
165
+ `plugin` value plus `Store` / `Index` types) — see `features/ecs/core.md`.
@@ -5,7 +5,7 @@ paths:
5
5
 
6
6
  # React component authoring
7
7
 
8
- React components live in the `components/` layer. Consume Observe and void actions from plugins. Use `@adobe/data-react` for `useDatabase`,
8
+ React components live in the `ui/` layer (`features/ui/`). Consume Observe and void actions from plugins. Use `@adobe/data-react` for `useDatabase`,
9
9
  `useObservableValues`, and `DatabaseProvider`.
10
10
 
11
11
  ---
@@ -61,7 +61,7 @@ function Counter() {
61
61
  count: db.observe.resources.count,
62
62
  }));
63
63
  if (!values) return null;
64
- return presentation.render({ ...values, increment: db.transactions.increment });
64
+ return presentation.render({ ...values, increment: db.actions.increment });
65
65
  }
66
66
  ```
67
67
 
@@ -81,8 +81,8 @@ Use **verb** or **verbNoun** names — never `on*` prefix. See `features/ui/pres
81
81
  **Pass function references directly** when the signature matches; wrap in an arrow function only to supply arguments:
82
82
 
83
83
  ```tsx
84
- increment: db.transactions.increment; // reference — no wrapper needed
85
- toggleSprite: () => db.transactions.toggleSpriteActive({ entity }); // args required — wrap
84
+ increment: db.actions.increment; // reference — no wrapper needed
85
+ toggleSprite: () => db.actions.toggleSpriteActive({ entity }); // args required — wrap
86
86
  ```
87
87
 
88
88
  ---
@@ -7,7 +7,7 @@ paths:
7
7
 
8
8
  # Lit element authoring
9
9
 
10
- Lit elements live in the `components/` layer per the structure rule. Consume Observe and void actions from plugins per the service rule.
10
+ Lit elements live in the `ui/` layer (`features/ui/`). Consume Observe and void actions from plugins per the service rule.
11
11
 
12
12
  ---
13
13
 
@@ -58,7 +58,7 @@ render() {
58
58
  userProfile: this.service.services.authentication.userProfile,
59
59
  }));
60
60
  if (!values) return;
61
- return presentation.render({ ...values, toggleView: () => this.service.transactions.setViewVisible({ name, visible: true }) });
61
+ return presentation.render({ ...values, toggleView: () => this.service.actions.setViewVisible({ name, visible: true }) });
62
62
  }
63
63
  ```
64
64
 
@@ -31,6 +31,8 @@ authoring abstraction.
31
31
 
32
32
  ## File naming
33
33
 
34
+ Outside `features/`. Inside a feature, the `features/ecs/` naming applies (`<layer>-database.ts`).
35
+
34
36
  Each file whose primary export is a `Database.Plugin` (created or
35
37
  combined) ends in `-plugin.ts` — e.g. `node-plugin.ts`, `model-plugin.ts`,
36
38
  `pbr-core-plugin.ts`. The suffix is for discovery: cursor rules,
@@ -6,6 +6,8 @@ paths:
6
6
 
7
7
  # Service authoring
8
8
 
9
+ > Inside a feature, `features/services/index.md` governs the folder layout: a small service keeps `create.ts` and `create-fake.ts` flat beside the interface (the single-file exception below); one with helpers moves its implementation into a sub-folder. Database-bound factories in `ecs/services/` follow `features/ecs/services.md`.
10
+
9
11
  Asynchronous data services. Live in the `services/` layer. Adhere to the namespace rule for type and function organization.
10
12
 
11
13
  **Data** = readonly JSON values, `ReadonlySet`, `ReadonlyMap`, or Blobs.
@@ -12,6 +12,9 @@ persisted document can be upgraded to the current schema on load. You (human or
12
12
  agent) should almost never have to reason about this by hand: **change the schema,
13
13
  run the tests, and the failing test prints the exact fix to apply.**
14
14
 
15
+ The history lives in the app root, `src/app/versioning/` (`app.md`), because the
16
+ persisted data covers every feature's schema in the database.
17
+
15
18
  ## The pieces (all from `@adobe/data/ecs`)
16
19
 
17
20
  - **`versions.ts`** — `export const versions: readonly VersionEntry[]`. `entries[i]`
@@ -59,7 +62,7 @@ run the tests, and the failing test prints the exact fix to apply.**
59
62
  writes to SEVERAL components (a cross-component move, deriving a new component from
60
63
  others, splitting one into many) must be hand-written to your upgrade algorithm:
61
64
  query the input components, compute, and write the outputs directly on the store.
62
- Add a test case either way (rule 6).
65
+ Add a test case either way (the guard test below).
63
66
  5. **Removing a component drops its data.** If the data still matters, migrate it in a
64
67
  handler BEFORE the removal entry.
65
68
  6. **Versioned schemas must be REAL** — declare a `type` (or `enum`/`const`), and the
@@ -83,7 +86,7 @@ history. Its message is a literal recipe. To fix it:
83
86
  and the `changes` block the error printed verbatim. `db.version` follows the history
84
87
  automatically (`= versions.length - 1`) — there is no resource default to update.
85
88
  2. If the error marks a change **BREAKING — needs a handler**, add a `handler` to that
86
- entry AND add a matching test case under `testUpgradeHandlers` (rule 6). The error
89
+ entry AND add a matching test case under `testUpgradeHandlers` (the guard test below). The error
87
90
  may point at `Store.remapComponent(...)` for an isolated single-component change, but
88
91
  that is only a suggestion — write whatever upgrade code the change actually needs.
89
92
 
@@ -97,17 +100,17 @@ schema (schema coverage) AND every entry with a `handler` has a passing test cas
97
100
 
98
101
  ```ts
99
102
  import { Database, assertVersioning, createVersionUpgrader } from "@adobe/data/ecs";
100
- import { MainService } from "../main-service.js";
103
+ import { AppSchema } from "../schema.js";
101
104
  import { versions } from "./versions.js";
102
105
 
103
106
  describe("database schema versions", () => {
104
107
  it("the version history is consistent (schema folds + every handler tested)", () =>
105
108
  assertVersioning({
106
109
  // the version-configured db, so db.version = the history's current version
107
- database: Database.create(MainService.plugin, { versioning: createVersionUpgrader(versions) }),
110
+ database: Database.create(AppSchema.plugin, { versioning: createVersionUpgrader(versions) }),
108
111
  entries: versions,
109
112
  handlers: {
110
- // Append a case per version that adds a `handler` (rule 6):
113
+ // Append a case per version that adds a `handler` (the guard test below):
111
114
  // <version>: { setup: (store) => <populate the version-(v-1) store, return entities>,
112
115
  // expect: (store, setup) => <assert the version-v result> },
113
116
  },
@@ -116,7 +119,6 @@ describe("database schema versions", () => {
116
119
  ```
117
120
 
118
121
  (`assertVersionsMatchSchema` and `testUpgradeHandlers` remain exported for bespoke use.)
119
- ```
120
122
 
121
123
  ## Example: a breaking change (number → object) at version 1
122
124
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adobe-data-ai",
3
- "version": "0.10.21",
3
+ "version": "0.10.23",
4
4
  "description": "Architecture skills for @adobe/data — data-oriented modelling, archetype iteration, hot-path performance, and related conventions.",
5
5
  "author": {
6
6
  "name": "Adobe"
package/README.md CHANGED
@@ -55,6 +55,14 @@ self-update**, editing your `package.json` and `.gitignore` to:
55
55
  scripts, so a `postinstall` shipped inside the package would silently not fire;
56
56
  3. gitignore the managed bundle folders.
57
57
 
58
+ Each managed folder also gets a short "externally managed — do not edit" notice so
59
+ an agent that opens it is warned before touching the regenerated files. The notice
60
+ follows your repo's instruction convention: it is written as `AGENTS.md` by default
61
+ (the cross-agent standard, which Claude Code also reads) and only as `CLAUDE.md` for a
62
+ repo that is still CLAUDE.md-only. `install` never drops a `CLAUDE.md` into a repo that
63
+ uses `AGENTS.md` — a stray `CLAUDE.md` anywhere makes Claude Code ignore every
64
+ `AGENTS.md` project-wide.
65
+
58
66
  The copied files are regenerated artifacts — never committed, never edited in
59
67
  place. The wiring is idempotent, so re-running is harmless; with `--global` or in a
60
68
  directory with no `package.json`, `install` just copies (no wiring).
package/bin/cli.mjs CHANGED
@@ -41,10 +41,13 @@ const { name: PKG_NAME, version: VERSION } = pkg;
41
41
  // each agent root.
42
42
  const BUNDLE = "adobe-data-ai";
43
43
 
44
- // A do-not-edit notice dropped at each bundle root. Claude Code loads nested
45
- // CLAUDE.md files when working in a subtree, so an agent that opens one of these
46
- // folders sees the notice before touching anything. `extra` adds a pointer to
47
- // the companion bundle.
44
+ // A do-not-edit notice dropped at each bundle root so an agent that opens one of
45
+ // these managed folders sees it before touching anything. The file is named for the
46
+ // project's instruction convention (see `noticeFilename`) — never unconditionally
47
+ // `CLAUDE.md`: Claude Code treats any CLAUDE.md in the tree as switching the whole
48
+ // project into CLAUDE.md mode and then ignores every AGENTS.md project-wide (observed
49
+ // on Claude Code 2.1.286), which would silently blank out instructions in an
50
+ // AGENTS.md-based repo. `extra` adds a pointer to the companion bundle.
48
51
  function notice(extra) {
49
52
  return `# Externally managed — do not edit
50
53
 
@@ -58,11 +61,23 @@ version, then reinstall — do not modify these files in place.
58
61
  ${extra ? `\n${extra}\n` : ""}`;
59
62
  }
60
63
 
61
- function writeMeta(bundleDir, noticeBody, extraMeta) {
62
- writeFileSync(join(bundleDir, "CLAUDE.md"), noticeBody);
64
+ // Choose the managed notice's filename from the project's instruction convention.
65
+ // Preference is AGENTS.md (the cross-agent standard, which Claude Code also reads); we
66
+ // fall back to CLAUDE.md only for a repo still on the CLAUDE.md convention (a root
67
+ // CLAUDE.md and no AGENTS.md), so the notice keeps auto-loading there. An AGENTS.md
68
+ // repo (and a greenfield one) therefore never receives a data-ai CLAUDE.md — a stray
69
+ // CLAUDE.md anywhere flips Claude Code out of AGENTS.md mode for the whole project.
70
+ function noticeFilename(base) {
71
+ const hasClaude = existsSync(join(base, "CLAUDE.md"));
72
+ const hasAgents = existsSync(join(base, "AGENTS.md"));
73
+ return hasClaude && !hasAgents ? "CLAUDE.md" : "AGENTS.md";
74
+ }
75
+
76
+ function writeMeta(bundleDir, noticeBody, extraMeta, noticeFile) {
77
+ writeFileSync(join(bundleDir, noticeFile), noticeBody);
63
78
  writeFileSync(
64
79
  join(bundleDir, ".data-ai.json"),
65
- JSON.stringify({ package: PKG_NAME, version: VERSION, ...extraMeta }, null, 2) + "\n",
80
+ JSON.stringify({ package: PKG_NAME, version: VERSION, notice: noticeFile, ...extraMeta }, null, 2) + "\n",
66
81
  );
67
82
  }
68
83
 
@@ -107,6 +122,7 @@ function installSkills(base, skills) {
107
122
  bundleDir,
108
123
  notice("The architecture rules these skills follow are installed at\n`.claude/rules/adobe-data-ai/` (referenced by name from each SKILL.md)."),
109
124
  { skills },
125
+ noticeFilename(base),
110
126
  );
111
127
  return bundleDir;
112
128
  }
@@ -118,7 +134,7 @@ function installRules(base) {
118
134
  // Copy the whole rules tree, minus the top-level README.md meta doc.
119
135
  const readme = join(rulesSrc, "README.md");
120
136
  cpSync(rulesSrc, bundleDir, { recursive: true, filter: (src) => src !== readme });
121
- writeMeta(bundleDir, notice(), { rules: countRules(rulesSrc) });
137
+ writeMeta(bundleDir, notice(), { rules: countRules(rulesSrc) }, noticeFilename(base));
122
138
  return bundleDir;
123
139
  }
124
140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data-ai",
3
- "version": "0.10.21",
3
+ "version": "0.10.23",
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.10.21"
32
+ "@adobe/data": "0.10.23"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^5.8.3",
36
36
  "vitest": "^1.6.0",
37
- "@adobe/data": "0.10.21"
37
+ "@adobe/data": "0.10.23"
38
38
  },
39
39
  "license": "MIT",
40
40
  "scripts": {
@@ -1,16 +1,15 @@
1
1
  ---
2
2
  name: build-actions
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.
3
+ description: Build a feature's ecs/actions layer — the operations the app performs.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Skip if this feature doesn't contain or require actions.
8
+ Create `ecs/actions/`: `action-database.ts` (extends the previous layer, adds `actions`
9
+ from `./index.js`), one action per file, and a barrel. Every spec action has a
10
+ same-named action. It awaits services and commits through at most one transaction
11
+ (add any transaction `build-transactions` missed). Point `ecs/main-service.ts` at the top layer.
9
12
 
10
- Create `services/main-service/action-database/`: `action-database.ts` (extends `ServiceDatabase`, adds `actions`
11
- from `./actions/index.js`) plus an `actions/` folder — one async orchestrator per file. An action
12
- takes `db: ServiceDatabase`, awaits `services/` ports, then commits **exactly one** transaction.
13
+ Gate: `checkFeature` passes for each op as it is added.
13
14
 
14
- The top main-service layer; comes after `service-database`.
15
-
16
- The how is in the auto-loading `features/services/main-service/actions.md` rule.
15
+ The how is in the auto-loading `features/ecs/actions.md` rule.
@@ -1,15 +1,19 @@
1
1
  ---
2
2
  name: build-app-entry
3
- description: Wire built features into a running application — import peer schemas, lazy-load peers, and mount the root UI.
3
+ description: Build an application's src/app/ composition root.
4
4
  input: app
5
5
  output: app
6
6
  ---
7
7
 
8
- - The base (`features/main`) core-database `imports` each peer feature's core-database
9
- plugin, so all schemas coexist, persist, and sync while the base type stays decoupled.
10
- - The base reaches a peer only through a lazy element wrapper (dynamic import), so peers
11
- load on demand.
12
- - The entry point creates the live database from `MainService.plugin` (the base
13
- feature's assembled database — see `features/services/main-service/index.md`) and mounts the root UI.
8
+ Create `src/app/`:
14
9
 
15
- See `features/index.md` (one-app-many-features) for the wiring rules.
10
+ - `schema.ts` — the composed plugin: the base feature's `MainService.plugin`, `imports`
11
+ of the core schema of each feature that loads lazily (single-feature apps skip this).
12
+ - `versioning/` — `versions.ts` + `versions.test.ts` over the composed schema, when
13
+ the app persists.
14
+ - `persistence.test.ts` — a save/load round-trip, when the app persists.
15
+ - `main.ts` — creates the database, injects every service whose `create` throws, and
16
+ mounts the root UI. Point `index.html` at it.
17
+
18
+ The how is in the auto-loading `app.md` and `versioning.md` rules, and
19
+ `features/index.md` (one app, many features).
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  name: build-computed
3
- description: Build a feature's computed-database — derived observable values.
3
+ description: Build a feature's ecs/computed layer. Optional.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Skip if this feature doesn't contain or require computed values.
8
+ Skip if the spec has no derivations and the UI needs no derived values.
9
9
 
10
- Create `services/main-service/computed-database/`: `computed-database.ts` (extends the previous layer, adds
11
- `computed` from `./computed/index.js`) plus a `computed/` folder — one `cached` derived value
12
- per file + barrel. Derivation logic lives in `data/`; a computed only wires a db observable to it.
10
+ Create `ecs/computed/`: `computed-database.ts` (extends the previous layer, adds
11
+ `computed` from `./index.js`), one `cached` derived value per file, and a barrel. Wire
12
+ `data/values/` helpers to the minimal observables. Name each entity-id-list computed in
13
+ the implementation's `hydrate`, and pass `computedPlugin: ComputedDatabase.plugin`.
13
14
 
14
- Comes after `transaction-database`.
15
+ Gate: each derivation's computed conforms.
15
16
 
16
- The how is in the auto-loading `features/services/main-service/computed.md` rule.
17
+ The how is in the auto-loading `features/ecs/computed.md` rule.
@@ -1,24 +1,27 @@
1
1
  ---
2
2
  name: build-core-database
3
- description: Build a feature's schema — the core-database (components, resources, archetypes). The first main-service layer.
3
+ description: Build a feature's ECS schema (ecs/core) and its conformance wiring. The first ecs layer.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Create `services/main-service/core-database/` — `core-database.ts` plus one single-export file per facet
9
- the feature actually uses (`components`, `resources`, `archetypes` are each optional on
10
- `Database.Plugin.create`). Create only the facet files you need; omit the rest rather than
11
- shipping empty `Database.components({})` boilerplate. A feature with **no entities** (a
12
- singleton whose whole state is resources) is just `resources.ts` + `core-database.ts`.
8
+ Create:
13
9
 
14
- - `components.ts` → `Database.components({ document, settings, presence, session })` (scopes optional) — only if the feature has entities.
15
- - `resources.ts` → `Database.resources({ … })` (every entry needs a `default`) — for singleton / global state.
16
- - `archetypes.ts` → `Database.archetypes(components, { … })` — only alongside components.
17
- - `core-database.ts` → `Database.Plugin.create({ imports?, components?, resources?, archetypes? })`, exporting the `CoreDatabase` namespace.
10
+ - `ecs/core/archetypes.ts` — `Database.archetypes(components, { … })`, spreading each
11
+ `data/entities` tuple and adding implementation-only columns (never a
12
+ `nonPersistent` column without a real default).
13
+ - `ecs/core/core-database.ts` — `Database.Plugin.create({ extends?, components?,
14
+ resources?, archetypes? })` from the `data/` barrels, exporting the `CoreDatabase`
15
+ namespace. A feature built on another extends that feature's core.
16
+ - `ecs/main-service.ts` — `export { CoreDatabase as MainService }`, moved up as layers
17
+ are added.
18
+ - `ecs/conformance/` — `projection.ts`, `implementation.ts`
19
+ (`Conformance.implementation(spec, …)`) and `conformance.test.ts`
20
+ (`Conformance.checkFeature`).
18
21
 
19
- The schema root; comes after `data/` (and `services/`). Bind each schema from its `data/`
20
- type — don't author shapes here. Components use JSON schemas; resources use the
21
- `{ default }` convention (a schemaless aggregate binds via `{ default: State.create() }`).
22
+ Gate: the samples round-trip through the projection. Until the later layers exist,
23
+ `checkFeature` lists each spec op without an implementation by name; that list is
24
+ the work remaining.
22
25
 
23
- The how is in the auto-loading `features/services/main-service/index.md` + `components.md` / `resources.md` /
24
- `archetypes.md` rules.
26
+ The how is in the auto-loading `features/ecs/index.md`, `core.md` and `conformance.md`
27
+ rules.
@@ -1,22 +1,23 @@
1
1
  ---
2
2
  name: build-data
3
- description: Build a feature's data/ layer — data-type namespaces and the State spec. The first, foundational feature phase.
3
+ description: Build a feature's data/ layer — values, components, resources, entities. The first, foundational phase.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Create the feature's `data/` layer (pure spec; depends on nothing but `@adobe/data`
9
- and other `data/` declarations):
8
+ Create the feature's `data/` layer: pure Data declarations, depending only on
9
+ `@adobe/data` and the `data/` of features this one builds on.
10
10
 
11
- - one namespace folder per data type — `<type>/{<type>.ts, public.ts, <helper>.ts}` (+ `schema.ts` only when a runtime boundary needs it);
12
- - `data/state/` — the `State` aggregate plus its pure transforms/derivations.
11
+ - `values/<type>/` — one namespace per Data type: a hand-written type, a schema pinned
12
+ to it (`type _Pin = Assert<Equal<Schema.ToType<typeof schema>, <Type>>>`) when
13
+ something needs one, and pure helpers, each with a test.
14
+ - `components/<name>.ts` — one schema per file, scope spread from `Scope`, plus an
15
+ `index.ts` barrel. Include implementation-only columns (tags, drag offsets).
16
+ - `resources/<name>.ts` — one schema per file, `satisfies ResourceSchema`, plus a barrel.
17
+ - `entities/<entity>.ts` — the spec-visible entity as a component-name tuple, with its
18
+ type derived by `Database.EntityType`, plus a barrel.
13
19
 
14
- **Always create `data/state/`** — even for a shell feature whose only field is a
15
- boolean or enum. A feature with ECS resources/transactions but no `State` is
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 = {}
20
+ Create only the folders the feature needs. Gate: it compiles and the helper tests
21
+ pass.
17
22
 
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
- Helpers are pure and unit-tested. Run this first — every other layer imports `data/`.
20
-
21
- The how is in the auto-loading rules: `features/data/index.md`, `features/data/state.md`, and
22
- `global/namespace.md`.
23
+ The how is in the auto-loading `features/data/` rules and `global/namespace.md`.
@@ -8,11 +8,12 @@ output: feature
8
8
  /graph-execute
9
9
  /build-data
10
10
  /build-services
11
+ /build-spec
11
12
  /build-core-database
12
13
  /build-indexes
13
14
  /build-transactions
14
- /build-computed
15
15
  /build-service-database
16
+ /build-computed
16
17
  /build-actions
17
18
  /build-systems
18
19
  /build-ui
@@ -7,15 +7,17 @@ output: game
7
7
 
8
8
  /build-application — a game is an application.
9
9
 
10
- Map game concepts to feature layers: data/ = state + rules (+ per-tick step math),
11
- components/archetypes = entity kinds, indexes = spatial/lookup queries,
12
- transactions = moves/spawns, computed = score/status/winner, ui = view + input.
10
+ Map game concepts to feature layers: data/ = value types, rules math, components,
11
+ resources (the board, the player, the score) and entities (the pieces); spec/ = the
12
+ game state + moves (+ one pure function per system); indexes = spatial/lookup queries;
13
+ transactions + actions = moves/spawns; computed = score/status/winner; ui = view + input.
13
14
 
14
15
  - **Turn-based** uses the reactive transaction → computed → ui loop, no systems
15
16
  (see data-lit-tictactoe).
16
17
  - **Real-time** adds the `build-systems` phase: a per-frame tick loop (movement,
17
18
  collision, lifetime) driven by the scheduler, over many entities in distinct
18
19
  archetypes. Systems must stay O(1) per touched entity — never project the whole
19
- `State` into/out of the store each frame (see `features/services/main-service/systems.md`).
20
+ `State` into/out of the store each frame, and never turn per-row work into per-entity
21
+ transactions. Each system conforms alone and as a frame (see `features/ecs/systems.md`).
20
22
  This is where components, archetypes, indexes, and systems all carry real
21
23
  weight.
@@ -1,16 +1,15 @@
1
1
  ---
2
2
  name: build-indexes
3
- description: Build a feature's index-database — component/archetype indexes for O(1) lookup. As needed.
3
+ description: Build a feature's ecs/indexes layer. Optional.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Skip if this feature doesn't contain or require indexes.
8
+ Skip if the feature needs no indexed lookups.
9
9
 
10
- Create `services/main-service/index-database/`: `index-database.ts` (extends `CoreDatabase`, adds the `indexes`
11
- facet from `./indexes/index.js`) plus an `indexes/` folder — one index per file + an `index.ts`
12
- barrel.
10
+ Create `ecs/indexes/`: `index-database.ts` (extends `CoreDatabase`, adds `indexes` from
11
+ `./index.js`), one index per file, and an `index.ts` barrel.
13
12
 
14
- Only if the feature needs indexed lookups. Comes after `core-database`.
13
+ Gate: conformance unchanged, plus index tests where an index has logic.
15
14
 
16
- The how is in the auto-loading `features/services/main-service/indexes.md` rule.
15
+ The how is in the auto-loading `features/ecs/indexes.md` rule.
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: build-service-database
3
- description: Build a feature's service-database — db-bound service factories. If the feature exposes services.
3
+ description: Build a feature's ecs/services layer — registering services on the database. Optional.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Create `services/main-service/service-database/`: `service-database.ts` (extends the previous layer, registers the
9
- `services` facet). Two forms:
8
+ Skip if the feature has no services.
10
9
 
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
12
- - plain async ports registered straight from the feature's `services/` contracts.
10
+ Create `ecs/services/service-database.ts` (extends the previous layer), registering
11
+ each `services/` service's `create` under its `Services` key, plus any database-bound
12
+ factories (one per file + barrel) for services that read observables or call
13
+ transactions. Pin with `Assert<Equal<ServiceDatabase["services"], Services>>`. Give
14
+ the implementation base fakes for services whose `create` throws.
13
15
 
14
- Comes after `computed-database`.
15
-
16
- The how is in the auto-loading `features/services/main-service/services.md` rule.
16
+ The how is in the auto-loading `features/ecs/services.md` rule.
@@ -1,16 +1,18 @@
1
1
  ---
2
2
  name: build-services
3
- description: Build a feature's services/ layer — async capability contracts (ports to the outside world). Optional.
3
+ description: Build a feature's services/ layer — async data services (interface, create, createFake). Optional.
4
4
  input: feature
5
5
  output: feature
6
6
  ---
7
7
 
8
- Skip if this feature doesn't contain or require services.
8
+ Skip if the feature talks to nothing outside itself.
9
9
 
10
- Create the feature's `services/` layer: one `<name>-service/` namespace folder per async
11
- contract — an `interface` (validated with `AsyncDataService.IsValid`), async-only members,
12
- and `create*` factories.
10
+ Create one `services/<name>-service/` namespace per service: the interface (an async
11
+ data service, validated with `AsyncDataService.IsValid`), `create` (the
12
+ implementation, or a factory that throws when the app must inject it),
13
+ `createFake`, and `public.ts`. Add `services/services.ts` with the `Services` map when
14
+ spec actions inject services.
13
15
 
14
- Only if the feature talks to the outside world. Comes after `data/`, before `services/main-service/`.
16
+ Depends only on `data/`. Gate: per-service unit tests.
15
17
 
16
- The how is in the auto-loading `features/services/index.md` rule (and `global/namespace.md`).
18
+ The how is in the auto-loading `features/services/index.md` rule.