Skip to content

Modules__Required: the ceiling written down and guarded, and the OVERWRITE nobody could see (Memex#131) - #2540

Merged
rbuergi merged 2 commits into
mainfrom
fix/131-modules-required-ceiling-guard
Aug 27, 2026
Merged

rbuergi merged 2 commits into
mainfrom
fix/131-modules-required-ceiling-guard

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

What Memex#131 reported, and what it turned out to be

memex-cloud declares Modules__Required__5: "MeshWeaver.Mcp.dll". The chart's block rendered 0..4, so the key reached no container, /mcp went on answering 404 with Mcp__BaseUrl configured a few lines below it, and Memex's config-key-coverage gate went red. #2512 already widened the block to 0..7 for the same failure on Memex#129 — so that red is gone.

Fixing it exposed a worse, live defect. Modules:Required is an array, so an overlay entry binds by index and never appends. Index 5 of the image's own list is MeshWeaver.Social.dll. So the moment index 5 started rendering, MeshWeaver.Mcp.dll did not add MCP — it replaced Social's requirement. Rendered through this chart with Memex main's overlay:

Modules__Required__4: "MeshWeaver.Speech.dll"
Modules__Required__5: "MeshWeaver.Mcp.dll"     <- was MeshWeaver.Social.dll

That is invisible from every direction: the replaced module is not missing (nobody asks for it any more), so MissingRequired and RequiredModuleStatus have nothing to say; the override is rendered, so the coverage gate passes; the deploy succeeds and /health stays green having quietly stopped guarding a module.

Three changes

1. Ceiling 7 → 9, and the reason is now in the block.

Why a ceiling exists at all: this ConfigMap's contract is that it names every key literally, because that is what EveryConfigKeySetInAValuesFile_IsBoundInTheConfigMap can check — a Helm range renders correctly and is invisible to the guard. A bounded list is the price of a checkable template, and the person who hits the bound sits in a different repository from the chart, so it has to be written down rather than counted off the template.

Why 9: the image's baseline Modules:Required is seven entries (0..6 — Radzen, Analysis, EntityViews, GoogleMaps, Speech, Social, AI). An overlay restating the baseline consumes 0..6, so anything it adds starts at 7. A ceiling of 7 leaves exactly one appendable slot. Ten leaves three.

2. Two static guards in test/MeshWeaver.Documentation.Test/PlatformBakeLaneGuard.cs, beside the existing coverage guard — for overflow:

  • ModulesRequired_IsRenderedAsAContiguousRangeFromZero — renders 0..N with no gap, and a comment still says renders 0..N. A gap is the silent variant: the overlay's index reaches no container while its neighbours do, so the deployment requires a different set than it declares.
  • NoValuesFile_DeclaresAModulesRequiredIndexAboveTheCeiling — an over-ceiling index fails with the ceiling named. The general guard already refuses such a key, but its instruction is "template each one", which is the wrong instruction for an ordered list whose bound the author cannot see.

3. A boot-time check in MeshBuilderModuleActivation.ShadowedRequired — for collision:

Reports every entry a later configuration provider replaced with a different module that appears nowhere else in the effective list. Provider order is the whole mechanism — a value a provider supplies that is not the effective one has been shadowed — so there is no provider-type sniffing and any layering (files, env, command line) is covered. It sits beside MissingRequired, reads the same key the same way, and reports on the same channel.

It has to be at boot rather than in a chart guard: the image's baseline ships from one repository and the overlay from another, and the two are only ever visible together in the running process.

Two shapes are deliberately not reported, because both are how the key is meant to be used, and both are pinned by tests: blanking an entry a deployment cannot satisfy (the sanctioned opt-out, and the only remedy the 2026-08-23 rollouts had), and reordering, where the module is still required at another index.

Proven RED — five seeds, each restored

A guard nobody watched fail is not a guard.

# Seed Result
A delete the index-3 hasKey block contiguity guard FAILS: "renders 0, 1, 2, 4, 5, 6, 7, 8, 9 — index 3 missing"
B a values file declares Modules__Required__10 ceiling guard FAILS: "…renders Modules__Required__0..9 — RAISE THE CEILING…"
C the literal Memex#131 shape (block back to 0..4, overlay declares 5) both fail; ceiling message names 0..4, and the comment-drift assertion catches the stale sentence
D ShadowedRequired returns [] (i.e. main today) the memex-cloud shape reports nothing — Expected: ["MeshWeaver.Social.dll"] / Actual: []
E ShadowedRequired made naive (drop the blank + reorder exclusions) BlankingAnEntry_IsNotAShadow and ReorderingTheList_IsNotAShadow both fail with two false positives each — neither exclusion is decoration

Verification

  • dotnet build test/MeshWeaver.Documentation.Test/… -c Release -warnaserror → 0 Warning(s) 0 Error(s); 94/94 passed
  • dotnet build test/MeshWeaver.Graph.Test/… -c Release -warnaserror --no-incremental → 0 Warning(s) 0 Error(s); ConfiguredModuleActivationTest 13/13 passed
  • dotnet build src/MeshWeaver.Mesh.Contract -c Release -warnaserror → clean. The change is purely additive (one new public static + one extra report loop), so no binary break.
  • helm template of Memex main's memex-cloud overlay renders as expected
  • scripts/check-config-key-coverage.py against Memex main's overlays with this chart → PASS

Companion

The Memex-side PR moves MCP to index 7 — the first index past the baseline — which is what actually makes /mcp required without dropping Social, and which needs this ceiling to exist.

Refs Memex#131.

🤖 Generated with Claude Code

Memex#131: memex-cloud declared Modules__Required__5 (MeshWeaver.Mcp.dll)
against a block that rendered 0..4, so the key reached no container and /mcp
went on answering 404 with Mcp__BaseUrl configured a few lines below it. #2512
widened the block to 0..7 for the same class of failure on Memex#129; this
raises the ceiling to 9, writes down what the ceiling IS and why one exists,
and puts the invariant under test.

Why 9: the image's baseline Modules:Required is seven entries (0..6 — Radzen,
Analysis, EntityViews, GoogleMaps, Speech, Social, AI), so an overlay that
restates the baseline consumes 0..6 and anything it ADDS starts at 7. A ceiling
of 7 leaves exactly one appendable slot; ten leaves three.

Why a ceiling exists at all, now stated in the block: the ConfigMap's contract
is that it names every key literally, because that is what
EveryConfigKeySetInAValuesFile_IsBoundInTheConfigMap can check. A Helm `range`
would render correctly and be invisible to the guard. A bounded list is the
price of a checkable template — so the bound has to be written where the person
who hits it reads it, in a repository that is not the one holding the overlay.

Two guards (test/MeshWeaver.Documentation.Test/PlatformBakeLaneGuard.cs):

* ModulesRequired_IsRenderedAsAContiguousRangeFromZero — the block must render
  0..N with no gap, and a comment must still say `renders 0..N`. A gap is the
  silent variant of the same defect: the overlay's index reaches no container
  while its neighbours do, so the deployment requires a different set than the
  one it declares and helm reports success.
* NoValuesFile_DeclaresAModulesRequiredIndexAboveTheCeiling — an over-ceiling
  index fails with the ceiling NAMED and the instruction to raise it. The
  general guard already refuses such a key, but tells the author to "template
  each one", which is the wrong instruction for an ordered list whose bound
  they cannot see.

Proven red before being trusted, three ways, each restored after:
  A. delete the index-3 block  -> "renders 0, 1, 2, 4, 5, 6, 7, 8, 9 — index 3
     missing"
  B. values file declares __10 -> "renders Modules__Required__0..9 — RAISE THE
     CEILING"
  C. the literal Memex#131 shape (block back to 0..4, overlay declares 5) ->
     both fire, naming ceiling 0..4 and the stale comment.

Also documents, in the block, that an overlay entry overrides the baseline BY
INDEX and never appends — Memex#128's index 5 would have replaced
MeshWeaver.Social.dll's guard the moment it started rendering. That is fixed on
the Memex side (Mcp moves to index 7).

Refs Memex#131.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 27, 2026 20:24

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 configuration/Helm templating ceiling where Modules__Required__N overrides beyond the chart’s rendered range were silently dropped (leading to features like /mcp staying effectively disabled despite values being set). It extends the rendered index range to 0..9 (10 slots) and adds explicit test guards to prevent both gaps and “values exceed ceiling” regressions, with the ceiling rationale documented where future maintainers will see it.

Changes:

  • Raise the rendered Modules__Required__N keys in the portal ConfigMap template to include indices 8 and 9, and document the intentional ceiling and its rationale.
  • Add two new guards in PlatformBakeLaneGuard to enforce (a) contiguous 0..N rendering and (b) no values file declaring an index above the rendered ceiling, with actionable failure messages.
  • Add a “What’s New” entry documenting the fix and the new effective limit (up to 10 required modules).

Reviewed changes

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

File Description
test/MeshWeaver.Documentation.Test/PlatformBakeLaneGuard.cs Adds guards that enforce contiguous Modules__Required__0..N rendering and prevent values files from declaring indices above the chart ceiling.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-27-required-modules-ceiling.md Documents the behavior change (up to 10 required modules) and the new guard behavior.
deploy/helm/templates/memex-portal/config.yaml Extends Modules__Required__N templating to indices 8 and 9 and records the ceiling rationale inline.

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

…only the one it loses

The overflow this issue started from (an index past the chart's ceiling reaching
no container) turned out to be the visible half. The other half is worse and is
live: `Modules:Required` is an ARRAY, so an overlay entry binds BY INDEX and
never appends. memex-cloud's `Modules__Required__5: MeshWeaver.Mcp.dll` does not
add MCP to the list — it REPLACES index 5 of the image's own list, which is
`MeshWeaver.Social.dll`. Rendered through the chart:

    Modules__Required__4: "MeshWeaver.Speech.dll"
    Modules__Required__5: "MeshWeaver.Mcp.dll"     <- was MeshWeaver.Social.dll

Nothing in the system can see it. The replaced module is not MISSING — nobody
asks for it any more — so MissingRequired and RequiredModuleStatus have nothing
to say; the override IS rendered, so the chart's coverage gate passes; the
deploy succeeds and /health stays green having silently stopped guarding a
module. It is only visible where the image's baseline and the deployment's
overlay finally meet, which is the running process: the two halves ship from
different repositories.

So the check is at boot, beside MissingRequired, reading the same key the same
way: MeshBuilderModuleActivation.ShadowedRequired reports every entry a LATER
configuration provider replaced with a different module that appears nowhere
else in the effective list. Provider ORDER is the whole mechanism — a value a
provider supplies that is not the effective one has been shadowed — so there is
no provider-type sniffing and any layering (files, env, command line) is
covered.

Two shapes are deliberately NOT reported, because both are how the key is meant
to be used, and both are pinned:
* BLANKING an entry the deployment cannot satisfy (empty winner) — the
  sanctioned opt-out, and the only remedy the 2026-08-23 rollouts had.
* REORDERING, where the replaced module is still required at another index.

Proven red, both directions, restored after each:
* Detector removed (return [] — the tree as it stands on main): the memex-cloud
  shape reports NOTHING. Expected ["MeshWeaver.Social.dll"], actual [].
* Detector made naive (drop the blank + reorder exclusions): BlankingAnEntry and
  ReorderingTheList both fail, each reporting two false positives — so neither
  exclusion is decoration.

Refs Memex#131.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi rbuergi changed the title Modules__Required renders 0..9 — the ceiling written down, and two guards that hold it (Memex#131) Modules__Required: the ceiling written down and guarded, and the OVERWRITE nobody could see (Memex#131) Aug 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 109 tests  ±0   1 109 ✅ ±0   18m 21s ⏱️ + 2m 25s
    7 suites ±0       0 💤 ±0 
    7 files   ±0       0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

    8 files      8 suites   11m 50s ⏱️
4 417 tests 4 414 ✅ 3 💤 0 ❌
5 022 runs  5 019 ✅ 3 💤 0 ❌

Results for commit 6094544.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 088 tests  ±0   896 ✅ ±0   5m 24s ⏱️ +23s
    6 suites ±0   192 💤 ±0 
    6 files   ±0     0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

1 312 tests  ±0   1 312 ✅ ±0   6m 13s ⏱️ -4s
    6 suites ±0       0 💤 ±0 
    6 files   ±0       0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 601 tests  +2   1 600 ✅ +2   9m 50s ⏱️ + 1m 16s
    9 suites ±0       1 💤 ±0 
    9 files   ±0       0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

739 tests  ±0   735 ✅ ±0   7m 20s ⏱️ +6s
  8 suites ±0     4 💤 ±0 
  8 files   ±0     0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

    44 files  ±0      44 suites  ±0   59m 0s ⏱️ + 4m 3s
10 264 tests +7  10 064 ✅ +7  200 💤 ±0  0 ❌ ±0 
10 871 runs  +8  10 671 ✅ +8  200 💤 ±0  0 ❌ ±0 

Results for commit 6094544. ± Comparison against base commit 4c1917e.

@rbuergi
rbuergi merged commit 5904be1 into main Aug 27, 2026
23 checks passed
@rbuergi
rbuergi deleted the fix/131-modules-required-ceiling-guard 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