@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.
- package/.claude/rules/README.md +26 -31
- package/.claude/rules/app.md +46 -0
- package/.claude/rules/archetypes.md +5 -3
- package/.claude/rules/data-modelling.md +8 -7
- package/.claude/rules/ecs.md +11 -7
- package/.claude/rules/element.md +6 -5
- package/.claude/rules/features/data/components.md +51 -0
- package/.claude/rules/features/data/entities.md +37 -0
- package/.claude/rules/features/data/index.md +18 -70
- package/.claude/rules/features/data/resources.md +34 -0
- package/.claude/rules/features/data/values.md +36 -0
- package/.claude/rules/features/ecs/actions.md +31 -0
- package/.claude/rules/features/ecs/computed.md +31 -0
- package/.claude/rules/features/ecs/conformance.md +85 -0
- package/.claude/rules/features/ecs/core.md +43 -0
- package/.claude/rules/features/ecs/index.md +72 -0
- package/.claude/rules/features/{services/main-service → ecs}/indexes.md +6 -7
- package/.claude/rules/features/ecs/services.md +36 -0
- package/.claude/rules/features/{services/main-service → ecs}/systems.md +61 -47
- package/.claude/rules/features/ecs/transactions.md +32 -0
- package/.claude/rules/features/index.md +104 -150
- package/.claude/rules/features/services/index.md +52 -84
- package/.claude/rules/features/spec/index.md +161 -0
- package/.claude/rules/features/ui/binding-element.md +5 -5
- package/.claude/rules/features/ui/index.md +8 -3
- package/.claude/rules/features/ui/lazy-wrapper.md +2 -2
- package/.claude/rules/features/ui/presentation-test.md +1 -0
- package/.claude/rules/features/ui/presentation.md +1 -0
- package/.claude/rules/global/function-references.md +2 -2
- package/.claude/rules/global/namespace.md +3 -3
- package/.claude/rules/global/react.md +4 -4
- package/.claude/rules/lit.md +2 -2
- package/.claude/rules/plugin-modelling.md +2 -0
- package/.claude/rules/service.md +2 -0
- package/.claude/rules/versioning.md +8 -6
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +8 -0
- package/bin/cli.mjs +24 -8
- package/package.json +3 -3
- package/skills/build-actions/SKILL.md +7 -8
- package/skills/build-app-entry/SKILL.md +12 -8
- package/skills/build-computed/SKILL.md +8 -7
- package/skills/build-core-database/SKILL.md +18 -15
- package/skills/build-data/SKILL.md +14 -13
- package/skills/build-feature/SKILL.md +2 -1
- package/skills/build-game/SKILL.md +6 -4
- package/skills/build-indexes/SKILL.md +6 -7
- package/skills/build-service-database/SKILL.md +8 -8
- package/skills/build-services/SKILL.md +9 -7
- package/skills/build-spec/SKILL.md +23 -0
- package/skills/build-systems/SKILL.md +12 -18
- package/skills/build-transactions/SKILL.md +8 -8
- package/skills/build-ui/SKILL.md +5 -4
- package/skills/review/SKILL.md +3 -3
- package/.claude/rules/features/data/state.md +0 -209
- package/.claude/rules/features/services/main-service/actions.md +0 -70
- package/.claude/rules/features/services/main-service/archetypes.md +0 -49
- package/.claude/rules/features/services/main-service/components.md +0 -41
- package/.claude/rules/features/services/main-service/computed.md +0 -82
- package/.claude/rules/features/services/main-service/conformance.md +0 -95
- package/.claude/rules/features/services/main-service/core-database.md +0 -48
- package/.claude/rules/features/services/main-service/index.md +0 -187
- package/.claude/rules/features/services/main-service/resources.md +0 -28
- package/.claude/rules/features/services/main-service/services.md +0 -25
- 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 `
|
|
9
|
-
owns no business logic — it subscribes to state, hands it to
|
|
10
|
-
presentation, and wires
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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`
|
|
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 `
|
|
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.
|
|
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.
|
|
85
|
-
toggleSprite: () => db.
|
|
84
|
+
increment: db.actions.increment; // reference — no wrapper needed
|
|
85
|
+
toggleSprite: () => db.actions.toggleSpriteActive({ entity }); // args required — wrap
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
---
|
package/.claude/rules/lit.md
CHANGED
|
@@ -7,7 +7,7 @@ paths:
|
|
|
7
7
|
|
|
8
8
|
# Lit element authoring
|
|
9
9
|
|
|
10
|
-
Lit elements live in the `
|
|
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.
|
|
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,
|
package/.claude/rules/service.md
CHANGED
|
@@ -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 (
|
|
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` (
|
|
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 {
|
|
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(
|
|
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` (
|
|
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
|
|
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
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// the
|
|
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
|
-
|
|
62
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
3
|
+
description: Build an application's src/app/ composition root.
|
|
4
4
|
input: app
|
|
5
5
|
output: app
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
8
|
+
Skip if the spec has no derivations and the UI needs no derived values.
|
|
9
9
|
|
|
10
|
-
Create `
|
|
11
|
-
`computed` from `./
|
|
12
|
-
|
|
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
|
-
|
|
15
|
+
Gate: each derivation's computed conforms.
|
|
15
16
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- `core-database.ts`
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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/
|
|
24
|
-
|
|
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 —
|
|
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
|
|
9
|
-
and
|
|
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
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
|
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/ =
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
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
|
|
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
|
|
8
|
+
Skip if the feature needs no indexed lookups.
|
|
9
9
|
|
|
10
|
-
Create `
|
|
11
|
-
|
|
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
|
-
|
|
13
|
+
Gate: conformance unchanged, plus index tests where an index has logic.
|
|
15
14
|
|
|
16
|
-
The how is in the auto-loading `features/
|
|
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
|
|
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
|
-
|
|
9
|
-
`services` facet). Two forms:
|
|
8
|
+
Skip if the feature has no services.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
8
|
+
Skip if the feature talks to nothing outside itself.
|
|
9
9
|
|
|
10
|
-
Create
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
18
|
+
The how is in the auto-loading `features/services/index.md` rule.
|