Skip to content

Modules__Root could not reach a container — the chart never rendered it - #1924

Merged
sierragolflima merged 4 commits into
mainfrom
fix/modules-root-reaches-the-configmap
Aug 20, 2026
Merged

sierragolflima merged 4 commits into
mainfrom
fix/modules-root-reaches-the-configmap

Conversation

@sierragolflima

@sierragolflima sierragolflima commented Aug 20, 2026 •

Copy link
Copy Markdown
Collaborator

The defect

A module that ships in the image is not automatically active. /mcp and every other module route is mapped by MapMeshModuleEndpoints from Modules:Assemblies, which is the appsettings baseline ∪ the store-activation sidecar. Both the sidecar and the landed DLLs are written under Modules:Root.

Unset, that root is AppContext.BaseDirectory — /app — which is read-only in the container. #1891 established exactly this and made the key configurable so a deployment could point it at its shared writable volume. Memex#53 then set config.memex_portal.Modules__Root: /data in all three AKS values files, titled "Modules__Root belongs in the chart — that is why it kept vanishing."

It kept vanishing because the chart does not render it. memex-portal-config names every key explicitly, there is no catch-all range over config.memex_portal, and the Deployment's only env path is envFrom on that ConfigMap plus the Secret. A values key this template omits produces exactly nothing, with no error anywhere — the failure mode deploy/aks documents as "un-templated chart keys are silently dropped". Three values files set the key; no container ever saw it.

Verified on a live deployment, before the fix

ConfigMap                          zero Modules* keys
/app/modules                       read-only  (touch → Permission denied)
/data                              writable   (the mounted hostPath/PVC)
/data/modules/activation.json      absent
/app/modules/MeshWeaver.Mcp/…dll   present, unused
POST /mcp                          404

…and after, on the same portal: Modules__Root=/data reaches the process, the sidecar is read, the entry passes its platform floor (module built against MeshWeaver.Graph/3.0.0-rc4.ci.0, platform 3.0.0-rc5.ci.0), MeshWeaver.Mcp.dll is mapped into the process (/proc/1/maps), and POST /mcp → 401 with a proper www-authenticate: Bearer resource_metadata=… challenge. An MCP client reconnects and serves real calls.

What changed

  1. deploy/helm/templates/memex-portal/config.yaml — templates Modules__Root, defaulting to Deployment__DataRoot rather than emitting a blank. Blank is not harmless: ModuleRoot treats it as absent and falls back to the read-only /app, silently restoring the defect the key exists to cure. An explicit value still wins.
  2. deploy/helm/values.yaml — neutral default "" (follow the data root).
  3. deploy/homebrew/share/values.local.defaults.yaml — pins /data for memex-local, so a laptop install gets a writable root without regenerating its user overlay, and a landed module survives memex-local update.
  4. deploy/aks/scripts/check-chart-invariants.py — invariant 8, so the next key cannot vanish the same way.

The gate, written test-first

check-chart-invariants exists because replicas.portal: 2 sat in git for a month being consumed by nothing. This is that defect in the other direction, so the rule is pinned rather than just the instance.

Red, against main's own chart:

::error::[main, unfixed] the rendered memex-portal-config carries no 'Modules__Root'
1 contradiction(s) across 7 invariants — exit 1

Green on this branch, all three shipped combinations:

self-host (neutral chart defaults) — self-consistent
AKS overlay                        — self-consistent
memex-local (Colima k3s)           — self-consistent

Negative control: forcing both Modules__Root and Deployment__DataRoot blank fails on the blank arm, not the missing arm — the two halves of the assertion are independently live rather than one covering for the other.

Risk

Low, and inert on merge. No workflow applies this chart: main-cd.yml builds and publishes images and never touches helm; the only helm-aware workflow is chart-drift.yml, a scheduled read-only drift detector. The chart takes effect when someone deliberately deploys.

On AKS the key currently resolves to read-only /app, so nothing can be store-activated today. After this it resolves to /data, which those pods already mount — and it stays inert until a module is actually activated. It cannot change existing behaviour, only unblock intended behaviour.

Checks

deploy/aks/scripts/check-chart-invariants.sh   all 3 combinations, exit 0
helm template … | grep Modules__Root           neutral /data · AKS /data · local /data
                                               --set /mnt/modules → honoured
dotnet build test/MeshWeaver.Documentation.Test -c Release -warnaserror   0 Warning(s) 0 Error(s)
dotnet test  test/MeshWeaver.Documentation.Test (repo-file guards)        15/15 passed

Found while doing this — filed separately, NOT in this PR

Widening that guard from PreWarm__* to every config.memex_portal key surfaces three more keys set in deploy/aks/values.aks.yaml that no template renders: SelfUpdate__MinRollInterval, WebhookInbox__Targets__0, AzureFoundry__Models__3. Templating them would change live AKS behaviour, and two are recent deliberate work, so they belong to their owner rather than to this PR. Issue: #1925.

🤖 Generated with Claude Code

sierragolflima and others added 4 commits August 20, 2026 10:09
A module that ships in the image is not automatically active: /mcp and every
other module route is mapped by MapMeshModuleEndpoints from Modules:Assemblies,
which is the appsettings baseline UNION the store-activation sidecar. Both the
sidecar and the landed DLLs are written under Modules:Root.

Unset, that root is AppContext.BaseDirectory — /app — which is READ-ONLY in the
container. #1891 established that and made the key configurable so a deployment
could point it at its shared writable volume. Memex#53 then set
`config.memex_portal.Modules__Root: /data` in all three AKS values files, with
the commit message "Modules__Root belongs in the chart — that is why it kept
vanishing".

It kept vanishing because the chart does not render it. memex-portal-config
names every key explicitly, there is no catch-all range over
config.memex_portal, and the Deployment's only env path is envFrom on that
ConfigMap plus the secret. So a values key this template omits produces exactly
nothing, with no error anywhere — the failure mode deploy/aks documents as
"un-templated chart keys are silently dropped". Three values files set the key;
no container ever saw it.

Verified on the live local deployment before the fix: the ConfigMap carries
zero Modules* keys, /app/modules is read-only, /data is writable, no
modules/activation.json exists, and MeshWeaver.Mcp.dll sits unused in the image
while /mcp answers 404.

So template it, defaulting to Deployment__DataRoot — the shared writable volume
by construction — rather than emitting a blank, which ModuleRoot reads as absent
and resolves straight back to the read-only /app. An explicit value still wins.

  helm template … | grep Modules__Root
    neutral defaults → /data      AKS overlay → /data
    memex-local      → /data      --set /mnt/modules → /mnt/modules
  deploy/aks/scripts/check-chart-invariants.sh → all 3 combinations consistent

memex-local additionally pins it in values.local.defaults.yaml: a laptop install
gets the writable root without regenerating its user overlay, and a module
landed there survives `memex-local update`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The chart fix alone would leave the next key to vanish the same way, so pin the
rule that was missing rather than only the instance that broke.

check-chart-invariants exists because `replicas.portal: 2` sat in git for a
month being consumed by nothing. Modules__Root is that defect in the other
direction, and this ConfigMap invites it: the template names every key
explicitly and the Deployment's only env path is envFrom on it, so a key the
template omits reaches no container. helm reports success either way — the
values file is syntactically perfect and the template simply never mentions it.

Invariant 8 asserts that every key the platform requires a deployment to be
able to set is RENDERED, and renders non-blank. Blank matters as much as absent
whenever the consumer treats blank as unset: ModuleRoot does, falling back to
the read-only /app, so an empty string silently restores the defect the key
exists to cure.

Red first, against main's own chart:

  ::error::[main, unfixed] the rendered memex-portal-config carries no 'Modules__Root'
  1 contradiction(s) across 7 invariants — exit 1

Green on this branch, all three shipped combinations (self-host, AKS overlay,
memex-local). Negative control: forcing both Modules__Root and
Deployment__DataRoot blank fails on the BLANK arm, not the missing one — so the
two halves of the assertion are independently live rather than one covering for
the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Category: Fix — a user notices this one. Anything shipping as a module was
absent from a deployment that never set the key, and the MCP server is the
visible case: its address answered "not found" on a portal that was healthy in
every other respect.

Written for the reader, not the mechanism: what was missing, why nothing said
so, and what changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Helm-chart configuration omission that prevented Modules__Root from reaching the running memex-portal container, which in turn blocked store-installed modules (e.g., MCP) from being activated because the default module root resolves to a read-only location inside the container.

Changes:

  • Template Modules__Root into the memex-portal-config ConfigMap, defaulting it to Deployment__DataRoot when unset/blank.
  • Add chart/homebrew defaults so local installs get a writable module root without regenerating overlays.
  • Extend check-chart-invariants with a new invariant that asserts required config keys are rendered and non-blank.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-20-installed-modules-can-be-activated.md Adds a “What’s New” entry documenting the restored ability to activate installed modules once the config key reaches the portal.
deploy/homebrew/share/values.local.defaults.yaml Pins Modules__Root to /data for memex-local so landed/activated modules persist across updates.
deploy/helm/values.yaml Introduces a neutral Modules__Root: "" default indicating “follow Deployment__DataRoot via the template”.
deploy/helm/templates/memex-portal/config.yaml Renders Modules__Root into the ConfigMap and defaults it to Deployment__DataRoot to avoid the read-only /app fallback.
deploy/aks/scripts/check-chart-invariants.py Adds invariant #8 to ensure Modules__Root is present and non-blank in the rendered ConfigMap.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

633 tests  ±0   530 ✅ ±0   10m 34s ⏱️ -29s
 10 suites ±0   103 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   10 files  ±0     10 suites  ±0   4m 52s ⏱️ -19s
2 193 tests ±0  2 192 ✅ ±0  1 💤 ±0  0 ❌ ±0 
2 642 runs  +1  2 641 ✅ +1  1 💤 ±0  0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 674 tests  ±0   1 672 ✅ ±0   5m 0s ⏱️ - 1m 7s
   11 suites ±0       2 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 565 tests  ±0   1 562 ✅ ±0   8m 27s ⏱️ +49s
   10 suites ±0       3 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

3 333 tests  ±0   3 141 ✅ ±0   8m 20s ⏱️ +18s
   10 suites ±0     192 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 481 tests  ±0   1 477 ✅ ±0   8m 17s ⏱️ +13s
   11 suites ±0       4 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

    62 files  ±0      62 suites  ±0   45m 32s ⏱️ -36s
10 879 tests ±0  10 574 ✅ ±0  305 💤 ±0  0 ❌ ±0 
11 328 runs  +1  11 023 ✅ +1  305 💤 ±0  0 ❌ ±0 

Results for commit 40a8024. ± Comparison against base commit a2e5bd3.

@sierragolflima
sierragolflima merged commit a2713c0 into main Aug 20, 2026
23 checks passed
@rbuergi
rbuergi deleted the fix/modules-root-reaches-the-configmap branch August 28, 2026 08:56
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.

2 participants