Skip to content

Layered feature architecture: spec/ecs conformance, per-system checks, samples ported - #210

Merged
krisnye merged 24 commits into
mainfrom
krisnye/feature-components
Sep 30, 2026
Merged

krisnye merged 24 commits into
mainfrom
krisnye/feature-components

Conversation

@krisnye

@krisnye krisnye commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

A layered feature architecture for apps built on @adobe/data, the conformance library to support it, all seven feature-based samples ported to it, and the data-ai guidance rewritten to match.

Layers: data/ (values, components, resources, entities) → services/ → spec/ (test tier) → ecs/ (core → indexes → transactions → services → computed → actions → systems) → ui/ → app/. Each layer has its own tsconfig project, and no runtime code imports spec/.

@adobe/data

  • Scope flag constants for declaring each component's or resource's scope in its own file.
  • Database.EntityComponents / Database.EntityType: entities declared as component tuples, with derived value types.
  • Database.archetypes accepts the nonPersistent / nonShared marker components.

@adobe/data-testing

  • Conformance.spec (pure; no ECS is built) and Conformance.implementation replace Conformance.feature, with checkSpec / checkFeature.
  • Spec actions pair with same-named actions, and derivations with computeds. A spec op with no implementation fails by name.
  • Per-system conformance: a spec systems group pairs a pure function with each ECS system, and each system is checked by running it alone. Frame cases fold the systems in the order derived from their schedule declarations and also drive one real frame. unmodelled lists systems the spec can't model (wasm, init-only), checked at compile time. noOp lists states every system must leave unchanged.
  • Strict recording doubles: calling a value method with no scheduled response fails the case.
  • StateMatches pin; base fake services for services the app must inject.

Samples

  • Every feature-based sample is ported to the new layout: todo, tictactoe, p2p-tictactoe, space-rock, hopper, pixie and solid-dashboard.
  • In todo, assign gains a spec, actions and conformance, and app/ composes the features.

Fixes

  • Todo lost every todo on reload.
  • Todo ops accepted another feature's entity id.
  • Tictactoe's current player didn't update after a restart.
  • ecs.md referenced a schema flag that doesn't exist.

data-ai

  • The rules mirror the new tree, the build skills follow the new order, and there is a new build-spec step.

Testing

  • pnpm run lint and pnpm run typecheck pass; the typecheck also passes with build caches cleared.
  • All tests pass in data (3,483), data-testing and every sample.
  • Every sample builds with Vite.
  • The todo app was smoke-tested in a browser, including a reload that keeps an assignment.

🤖 Generated with Claude Code

krisnye and others added 24 commits September 29, 2026 17:44
….implementation; add Scope, entity types

- Conformance.spec (pure, reads reference marks from schemas; no plugin)
- Conformance.implementation pairs a spec with its ECS build; checkFeature
  conforms spec ops against same-named actions (transaction fallback for
  system-dispatched ops) and fails on a spec op with no implementation
- base services injected into every conformance db
- Conformance.StateMatches pin type
- @adobe/data: Scope flag constants, Database.EntityComponents/EntityType,
  Database.archetypes accepts the built-in marker components

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
data/ (values, components, resources, entities) → services/ → spec/ (test
tier) → ecs/ → ui/ → app/. Per-file components/resources with scope flags;
entities declared as component tuples; State pinned with StateMatches;
Conformance.spec + Conformance.implementation; app/ composes feature schemas
and owns versioning. assign gains a spec, actions and conformance, owns its
assignees column, and renders its own assignee chips. Layer tsconfigs:
data, spec, ecs, test.

data-testing: spec transitions pair with same-named actions; transactionOps
opts a system-dispatched op into transaction pairing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Position into Todo

A nonPersistent column with no real default is stripped on load, so every
reloaded todo fell out of the Todo archetype (and out of every Todo query).
The drag transaction now adds dragPosition on demand. Adds a persistence
round-trip regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
State holds placed marks (entities) with the board as a derivation; fixes
currentPlayer not reacting to firstPlayer after a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng one system frame

Real-time features keep in-place column-writing systems; checkFeature seeds a
system db, applies the case args via setup, runs every system once, and
compares. Adds a frame conformance test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frog is a resource; hop is a real action; per-frame systems keep in-place
column writes and step conforms via frame conformance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rally, never importing entities

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ship is a resource; shared logic moved from the spec into data/values
helpers (Wave.ring, Bullet.fire, Lives.isGameOver); per-frame systems keep
in-place column writes and step conforms via frame conformance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en ops

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ties

toggleComplete/deleteTodo/selectTodo read through the Todo archetype, so a
user id changes nothing. Adds a cross-feature regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rules mirror the new tree: data/ (values, components, resources, entities),
services/, spec/ (test tier), ecs/ (core → indexes → transactions → computed
→ services → actions → systems, conformance), ui/, and app.md for the
composition root. Build skills follow the new order, with a new build-spec
step. Relaxed tag guidance (a kind is its component set); per-file scope
flags; frame conformance for real-time features; persistence and
cross-feature-id pitfalls. Fixes stale ecs.md guidance (a nonexistent
ephemeral flag, unsafe null casts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ecs.md / plugin-modelling.md / service.md defer to the feature rules for
  layout and naming inside features/
- presentations may call sibling lazy wrappers; the cross-feature UI seam
  points at lazy-element.md
- systems examples extend ActionDatabase; headless frame recipe corrected
- globs: data-modelling, archetypes, .tsx UI files, **/src prefixes
- element.md delegation matches presentation.render; stale layer names fixed
- versioning.md fence and cross-reference fixed
- ship the real-time samples and the layer tsconfigs as references
- stale sample comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
core → indexes → transactions → services → computed → actions → systems, so
computeds may derive from service observables and service factories never
read computeds. tictactoe's agent derives its observables via a shared
readBoard reader. Rules and build skills follow; systems conformance text
spells out how a frame case runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ule order

A spec's systems group pairs pure functions with same-named ECS systems. Each
system conforms by running just that system; each frame case folds the spec
systems in db.system.order and drives one real frame (awaiting async
systems). frame: { args, unmodelled } replaces { op, setup }: one writer per
frame data arg, and every ECS system is modelled xor declared unmodelled
(checked at compile time), so wasm or init-only systems are skipped and
listed. Systems sharing a tier must commute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rules

- data-testing: every frame run and the tier-commute check get fresh
  recording doubles (a frame case with service responses no longer drains
  one run's schedule in the other); a no-arg system adds nothing to FrameArgs
- data-gpu-hopper: spec systems movement + collision mirror the ECS systems;
  step removed; outcome-selection tests became collision cases
- data-ai: systems/conformance/spec rules and skills describe per-system and
  frame conformance, schedule-derived order, and unmodelled systems

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec systems control/movement/lifetime/collision/waves mirror the ECS
systems; step and the second helper manifest removed; collision-detection
and waves tests became per-system cases; frame cases include an
order-dependent refill (collision before waves).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… noOp cases

- a value-returning service method called with no response scheduled throws
  (one probe call per fake method tells value from void), so a case owns every
  value its code reads
- whole-frame cases move to Conformance.implementation's frame.cases: they
  depend on the ECS's schedule, so the spec stays purely per-system
- systems.noOp: states every modelled system must leave unchanged (e.g. game
  over), appended to each system's cases on both the spec and ECS side

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p game-over case

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, noOp game-over states; rules

Two noOp states (live field, cleared field) replace every per-system
game-over case; the strict doubles make effects: { random: [] } redundant.
data-ai: spec/conformance/systems rules and skills describe noOp, frame
cases in ecs/conformance, and strict response schedules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@krisnye
krisnye merged commit 0eec7de into main Sep 30, 2026
3 checks passed
@krisnye
krisnye deleted the krisnye/feature-components branch September 30, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant