First real piece of M0 rather than scaffolding. GameWorld : IWorld owns
GameObject creation/destruction, a type-indexed Dictionary<Type,
HashSet<GameObject>> backing Query<T>(), and hierarchy bookkeeping
(roots list, parent/children, cycle rejection on SetParent).
Two corrections to the design doc found while implementing:
- WorldMatrix can't be cached on Transform as described — Transform is
a plain struct with no reference to the hierarchy it would need to
compose against. Moved to GameObject.WorldMatrix, computed from the
parent chain on read; docs/kernel-contract.md and the §3 example
updated (go.Transform.WorldMatrix -> go.WorldMatrix).
- The concrete World class collided with its own containing namespace
(Engine.Kernel.World.World), which makes the bare name ambiguous for
every consumer. Renamed to GameWorld; IWorld and the World folder/
namespace are unaffected, and the doc never named the concrete class
either way, so nothing there needed to change.
Also fixed a real correctness bug caught while writing World.Destroy:
a GameObject can carry more than one component of the same type, so
removing one from the type index has to check whether any others of
that type remain before dropping the GameObject from the index set —
tested directly (Query_Still_Finds_A_GameObject_With_A_Duplicate_
Component_After_One_Removal).
12 tests in Engine.Kernel.Tests cover creation, hierarchy (including
cycle rejection), component add/remove/query, destroy cascading
through a subtree, and WorldMatrix composition (verified against a
worked-through parent+child translation, not just asserted).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N
Buildable skeleton matching docs/kernel-contract.md:
- src/Engine.Kernel — the frozen kernel. Interfaces and data types only
(World, Component, GameObject, Transform, ISchedule, IServiceRegistry,
IEventBus, ILogger, IPlugin/IPluginContext, plugin.json and project.json
manifest models). No PluginHost/Scheduler/World implementation yet —
that's M0's actual work, not scaffolding.
- src/Engine.Host — the CLI runtime entry point, placeholder for now.
- plugins/sandbox.echo — a minimal two-assembly plugin (Contracts in
Default ALC, implementation in collectible ALC) that exists only to
exercise the reload loop end to end once PluginHost exists.
- tests/Engine.Kernel.Tests, tests/Engine.ConformanceHarness — wired up
with one Skip-marked placeholder test each, naming what M0 needs to
make them real (including the 200-cycle ALC leak test from §4). The
harness references the sandbox plugin with
ReferenceOutputAssembly="false" so it loads it dynamically by path
instead of linking its types into its own Default ALC.
- samples/EmptyProject — a starter project.json.
- Directory.Build.props centralizes shared settings across every
project, including AllowUnsafeBlocks=false to enforce the §7 rule at
the build level rather than by convention.
Solution builds clean and `dotnet test` runs both placeholders as
Skipped (not failing) — confirms the wiring, not the engine.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N