You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Apply AssemblyLoadContext isolation to each plugin and wire tools/PluginContractValidator into the loading flow then extend the validator with rules covering the new contract hooks so invalid or conflicting plugins fail at startup, not at runtime.
Goal
Prevent dependency conflicts between plugins and the host via per plugin load contexts, and make contract validation an automatic, fully covered part of loading.
Background
With single shared load context, conflicting transitive dependencies between plugins cause TypeLoadException, ambiguous types, or silent wrong version binds. Separately, the validator today is separate tool the loader does not run, so invalid plugins are only caught when manually validated or when they blow up at runtime. Isolation and validation are applied by the loader after discovery and after the manifest is read.
Scope
G4. AssemblyLoadContext isolation
The loader creates one AssemblyLoadContext per plugin and records it on DiscoveredPlugin.
G5. Validator integration
The loader invokes PluginContractValidator on each discovered plugin as part of loading.
G6. Validator hooks coverage
Add validation rules for the new contract members introduced by other sections.
Architecture
flowchart TD
Disc["DiscoveredPlugin<br/>Manifest + Location"] --> Gate{"Compatibility Gate<br/>G3"}
Gate -- accepted --> ALC["Isolated Load G4<br/>new AssemblyLoadContext<br/>per plugin IsCollectible"]
ALC --> LP["LoadedPlugin<br/>Manifest + Instance + ALC"]
LP --> Val["PluginContractValidator G5<br/>runs automatically"]
Val -- fail --> Rej["REJECT<br/>startup error"]
Val -- pass --> Cov["Coverage G6<br/>G1/G2/B/C/D/F hooks"]
Cov --> Act["ACTIVATION"]
Host["Host / framework<br/>shared - not duplicated"] -. shared .-> ALC
classDef gate fill:#fef3c7,stroke:#f59e0b,color:#92400e
classDef iso fill:#dbeafe,stroke:#3b82f6,color:#1e40af
classDef reject fill:#fee2e2,stroke:#ef4444,color:#991b1b
classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d
class Gate,Val,Cov gate
class ALC,LP iso
class Rej reject
class Act ok
Loading
flowchart TD
LP4["LoadedPlugin"] --> ALC2["AssemblyLoadContext<br/>isolated runtime"]
ALC2 --> Priv["plugin private deps<br/>isolated"]
ALC2 -. shared .-> Shared["Host assemblies<br/>shared"]
Priv --> Inst["Instance"]
Inst --> Act2["ACTIVATION"]
classDef iso fill:#dbeafe,stroke:#3b82f6,color:#1e40af
class ALC2,Priv iso
class Shared fill:#f3f4f6,stroke:#9ca3af,color:#374151
Loading
Proposed Contract
G4: the loader creates one AssemblyLoadContext per plugin (preferably IsCollectible to support unload/hot-reload). Host assemblies remain shared only plugin private dependencies are isolated. The context is created during the isolated load and attached to LoadedPlugin (the runtime model from G1), not to DiscoveredPlugin, DiscoveredPlugin stays discovery only (Manifest + Location).
G5: the loade invokes PluginContractValidator on each loaded plugin/type after isolation. On failure it raises startup error (the basic gate is hard reject Strict/Warn/Ignore policy engine is separate host feature, not part of this contract).
G6: add validation rules for the presence/signatures of the new hooks (loader/discoverer interfaces, manifest shape, lifecycle hooks from B, middleware models from C, health result from D, security scheme extensions from F). Each rule reports the specific missing/invalid member.
Requirements
G4. Isolation
Each plugin loads into its own AssemblyLoadContext.
Host/framework assemblies are shared, not duplicated per plugin.
Only plugin private dependencies are isolated.
The AssemblyLoadContext is attached to LoadedPlugin produced by IPluginLoader.LoadAsync.
Contexts are IsCollectible where possible to enable hot reload (K12).
G5. Integration
The validator runs automatically as part of loading no separate manual step required for the host to be safe.
On failure: startup error (the basic gate is hard reject Strict/Warn/Ignore is later host policy).
Validation happens after isolation and before activation.
Validation rules themselves are extended by G6
G6. Coverage
The validator covers every new contract member end to end.
A plugin missing or mis signing new hook is reported with the specific member name.
Rules are added for: loader/discoverer interfaces, manifest shape, B lifecycle hooks, C middleware models, D health result, F security scheme extensions.
Each rule reports the specific missing/invalid member (no silent passes).
Backward Compatibility
Plugins that do not declare load context continue to load via the existing mechanism isolation is applied by the new loader path for plugins that opt in via manifest Plugins that already pass the validator are unaffected. No contract members are changed by G6 only validation is added.
Validation
G4. Isolation
Each plugin loads into its own AssemblyLoadContext.
No type collisions occur between plugins that ship conflicting dependency versions.
Host types remain shared (not duplicated per plugin).
DevTokens still loads after the contract change.
G5. Integration
A non conforming plugin results in startup error (hard reject).
The validator runs automatically as part of loading.
DevTokens passes validation after the contract change.
G6. Coverage
The validator covers every new contract member end to end (full contract coverage).
A plugin missing or mis signing new hook is reported with the specific member name.
Rules exist for G1, G2, B, C, D, and F hooks.
Acceptance Criteria
Each plugin loads into its own AssemblyLoadContext.
No type collisions occur between plugins that ship conflicting dependency versions.
Host types remain shared (not duplicated per plugin).
A non conforming plugin results in startup error (or warning when configured).
The validator runs automatically as part of loading no separate manual step required for the host to be safe.
The validator covers every new contract member end to end (full contract coverage).
A plugin missing or mis signing new hook is reported with the specific member name.
Non Goals
G4: no isolation of host/framework assemblies. Unload/hot reload lifecycle is enabled by collectible contexts but the reload orchestration itself i later concern/
G5: no change to validator internals beyond being invoked by the loader. No new validation rules (those belong to G6).
Isolation (G4): each plugin loads into its own collectible AssemblyLoadContext. Assemblies already loaded by default stay shared (returned as null to fall back); only plugin-private dependencies resolve from the plugin directory. The context is attached to the contract LoadedPlugin, never to DiscoveredPlugin. Verified: separate contexts and diverging plugin types across loads, shared IAuthKitPlugin identity, and a real DevTokens assembly loading isolated.
Validation (G5): the host validator runs automatically in the pipeline after isolation, before activation. A violation rejects that plugin as Invalid in PluginLoadResult with an error log, and the host…
Summary
Apply
AssemblyLoadContextisolation to each plugin and wiretools/PluginContractValidatorinto the loading flow then extend the validator with rules covering the new contract hooks so invalid or conflicting plugins fail at startup, not at runtime.Goal
Prevent dependency conflicts between plugins and the host via per plugin load contexts, and make contract validation an automatic, fully covered part of loading.
Background
With single shared load context, conflicting transitive dependencies between plugins cause
TypeLoadException, ambiguous types, or silent wrong version binds. Separately, the validator today is separate tool the loader does not run, so invalid plugins are only caught when manually validated or when they blow up at runtime. Isolation and validation are applied by the loader after discovery and after the manifest is read.Scope
G4.
AssemblyLoadContextisolationThe loader creates one
AssemblyLoadContextper plugin and records it onDiscoveredPlugin.G5. Validator integration
The loader invokes
PluginContractValidatoron each discovered plugin as part of loading.G6. Validator hooks coverage
Add validation rules for the new contract members introduced by other sections.
Architecture
flowchart TD Disc["DiscoveredPlugin<br/>Manifest + Location"] --> Gate{"Compatibility Gate<br/>G3"} Gate -- accepted --> ALC["Isolated Load G4<br/>new AssemblyLoadContext<br/>per plugin IsCollectible"] ALC --> LP["LoadedPlugin<br/>Manifest + Instance + ALC"] LP --> Val["PluginContractValidator G5<br/>runs automatically"] Val -- fail --> Rej["REJECT<br/>startup error"] Val -- pass --> Cov["Coverage G6<br/>G1/G2/B/C/D/F hooks"] Cov --> Act["ACTIVATION"] Host["Host / framework<br/>shared - not duplicated"] -. shared .-> ALC classDef gate fill:#fef3c7,stroke:#f59e0b,color:#92400e classDef iso fill:#dbeafe,stroke:#3b82f6,color:#1e40af classDef reject fill:#fee2e2,stroke:#ef4444,color:#991b1b classDef ok fill:#dcfce7,stroke:#22c55e,color:#14532d class Gate,Val,Cov gate class ALC,LP iso class Rej reject class Act okflowchart TD LP4["LoadedPlugin"] --> ALC2["AssemblyLoadContext<br/>isolated runtime"] ALC2 --> Priv["plugin private deps<br/>isolated"] ALC2 -. shared .-> Shared["Host assemblies<br/>shared"] Priv --> Inst["Instance"] Inst --> Act2["ACTIVATION"] classDef iso fill:#dbeafe,stroke:#3b82f6,color:#1e40af class ALC2,Priv iso class Shared fill:#f3f4f6,stroke:#9ca3af,color:#374151Proposed Contract
AssemblyLoadContextper plugin (preferablyIsCollectibleto support unload/hot-reload). Host assemblies remain shared only plugin private dependencies are isolated. The context is created during the isolated load and attached toLoadedPlugin(the runtime model from G1), not toDiscoveredPlugin,DiscoveredPluginstays discovery only (Manifest + Location).PluginContractValidatoron each loaded plugin/type after isolation. On failure it raises startup error (the basic gate is hard rejectStrict/Warn/Ignorepolicy engine is separate host feature, not part of this contract).Requirements
G4. Isolation
AssemblyLoadContext.AssemblyLoadContextis attached toLoadedPluginproduced byIPluginLoader.LoadAsync.IsCollectiblewhere possible to enable hot reload (K12).G5. Integration
Strict/Warn/Ignoreis later host policy).G6. Coverage
Backward Compatibility
Plugins that do not declare load context continue to load via the existing mechanism isolation is applied by the new loader path for plugins that opt in via manifest Plugins that already pass the validator are unaffected. No contract members are changed by G6 only validation is added.
Validation
G4. Isolation
AssemblyLoadContext.DevTokensstill loads after the contract change.G5. Integration
DevTokenspasses validation after the contract change.G6. Coverage
Acceptance Criteria
AssemblyLoadContext.Non Goals
Update: G4–G6 implementation notes.
Isolation (G4): each plugin loads into its own collectible AssemblyLoadContext. Assemblies already loaded by default stay shared (returned as null to fall back); only plugin-private dependencies resolve from the plugin directory. The context is attached to the contract LoadedPlugin, never to DiscoveredPlugin. Verified: separate contexts and diverging plugin types across loads, shared IAuthKitPlugin identity, and a real DevTokens assembly loading isolated.
Validation (G5): the host validator runs automatically in the pipeline after isolation, before activation. A violation rejects that plugin as Invalid in PluginLoadResult with an error log, and the host…