Scaffold bevy plugin with bevy-0.19 and bevy-upgrade skills

Add plugin skeleton with versioned skill directories, TODO SKILL.md
scaffolds with goals for skill-creator, and copy over bevy-skill-research
reports from spacecraft into docs/workpad/research/.
This commit is contained in:
2026-07-07 17:52:00 +02:00
parent 8545738a05
commit 325e278af1
13 changed files with 1211 additions and 0 deletions
@@ -0,0 +1,106 @@
# Bevy Skill Research: ECS Core
> **Bevy version**: 0.18, with 0.19 notes
> **Date**: 2026-07-07
## Components
**Derive**: `#[derive(Component)]` on structs/enums. Requires `Send + Sync + 'static`.
**Storage**:
- Table (default) — fast iteration
- `#[component(storage = "SparseSet")]` — fast insert/remove
**Immutable components** (0.18+): `#[component(immutable)]` — restricts to `&T` only, no `&mut T`.
**Required components**: `#[require(B, C)]` — auto-inserts `B` and `C` when this component is added. Supports constructors: `B(value)`, `C { field: val }`, function calls, `= expr`.
**Hooks**: `on_add`, `on_insert`, `on_replace`, `on_remove`, `on_despawn` — set via `#[component(on_add = fn_name)]` or self-referential `#[component(on_add)]`.
**Clone behavior**: `#[component(clone_behavior = Ignore)]` to skip during entity clone.
**Bundles removed** (0.16+): Use `#[require(...)]` or spawn with tuples instead of `#[derive(Bundle)]`.
## Resources
| Type | Purpose |
|------|---------|
| `Res<T>` | Shared read access |
| `ResMut<T>` | Exclusive write access |
| `Option<Res<T>>` / `Option<ResMut<T>>` | Resources that may not exist |
| `Local<'a, T>` | Per-system private state, persists across calls, default-initialized |
| `NonSend<T>` / `NonSendMut<T>` | For `!Send` resources |
World methods: `init_resource::<R>()` (uses `Default`), `insert_resource(value)`, `remove_resource::<R>()`.
**0.19 change**: Resources implement `Component` and live on abstract entities. `Query<Entity>` now matches resource entities — filter with `Without<IsResource>`. Cannot derive both `Component` and `Resource` on one type.
## Systems
**Standard**: any `fn(params...) { }` where each param implements `SystemParam`.
**Fallible** (0.18+): `fn system(params...) -> Result` — bare `Result` (alias for `Result<(), BevyError>`). Errors are logged, system continues on next tick. `?` operator works with any error convertible to `BevyError`.
**Exclusive**: `fn system(world: &mut World) { }` — full world access, blocks parallelism.
**Registration**: `app.add_systems(Update, (sys_a, sys_b))` or `app.add_systems(Startup, setup)`.
### Valid SystemParam Types
`Query`, `Res`, `ResMut`, `Commands`, `Local`, `MessageReader`, `MessageWriter`, `ParamSet`, `Single`, `Populated`, `SystemName`, `SystemChangeTick`, `RemovedComponents<T>`, `DeferredWorld`, `&World`, `()`, `Option<T>`, `Result<T, SystemParamValidationError>`, `PhantomData<T>`.
## Queries
**Signature**: `Query<D, F>` where `D: QueryData`, `F: QueryFilter` (default `()`).
### Data Parameters
- `&T` — read
- `&mut T` — write
- `Entity` — entity ID
- `Has<T>` — returns bool
- Tuples of the above
### Filters
| Filter | Fires when |
|--------|-----------|
| `With<T>` | Entity has component T |
| `Without<T>` | Entity lacks component T |
| `Added<T>` | First tick after component added |
| `Changed<T>` | First tick after add or mut deref |
| `Spawned` | First tick after entity spawn (new) |
| `Or<(A, B)>` | Either filter matches |
### Key Methods
- `.iter()` / `.iter_mut()` — iterate all matching
- `.get(entity)` / `.get_mut(entity)` — single entity lookup
- `.single()` — panics if not exactly one match
- `.par_iter()` / `.par_iter_mut()` — parallel iteration
### Specialized System Params
- `Single<D, F>` — system param alternative to `query.single()`, panics if not exactly one
- `Populated<D, F>` — system skips execution if query is empty
### Custom Derives
- `#[derive(QueryData)]` — for complex query data
- `#[derive(QueryFilter)]` — for reusable filter combinations
## Commands
| Method | Purpose |
|--------|---------|
| `commands.spawn((CompA, CompB))` | Spawn entity with tuple, returns `EntityCommands` |
| `commands.spawn_empty()` | Empty entity, chain `.insert(bundle)` |
| `commands.spawn_batch(iter)` | Batch spawn from iterator |
| `commands.entity(e).insert(comp)` | Add component to existing entity |
| `commands.entity(e).insert_if_new(comp)` | Add only if absent |
| `commands.entity(e).remove::<T>()` | Remove component |
| `commands.entity(e).despawn()` | Destroy entity (auto-recursive with children) |
| `commands.queue(custom_command)` | Push custom `Command` impl |
## System Ordering
- `(a, b).chain()``a` runs before `b`
- `.before(system_or_set)` / `.after(system_or_set)` — explicit ordering
- `#[derive(SystemSet)]` — define named sets for grouping
- `app.configure_sets(Update, SetA.before(SetB))` — order sets
- `AmbiguousSystemConflictsWarning` warns about unordered systems with overlapping access