Skip to content

feat(modules): every module runs in its own collectible load context (live update, slice 1) - #6121

Merged
rbuergi merged 1 commit into
mainfrom
feat/module-live-contexts
Oct 5, 2026
Merged

rbuergi merged 1 commit into
mainfrom
feat/module-live-contexts

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Live module update — slice 1: every module runs in its own collectible load context

Policy module-live-update-default (new register row): a module update goes live in the running process by default; a restart only when a module declares restartRequired or the live swap fails at runtime (then automatic, no approval). Manual: Doc/Architecture/LiveModuleUpdate (new).

This slice is the foundation — the contexts. It does not yet change the update path (the swap + its runtime fallback is slice 2; the Plugins module classification / restartRequired declarations / guard is slice 3).

What changes

  • ModuleLoadContext — collectible, one per module generation (module:<Name>#n). Resolution: platform first (any name the default context can bind — one copy of every platform contract; a bundled platform copy is never used) → another module's current generation (edge recorded as DependsOn) → the generation's own directory. Natives via the ModuleNativeAssets candidates. IPlatformLoadContext (impersonation guard treats module code as platform). Purges Autofac + System.Text.Json static caches on Unloading.
  • ModuleContexts — the mesh's registry singleton: Load / Commit / Retire (unload on a positive AlcLeaseRegistry quiescence signal, never a timer; tracked on CollectibleContextUnloads), Resolve, DependentsOf; disposed with the mesh.
  • MeshBuilder.InstallModules — every module not in TRUSTED_PLATFORM_ASSEMBLIES loads into its own context and is committed only after its contributions materialise. A failed generation is unloaded and never takes the name, so the previous generation / image copy remain reachable (If no module version loads on this platform, the instance keeps running the previous generation #3649/A refused module generation is loaded anyway when it has no previous generation — the image baseline is never used as the fallback, and nothing in the log says so #3735). Image-bound modules load exactly as before.
  • NodeAssemblyLoadContext and the kernel ScriptSession bind modules through ModuleContexts.Resolve (current generation) — no Default.Resolving handler ever hands out a module (it would pin the first generation forever).
  • JsonMemberAccessorCacheEviction moves (internal → public) to MeshWeaver.Mesh.Contract.

🚨 Behaviour change behind an unchanged signature (shape 7)

On the portal, every landed / seeded module that is not in the image's TPA — notably the MeshModuleClosure seeds MeshWeaver.AI, MeshWeaver.Blazor.Chat, MeshWeaver.Markdown.Collaboration, MeshWeaver.Mcp — now runs in a collectible context instead of the default one. Three existing tests in ConfiguredModuleActivationTest pinned default-context-only limitations and were rewritten to the new truth (a substitution can no longer happen to a context-loaded module; a generation that loaded-then-failed now falls back to the image copy instead of stranding the module).

Not established here: a portal-level boot (Plugins Memex.Portal.*) with modules in their own contexts — Blazor component types and Orleans payloads from a collectible context were not exercised. Recycle after deploy: none needed for this slice beyond the normal roll (load path changes at boot).

Tests (local, Release)

  • ModulesRunInTheirOwnContextTest (new) — 7/7: own collectible context + one platform; image-bound classifier; two generations coexist and the retired one is really collected; negative control: a held instance → reported RETAINED by context name (this control caught a first sentinel that read "collected" the moment the unload started); dependent binds its dependency's current generation and is recorded; a generation whose contributions throw leaves the serving one current; a NodeType compiled against N+1's member fails on N (MissingMember — the 2026-10-05 incident shape) and binds once N+1 is current.
  • MeshWeaver.Compiler.Pipeline.Test full run before the rewrite: 1192/1195 (the 3 rewritten); after: ConfiguredModuleActivationTest + new class 51/51.
  • Memex.Portal.Shared.Test (Module* / ConfigurationHandOver / SelfUpdatePendingRestart): 386/386.
  • MeshWeaver.Graph.Test (Teardown / Collectible / Module): 95/95.
  • Release -warnaserror: Mesh.Contract, Kernel.Hub, Compiler.Pipeline, Documentation, Compiler.Pipeline.Test, Graph.Test, Memex.Portal.Shared.Test — 0 warnings, 0 errors.

Pairs-with: none — no public type or member is removed (JsonMemberAccessorCacheEviction was internal).
Implementers: none — no interface member added.
Mirror-sync: none — no i18n key added or changed.

🤖 Generated with Claude Code

…(live update, slice 1)

Policy module-live-update-default: a module update goes live in the running process
by default. This slice lands the foundation — the load contexts:

- ModuleLoadContext: collectible context per module generation; platform first
  (one copy of every platform contract), then another module's current generation
  (dependency edges recorded), then the generation's own directory; natives via the
  ModuleNativeAssets candidates; IPlatformLoadContext; Autofac + STJ cache eviction.
- ModuleContexts: the mesh's registry — Load / Commit / Retire (unload on a positive
  quiescence signal, tracked on CollectibleContextUnloads), Resolve, DependentsOf;
  disposed with the mesh.
- MeshBuilder.InstallModules loads every module the image does not bind
  (TRUSTED_PLATFORM_ASSEMBLIES) into its own context and commits it only after its
  contributions materialised; a failed generation is unloaded and never takes the
  name, so the previous generation / image copy stay reachable.
- NodeType contexts and kernel script sessions bind modules through
  ModuleContexts.Resolve (the current generation).
- JsonMemberAccessorCacheEviction moves to MeshWeaver.Mesh.Contract (public) so a
  module context can use it.

Doc: Architecture/LiveModuleUpdate; policy row module-live-update-default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@meshweaver-cloud
meshweaver-cloud Bot enabled auto-merge October 5, 2026 05:56
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    17 files      17 suites   42m 4s ⏱️
10 333 tests 10 333 ✅ 0 💤 0 ❌
10 346 runs  10 346 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

  3 files    3 suites   3m 43s ⏱️
405 tests 405 ✅ 0 💤 0 ❌
409 runs  409 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

    3 files      3 suites   3m 23s ⏱️
1 987 tests 1 987 ✅ 0 💤 0 ❌
1 988 runs  1 988 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 629 tests   1 629 ✅  6m 34s ⏱️
    2 suites      0 💤
    2 files        0 ❌

Results for commit 90a54d5.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    2 files      2 suites   7m 9s ⏱️
1 098 tests 1 098 ✅ 0 💤 0 ❌
1 099 runs  1 099 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

    3 files      3 suites   9m 38s ⏱️
2 803 tests 2 803 ✅ 0 💤 0 ❌
2 806 runs  2 806 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

    4 files      4 suites   11m 35s ⏱️
2 411 tests 2 411 ✅ 0 💤 0 ❌
2 415 runs  2 415 ✅ 0 💤 0 ❌

Results for commit 90a54d5.

@rbuergi
rbuergi merged commit cab1f4c into main Oct 5, 2026
62 of 66 checks passed
@rbuergi
rbuergi deleted the feat/module-live-contexts branch October 10, 2026 13:10
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