@adobe/data-ai 0.9.100 → 0.9.102

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.
@@ -49,9 +49,13 @@ single source of truth, not a downstream comparison flag:
49
49
  references is `ReadonlySet<number>` (a set of ids), and an unordered entity
50
50
  *query result* is `ReadonlySet<number>`.
51
51
  - **`ReadonlyMap<K, V>`** — a keyed lookup. This is how **identity-keyed entities**
52
- are modelled: `entities: ReadonlyMap<number, EntityValue>` — the `number` id is the
53
- key, and the value carries **no `id` of its own** (identity is the key, never a
54
- field). Also used for deterministic-key lookups (an enum, a name, a stable string).
52
+ are modelled: `entities: ReadonlyMap<number, EntityValue>` — the id is the key, and
53
+ the value carries **no `id` of its own** (identity is the key, never a field). The key
54
+ may be a plain `number` or the ECS `Entity` type (a branded `number`) — reusing
55
+ `Entity`, and reference bundles like `CoeditingRelations.OrderedChild`, is allowed:
56
+ they are plain value types, not ECS machinery, and keep the spec aligned with the ECS
57
+ (see `features/data/state.md`). Also used for deterministic-key lookups (an enum, a
58
+ name, a stable string).
55
59
 
56
60
  **Entities are keyed, never id-bearing values.** An entity's identity is its map
57
61
  key, so entity value types (`Todo`, `Bullet`) have no `id` field, and there is no
@@ -49,12 +49,18 @@ A `State` has two kinds of field:
49
49
  types (`Todo`, or `Bullet | Asteroid`). Omit `entities` entirely for a feature with
50
50
  no entities (`tictactoe`, `dashboard`) — it is then all singletons.
51
51
 
52
- This maps 1:1 to the ECS with **zero dependency on it** — the key is a plain `number`,
53
- never the ECS `Entity`, so `data/` provably cannot reach ECS machinery. Singletons ↔
54
- resources; each `entities` value ↔ one entity whose **component set is the value's own
55
- keys**, so `fromState` inserts a value into the archetype named by `Object.keys(value)`
56
- and `toState` reads every entity back into the map. Identity lives in the key, never a
57
- field — see `../../data-modelling.md` (Entities are keyed, never id-bearing values).
52
+ This maps 1:1 to the ECS. `State` must not depend on ECS *machinery* (a store, a
53
+ transaction, an index) — but it **may reuse ECS-related value types**: the key can be
54
+ the `Entity` type (it is just a branded `number`), and a reference/hierarchy bundle can
55
+ reuse a shared value type like `CoeditingRelations.OrderedChild` (`{ parent, order }`).
56
+ Those are plain serializable data, not ECS behaviour, and reusing them keeps the spec
57
+ and the ECS in lockstep — the same `Entity.schema` that types an id here is what
58
+ conformance walks to compare the two up to an id-bijection (see `conformance.md`).
59
+ Singletons ↔ resources; each `entities` value ↔ one entity whose **component set is the
60
+ value's own keys**, so `fromState` inserts a value into the archetype named by
61
+ `Object.keys(value)` and `toState` reads every entity back into the map. Identity lives
62
+ in the key, never a field — see `../../data-modelling.md` (Entities are keyed, never
63
+ id-bearing values).
58
64
 
59
65
  ### Entity value types — structural, id-less, one `is` guard each
60
66
 
@@ -146,7 +152,7 @@ and `.test.ts`.
146
152
  fixtures the pure `spec.test.ts` **and** the ecs `runFeature` call both reuse
147
153
  (driving transaction / action / computed conformance) — the transform's contract
148
154
  expressed as data, not a per-file test — so they belong
149
- beside the thing they specify, and `Conformance<typeof fn>` binds them to the
155
+ beside the thing they specify, and `Conformance.cases(fn, …)` binds them to the
150
156
  signature so they can't drift. Kept inert (no `describe`; one aggregator runs
151
157
  them) they also sidestep the double execution vitest triggers when a single file
152
158
  both exports cases and runs its own `describe`. Coverage is then enforced
@@ -166,18 +172,16 @@ transform/derivation files can write a one-parameter type:
166
172
  // data/state/conformance-case.ts — the only per-feature conformance declaration
167
173
  import { Conformance as ConformanceApi } from "@adobe/data-testing";
168
174
  import type { State } from "./state.js";
169
- export type Conformance<F extends (...args: never[]) => unknown> = ConformanceApi.Cases<State, F>;
175
+ // `Conformance.cases(fn, [options,] ...cases)` declares a transform's cases — the
176
+ // case types (args, before/after) come from `fn`. A derivation authors `Derivation<typeof fn>`.
177
+ export const Conformance = { cases: ConformanceApi.casesBuilder<State>() };
170
178
  export type Derivation<F extends (...args: never[]) => unknown> = ConformanceApi.DerivationCases<F>;
171
179
  export type Effects<Args> = ConformanceApi.Effects<Args>;
172
- // The entity-reference marker for identity-addressed case args, re-exported so
173
- // cases import it beside `Conformance`: `args: { id: entity(2) }`.
174
- export const entity = ConformanceApi.entity;
175
180
  ```
176
181
 
177
182
  ```ts
178
183
  // create-todo.ts
179
- import { Match } from "@adobe/data-testing";
180
- import type { Conformance } from "./conformance-case.js"; // the thin per-feature alias above
184
+ import { Conformance } from "./conformance-case.js"; // the thin per-feature alias above
181
185
  import type { Services } from "../../services/services.js"; // the feature's service map
182
186
  export const createTodo = (
183
187
  state: Pick<State, "entities">,
@@ -191,16 +195,17 @@ export const createTodo = (
191
195
  return { entities: new Map(state.entities).set(id, { name, complete: false, order }) };
192
196
  };
193
197
 
194
- export const cases: Conformance<typeof createTodo> = [
198
+ export const cases = Conformance.cases(createTodo,
195
199
  { name: "adds the first todo",
196
200
  before: {}, // empty delta — the default State.create() (empty entities)
197
201
  args: { name: "a", analytics: AnalyticsService.createFake() },
198
- // The map key is an id the ECS mints — use `Match.ref("label")` (a DISTINCT
199
- // label per entity). The value is id-less so content compares directly. (Maps
200
- // compare entry-wise / order-independently — see conformance.md.)
201
- after: { entities: new Map([[Match.ref("a"), { name: "a", complete: false, order: 0 }]]) },
202
+ // The map key is a PLAIN spec-id you choose (1). The ECS mints its own id, so
203
+ // conformance compares up to an id-bijection — the key need only be distinct.
204
+ // The value is id-less so content compares directly. (Maps compare entry-wise /
205
+ // order-independently — see conformance.md.)
206
+ after: { entities: new Map([[1, { name: "a", complete: false, order: 0 }]]) },
202
207
  effects: { analytics: [["todoCreated", { name: "a" }]] } },
203
- ];
208
+ );
204
209
  ```
205
210
 
206
211
  - **Signature** `(state: Pick<State, …reads>, args) => Pick<State, …writes>` — a
@@ -208,8 +213,8 @@ export const cases: Conformance<typeof createTodo> = [
208
213
  transition **reads**; the return is *only the fields it **writes***. **No
209
214
  `<T> => T` generic, no `...state` spread** in the return — return the patch and
210
215
  let the runner merge it. **All non-state inputs go in the single `args` object**
211
- (`Conformance<typeof fn>` reads `Parameters[1]`) — bundle a `dt`, an injected
212
- service, etc. into it, never as a third positional. A transition that takes
216
+ (the case's `args` type is read from `fn`'s `Parameters[1]`) — bundle a `dt`, an
217
+ injected service, etc. into it, never as a third positional. A transition that takes
213
218
  **no** args omits `args` from each case entirely (the shared `Case` type makes
214
219
  `args` optional exactly then). **Guard no-ops by returning an empty patch `{}`**
215
220
  (or the unchanged slice), never throw.
@@ -222,61 +227,56 @@ export const cases: Conformance<typeof createTodo> = [
222
227
  any barrel member) in a top-level `cases` literal dead-locks the import cycle.
223
228
  Import the concrete helper directly (`import { create } from "./create.js"`) or
224
229
  inline full-`State` literals.
225
- - **`Conformance<typeof fn>`** (the alias above) derives the case `args` type from
226
- the function's own signature — author it once, and cases can't drift from what
227
- the function accepts. **`before` is a delta over `State.create()`** — list only
230
+ - **`Conformance.cases(fn, ...cases)`** (the builder above) derives each case's
231
+ types from `fn`'s own signature — pass the transition function and cases can't drift
232
+ from what it accepts. **`before` is a delta over `State.create()`** — list only
228
233
  the fields this case sets differently from the default; **`after` is the writes
229
234
  patch** — only the fields the transition changes. The runner seeds
230
235
  `{ ...State.create(), ...before }` and compares against
231
236
  `{ ...State.create(), ...before, ...after }`, so any field a case doesn't mention
232
237
  is the default and stays unchanged. (A full `before`/`after` still works — it just
233
238
  overrides the default wholesale.)
234
- - **`after` leaves minted values open** with the shared matchers `Match.anyNumber`
235
- / `Match.anyString`, imported from `@adobe/data-testing` — there is **no**
236
- per-feature `matchers.ts` anymore. An entity's identity is the `entities` map **key**,
237
- not a value field, so entity content compares directly with nothing to ignore. The
238
- key is an ECS-minted id, so use **`Match.ref("label")` with a DISTINCT label per map
239
- entry** (`[Match.ref("a"), value]`) — `ref` returns a fresh object so distinct labels
240
- are distinct keys, and its injective binding both keeps entities distinct and lets an
241
- entity **correlate** with a reference elsewhere in the case (reuse the label, e.g. a
242
- `selectedId: Match.ref("a")` singleton). Do **not** use `Match.anyNumber` as a map key
243
- — it is a shared singleton, so two entries keyed by it collapse to one. `entity(specId)`
244
- is for **`args`** (it resolves via the seed map), **not** `after` keys — a case's `after`
245
- entities may be freshly created, with no seed mapping. Match by content, not by a value
246
- you don't control. `Match` is framework-agnostic and
247
- honors any asymmetric matcher, so vitest's `expect.stringContaining(...)` interops
248
- on the expected side too. When an id must **line up in two places** within one
249
- comparison — a `selectedId` that points at a specific todo, say — use
250
- `Match.ref(label)`: it asserts id *correspondence* (a bijection up to renaming),
251
- not a pinned value, so the two occurrences of the label must resolve to the same
252
- actual id and two labels can't collide. `anyNumber`/`anyString` are for an id a
253
- case does not pin at all; `ref` for one that must be consistent across the case.
254
- - **Entity-addressed cases use `entity(specId)` in `args`.** A transition that
255
- addresses an entity by id writes it as `args: { id: entity(2) }` — `entity` imported
256
- from the feature's `conformance-case.ts` (re-exported from `@adobe/data-testing`). It
257
- types as the id it stands for, so it slots into the transform's own arg type. `runSpec`
258
- unwraps it to the plain data-id for the pure side; the ECS runners resolve it (via the
259
- `fromState` seed map) to the seeded entity (see `conformance.md`).
239
+ - **`after` uses PLAIN spec-ids everywhere — no hand-authored key matchers.** An
240
+ entity's identity is the `entities` map **key**, not a value field, so entity content
241
+ compares directly. The ECS mints its **own** ids (a different set from the spec's), so
242
+ conformance compares the two **up to an id-bijection**: the runner reads the store's
243
+ schemas, finds every entity reference (a map key, and any field whose schema is
244
+ `Entity.schema` — recursing into a bundle like `{ parent, order }`), and matches the
245
+ case's spec-ids to the ECS's ids one-to-one. So a case author writes ordinary numbers
246
+ — `[[1, value], [2, value]]` — and **a reference field just holds the id it points at**
247
+ (`{ asset: 2 }`, `placement: { parent: 1, … }`). The bijection makes a key and every
248
+ field that names it line up on one actual id automatically — this is what killed the
249
+ old `Match.ref` / `Match.refMap` key-labelling (no more distinct-label bookkeeping,
250
+ and cross-references between entities finally compare correctly). For a **minted scalar**
251
+ a case does not want to pin — a generated timestamp, a random value — still use
252
+ `Match.anyNumber` / `Match.anyString` (imported from `@adobe/data-testing`); `Match` is
253
+ framework-agnostic, so vitest's `expect.stringContaining(...)` interops on the expected
254
+ side too. (The id-correspondence solver is now internal — the runner generates it from
255
+ the schema-marked spec-ids; there is no case-facing `ref`/`refMap` matcher anymore.)
256
+ - **Entity-addressed cases pass an `args` schema in the builder options.** State
257
+ references are detected from the store's schemas; an **arg** has none, so a transition
258
+ that addresses an entity by id gives `Conformance.cases` a leading options object whose
259
+ `args` schema marks the reference fields:
260
+ `Conformance.cases(fn, { args: { type: "object", properties: { id: Entity.schema } } }, …cases)`.
261
+ The case then writes the id **plain** — `args: { id: 2 }` — and the runner resolves each
262
+ `Entity.schema`-marked field to the seeded entity (via the `fromState` seed map) on the
263
+ ECS side, while the pure side reads the plain spec-id directly. Describe **only** the
264
+ reference fields (services and non-id args are omitted); the args type must be assignable
265
+ to the schema's `Schema.ToType`, so the schema can't drift from the signature (the builder
266
+ fails to type-check otherwise). This is the args-side analog of the `Entity.schema` mark
267
+ the runner reads on state.
260
268
  - **The `entities` map key convention across a case, in one place:**
261
269
  - **`before`** (the seed): **plain spec-id numbers** (`new Map([[1, …], [2, …]])`).
262
- `fromState` seeds from these and returns the `spec-id → entity` map, so `args:
263
- { id: entity(1) }` resolves to the entity seeded for `1`.
264
- - **`after`** (the expectation, compared against the ECS by content): **`Match.ref`
265
- with a distinct label per entry** (`[[Match.ref("a"), …], [Match.ref("b"), …]]`) —
266
- the ECS mints its own ids, so keys must be open; `ref` is a fresh object (distinct
267
- keys don't collapse) and injective (entities stay distinct, and a label reused in a
268
- singleton reference correlates). Never `entity(specId)` here — a case's `after` may
269
- hold freshly created entities with no seed mapping.
270
- - **`samples`** (round-tripped `toState ∘ fromState`): same as `after` — open,
271
- distinct keys against ECS-minted ids.
272
- - **Building the open-keyed map — `Match.refMap(values)`.** When the entries come
273
- from a list rather than hand-authored labels (a `samples` entry, a case `after`
274
- whose entities you don't cross-reference), don't hand-roll the `Match.ref` keys:
275
- `Match.refMap(iterableOfValues)` returns a `ReadonlyMap<number, V>` keyed by
276
- process-unique open `ref`s — the standard way to build an identity-keyed collection
277
- for comparison. Reach for a **hand-written `Match.ref(label)`** only when a key must
278
- **correspond** to a reference elsewhere in the same case (a `selectedId`), where the
279
- shared label is the whole point.
270
+ `fromState` seeds from these and returns the `spec-id → entity` map, so a case's
271
+ `args: { id: 1 }` resolves to the entity seeded for `1`.
272
+ - **`after`** (the expectation, compared against the ECS): **plain spec-id numbers**,
273
+ distinct per entry, and reference fields hold the spec-id they point at. The runner
274
+ refifies them and compares up to an id-bijection, so the ECS's own ids need not match.
275
+ You may reuse the same spec-ids as `before` for entities that persist (it reads
276
+ clearly), but any distinct numbers work — the comparison is by content + reference
277
+ structure, never by the literal id.
278
+ - **`samples`** (round-tripped `toState ∘ fromState`): same as `after` — plain spec-id
279
+ keys, compared up to a bijection against the ECS-minted ids.
280
280
  - No per-transform test. The single **`spec.test.ts`** is one call —
281
281
  `Conformance.runSpec({ state: State, transitions })` importing `transitions`
282
282
  from the test-only `./transitions.js` (above) — that auto-discovers every module
@@ -37,10 +37,10 @@ installing `@adobe/data` never pulls in a `vitest` peer dependency):
37
37
 
38
38
  - **`Match`** — the tolerant, matcher-aware value comparison: `matches(actual,
39
39
  expected, options?)` and its throwing wrapper `assert(...)`, plus the matchers
40
- `Match.anyNumber` / `Match.anyString` / `Match.ref(label)`, and the collection
41
- builder `Match.refMap(values)` (an identity-keyed `ReadonlyMap<number, V>` of
42
- id-less values with distinct open keys — the standard way to author a `samples`
43
- entry or a created-entity `after`; see `../../../data/state.md`). Options are
40
+ `Match.anyNumber` / `Match.anyString` for minted scalars a case does not pin.
41
+ **Entity ids need no matcher**: a case writes plain spec-ids for map keys and
42
+ reference fields, and the runner compares up to an id-bijection (below), so `samples`
43
+ and `after` are ordinary `new Map([[1, value], …])` — no key labelling. Options are
44
44
  `{ tolerance?: number }` — numbers snap to `tolerance` (default `0.01`) to absorb
45
45
  F32↔f64 / trig noise. **Ordering is carried by the value's type**: a
46
46
  `ReadonlyArray` compares **in order**, a `ReadonlySet` / `ReadonlyMap`
@@ -50,8 +50,8 @@ installing `@adobe/data` never pulls in a `vitest` peer dependency):
50
50
  ignore when comparing content. Framework-agnostic: it honors any asymmetric matcher,
51
51
  so vitest's `expect.any(...)` interops.
52
52
  - **`Conformance`** — the case types (`Case`, `Cases`, `DerivationCase`,
53
- `DerivationCases`, `Effects`, `ServiceCall`), the `entity(specId)` identity
54
- marker, the id `resolver(map)`, the whole-feature driver **`runFeature`**, the
53
+ `DerivationCases`, `Effects`, `ServiceCall`), the `casesBuilder` (a feature's
54
+ `Conformance.cases`), `assertState` for custom harnesses, the whole-feature driver **`runFeature`**, the
55
55
  pure-spec driver **`runSpec`**, and the lower-level per-surface drivers
56
56
  `runTransactions` / `runActions` / `runComputeds` (internals of `runFeature`,
57
57
  exported for the escape hatch below). Auto-pairing (transition ⇄ op by name),
@@ -167,8 +167,8 @@ Conformance.runSpec({ state: State, transitions });
167
167
  It discovers every file exporting `cases`, enforces the two-exports rule, and
168
168
  dispatches on case shape (transition → state + effects, derivation →
169
169
  `fn(input) ≡ value`), seeding each case's `before`/`input` as a delta over
170
- `State.create()`. Unwraps any `entity(specId)` arg marker to its plain data-id for
171
- the pure side.
170
+ `State.create()`. On the ECS side it resolves the reference args a case's `args`
171
+ schema marks; the pure side reads them plain.
172
172
 
173
173
  ## The exception — a per-surface `userId`, via the lower-level runners
174
174
 
@@ -182,17 +182,21 @@ action db. `runFeature` has no place to thread two different `seedContext`s, so
182
182
  this feature drops to the lower level. This is the escape hatch — ordinary
183
183
  features never touch these drivers directly.
184
184
 
185
- ## Identity — the `entity(specId)` marker
185
+ ## Identity — the `args` schema
186
186
 
187
- An entity-addressed transition writes its addressed id as `args: { id: entity(2) }`
188
- — import `entity`, re-exported from the feature's `data/state/conformance-case.ts`.
189
- `runSpec` unwraps it to the plain data-id; the ECS runner resolves it to the
187
+ An entity-addressed transition declares an `args` schema in the builder options and
188
+ writes the id **plain**: `Conformance.cases(fn, { args: { type: "object", properties:
189
+ { id: Entity.schema } } }, { …, args: { id: 2 }, … })`. The runner
190
+ finds each `Entity.schema`-marked arg field and resolves the plain spec-id to the
190
191
  **seeded entity** via the id→entity map `fromState` returns (turned into a `resolve`
191
- by `Conformance.resolver` — no feature writes `resolve` by hand). Two conventions
192
- make the wiring vanish: the ECS op takes the entity **under the transition's own arg
193
- key** (`{ id }`, same-shape args, no reshape), and `fromState` returns the
194
- `ReadonlyMap<Id, Entity>` id→entity map (or `void` for an index-addressed / singleton
195
- feature, whose ids then resolve to `Entity.none`). `data-lit-todo` is the reference.
192
+ by `Conformance.resolver` — no feature writes `resolve` by hand); the pure side reads
193
+ the spec-id as-is. Two conventions make the wiring vanish: the ECS op takes the entity
194
+ **under the transition's own arg key** (`{ id }`, same-shape args, no reshape), and
195
+ `fromState` returns the `ReadonlyMap<Id, Entity>` id→entity map (or `void` for an
196
+ index-addressed / singleton feature, whose ids then resolve to `Entity.none`).
197
+ `data-lit-todo` is the reference. Describe only the reference fields — services and
198
+ non-id args are omitted, and the args type must be assignable to the schema's
199
+ `Schema.ToType` so it cannot drift from the signature.
196
200
 
197
201
  ## Name-parity — add a same-named op, never a per-item adapter
198
202
 
@@ -202,25 +206,31 @@ op is infra, and a thin **same-named** op (todo's `reorderTodo` action, space-ro
202
206
  `createInitial` transaction) gives the transition something to pair with. Do **not**
203
207
  reintroduce a per-item adapter to bridge a name mismatch — add the same-named op.
204
208
 
205
- ## Ordering, tolerance, `ref`
209
+ ## Ordering, tolerance, the id-bijection
206
210
 
207
211
  Ordering is carried by the value's **type**, not a match option — `ReadonlyArray`
208
212
  positional, `ReadonlySet` / `ReadonlyMap` order-independent (the rule and its
209
213
  rationale live in `../../../data-modelling.md`). What's specific to writing
210
214
  conformance cases:
211
215
 
212
- - **Entity identity is the `State.entities` key, not a value field.** Entity values
213
- carry no `id`, so there is nothing to omit or ignore — content compares directly.
214
- The map key is a spec-domain id the runner resolves to the allocated ECS entity via
215
- `resolver`; assert a *reference* between entities with `Match.ref` (below).
216
+ - **The spec and the ECS mint different id sets, so comparison is up to an
217
+ id-bijection — automatically.** The runner reads the store's `componentSchemas`
218
+ (which also carry the resource schemas) and finds every entity reference: an
219
+ `entities` map **key** (an id by construction) and any field whose schema is
220
+ `Entity.schema` (`schema.entity === true`), recursing into bundled object schemas so
221
+ a `placement: { parent, order }` reference is found. It replaces each spec-id there
222
+ with a correspondence variable and solves a one-to-one matching against the ECS's
223
+ ids. So a case **writes plain spec-ids** for keys and reference fields, and a key and
224
+ every field naming it line up on one actual id with no author bookkeeping — this is
225
+ what retired `Match.ref` / `Match.refMap` for keys, and it is what makes a
226
+ cross-reference between entities (an `asset`, a `parent`) compare correctly at all.
227
+ - **Entity values carry no `id`** — identity is the key — so entity content compares
228
+ directly, with nothing to omit.
216
229
  - **Float noise** is absorbed by the default `tolerance` (`0.01`), threaded through
217
230
  `match?: { tolerance }`; raise it only when a case needs a looser grid.
218
- - **`Match.ref(label)`** on the expected side asserts id *correspondence* for a
219
- referential feature — a reference that must line up with the entity it points at
220
- (a `selectedId` → a specific todo). Put `ref("t")` on both the reference **and**
221
- that entity's `id`; the labels form a bijection over the actual ids.
222
- `Match.anyNumber` / `anyString` leave an id a case doesn't pin fully open. `ref`
223
- correspondence holds even across a `ReadonlySet` boundary.
231
+ - **A minted scalar** a case does not pin — a timestamp, a random draw — uses
232
+ `Match.anyNumber` / `Match.anyString`. Entity-id correspondence needs no matcher: the
233
+ runner derives it from the schema-marked spec-ids (the solver is internal).
224
234
 
225
235
  ## Recording side effects — built in, no Proxy
226
236
 
@@ -45,7 +45,7 @@ export const playMove = (t: CoreDatabase.Store, { index }: PlayMoveArgs) => {
45
45
  (state only; service effects are asserted through the action). There is **no**
46
46
  `define`/`conforms` adapter and **no** `covers` guard. A transaction taking
47
47
  **entity ids** takes them under the transition's own arg key (`{ id }`); the runner
48
- resolves each `entity(specId)` marker via the id→entity map `fromState` returns. A
48
+ resolves each id the case's `args` schema marks via the id→entity map `fromState` returns. A
49
49
  transaction with **no same-named transition** is infrastructure (`setInput`,
50
50
  `setBounds`) or the drag UI op (`dragTodo`) or system-dispatched — it is simply
51
51
  **skipped**, no guard needed. Don't add a per-item adapter for a renamed op; add a
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adobe-data-ai",
3
- "version": "0.9.100",
3
+ "version": "0.9.102",
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data-ai",
3
- "version": "0.9.100",
3
+ "version": "0.9.102",
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.100"
32
+ "@adobe/data": "0.9.102"
33
33
  },
34
34
  "devDependencies": {
35
35
  "typescript": "^5.8.3",
36
36
  "vitest": "^1.6.0",
37
- "@adobe/data": "0.9.100"
37
+ "@adobe/data": "0.9.102"
38
38
  },
39
39
  "license": "MIT",
40
40
  "scripts": {