The centerpiece of the whole "no domain reload" claim, now proven
empirically rather than argued on paper: 200 load/unload cycles against
a real plugin (sandbox.echo), each one checked with a WeakReference
that the collectible ALC actually collected — docs/kernel-contract.md
§4's leak test, previously Skip-marked since the very first scaffold,
now runs and passes (stable across repeated runs).
New pieces, minimal by design:
- PluginLoadContext: the collectible ALC a plugin's implementation
loads into. Load() defers to whatever's already in the Default ALC
(Engine.Kernel, the plugin's own Contracts assembly) before
consulting AssemblyDependencyResolver for genuinely private
dependencies — the standard .NET plugin pattern, needed so component
types stay identical across the plugin boundary instead of loading
as two distinct, incompatible copies.
- PluginHost: reads plugin.json, loads Contracts into the Default ALC
(once — verified directly, not just inferred from the leak test),
loads the implementation into a fresh PluginLoadContext, finds the
IPlugin type via reflection, calls Configure(). Unload() calls
Shutdown() first, then .Unload()s the ALC and hands back a
WeakReference for the caller to check.
- Schedule, ServiceRegistry, NullEventBus, ConsoleLogger: minimal real
implementations of the remaining IPluginContext pieces — no stage
execution or parallelism in Schedule yet, that's separate Scheduler
work. Schedule.RemoveAllFrom is the one piece that has to be
correct now, not later: it's what lets a plugin's Shutdown() actually
drop the delegate reference into its own collectible ALC, which is
exactly what the leak test is checking end to end.
Explicitly out of scope for this pass: resolving a project's or
plugin's dependsOn graph to order loading across multiple plugins.
Nothing to test that against yet — sandbox.echo is deliberately the
only, dependency-free fixture. Noted as a TODO on PluginHost rather
than built speculatively.
Sandbox.Echo.csproj gets <EnableDynamicLoading>true</EnableDynamicLoading>
(future plugins with real dependencies will need the deps.json this
generates). Engine.ConformanceHarness.csproj now copies plugin.json and
both built DLLs into one flat directory under its own output, matching
the layout PluginHost.Load(directory) expects — via $(Configuration)/
$(TargetFramework)-aware CopyToOutputDirectory items, correct in
Release too, not just Debug.
17 tests total now (13 in Engine.Kernel.Tests, 4 in
Engine.ConformanceHarness), all passing on a clean build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N