Diagnose controls rendered as text
A control displayed as its own description, such as StackControl { … }, reached the
escaped-text fallback after every registered view map declined it. Loading a view assembly is
not proof that its map was registered, and registering a map is not proof that it accepted
the particular control or skin.
The diagnostics answer those questions separately:
MeshWeaver.Blazor.Views.ViewsExtensionsrecords DefaultViews registered on hub at Information when the layout client aggregates its configuration. The structured fieldsAddress,MvidandCountidentify the hub, the exact view assembly and the map count.MeshWeaver.Layout.Client.ViewDispatchrecords the platform warning when no map accepts a control. The registered map owners identify which contributors were consulted.MeshWeaver.Blazor.Components.DispatchViewrecords through the escaped-HTML fallback at Information for an actual fallback render, namingArea,ControlTypeandHubAddress. Ordinary view dispatch remains at Debug. Unknown-skin details remain at Debug as well./healthincludesview_packs, with the registeredowners,countand roothub. A missing REQUIRED pack (the default views, entity-views or graph-views pack), or an absentILayoutClient, is Unhealthy, andview_packsis a roll gate (MeshWeaver#7328): it holds/readyonly. The replica stays alive and out of the Service, a roll that would produce such a replica stalls with the previous image serving, and neither the startup probe nor liveness kills a container for it. As a roll gate its reading always prints on/health, Healthy or not, so "registered and clean" reads differently from "check not registered".- The same verdict alerts:
MeshWeaver.Hosting.Blazor.ViewPacksHealthChecklogs ONE Error line naming the missing packs the first time a replica sees them missing (and again only when the missing set changes), which is the red-log path the log watcher tickets. The recovery is logged at Information. Both hosts register the check as a singleton, because the once-per-change memory lives on the instance;ViewPackDeliveryPathTestholds them to it.
The registration event runs once per layout-client aggregation, not on every control. The fallback event is limited to failed view selection. These are persistent operational signals whose value is diagnosing a visible rendering defect; they are not a temporary increase in general render logging.
On a fresh installation whose render packs arrive through the store after boot, the replica therefore takes no traffic until they have landed and been activated (a module's first landing needs a restart). That is deliberate: before then it cannot render a form, and an outage that says so is what the 2026-10-17 installation below lacked.
The hold has ONE exemption, where waiting could never end: an installation still to be
registered through its own instance app (the first configured registry has no token, there is
no registration key, an instance id is set) and holding no issued key yet. Its default install
waits for that registration before it installs from any registry, and the registration waits for
a platform admin to consent on this very replica. There the check reports Degraded, alerts all
the same, and names why readiness is not held. The decision is live (ViewPacksReadinessMonitor
in Memex.Portal.Shared): it watches Admin/PluginRegistryCredential/{host}, and the moment
the issued key is stored the hold applies, on this boot and on every later roll. Every other
shape holds, whatever the order or mix of tokened and untokened registries. Until the credential
store has answered, the gate holds.
Which hub is measured
Module HubConfigurations install the view maps on the root mesh hub. Ordinary portal circuit
hubs inherit its service scope, so DispatchView resolves the root ILayoutClient; the health
check reads that same registration. A circuit with its own layout-client registration needs
separate investigation: the root health result does not establish which maps that circuit uses.
Descriptor consumers
The fallback descriptor includes HtmlView.IsFallback, a real Blazor component parameter.
Consumers can forward the entire parameter bag, including that flag, without a special filter.
The flag only identifies the rendering path; it does not alter the content or its encoding.
The initial implementation put the flag in the parameter bag but stripped it only inside
DispatchView. A direct descriptor host forwarded it to an HtmlView that did not declare
the parameter and threw an unmatched-parameter exception. A real HtmlRenderer regression
reproduced that failure; another regression reproduced the misleading no-layout-client
health message. Registration and fallback tests also capture the event category, level and
structured fields, and verify that a normal HTML view emits no fallback event.
These diagnostics do not repair a missing view map, an incompatible module, or a node whose latest compiled assembly was refused by its consumer. They provide the evidence for identifying that defect. A healthy registration check alone must not be used to claim the affected page works.
A render pack that never reached the installation
The packs that render the platform's own pages — EntityViews (every text, number, date and
choice input), GraphViews (the picture upload, MeshNode picker, card and content editor), Chat
(the composer) and the collaborative markdown editors in Essentials — are registry packages, not
image entries. An installation receives them only through its install pass, and that pass has two
lanes: the registry's preInstalled baseline, and the packages the deployment names in
PluginCatalog:InstallByDefault (its record's preInstall) together with their requires.
A list that names packages exactly is an explicit set (core policy declared-package-set-is-exact):
the baseline lane is off and only the named packages and their dependency closure install. So
the render packs must be in the closure of what an installation names, not only in the baseline.
Essentials — the package new installations pre-install — therefore requires EntityViews,
GraphViews and Chat, and Memex.Hosts.Test →
ViewPackDeliveryPathTest.AnInstallThatDeclaresOnlyEssentials_StillReceivesEveryRenderPack reads
the repository's package roots and fails when the closure stops reaching one of them.
Measured 2026-10-17 on a freshly provisioned customer installation whose record pre-installs Essentials alone. Nothing errored and the pods were ready — the check was Degraded-only then, which is what MeshWeaver#7328 changed. The readings that named the defect:
- The
view_packshealth entry wasDegraded: the entity-views pack did not register on this hub … the graph-views pack did not register on this hub, with two maps registered (MeshWeaver.Blazor.Views:AddDefaultViews,MeshWeaver.Blazor.Portal:UserProfileControl→UserProfilePageView). MeshWeaver.Layout.Client.ViewDispatchlogged no view map accepted TextFieldControl … in area Settings/1/1, then the same forNodeImageUploadControl,MeshNodeContentEditorControlandMarkdownEditorControlinEditProfile/…areas, andThreadChatControlinComposer.- No module-landing line ever named EntityViews, GraphViews or Chat. Essentials' own closure (AI,
the providers, Export, Mcp, Notifications, Markdown.Collaboration) landed, but each landing said
RESTART REQUIRED to load it, and
pending_module_activationlisted them as landed and not loaded.
The Settings → Profile page painted TextFieldControl { Id = , … } where the display-name field
belonged, while its headings, nav and back arrow (all default-pack controls) rendered normally.
Three gates now cover this failure:
SettingsProfileRenderTest(MeshWeaver.Blazor.Views.Test) renders Settings → Profile and the profile editor through the real Blazor renderer, using exactly the packs Essentials' closure delivers. It waits until every section's own view has rendered, it fails on a render that never settles, and it fails on any visible text that matches a control's recordToString().SettingsProfileWithoutCollaborationViewsTestis its negative control: without the markdown editors' pack, the Bio and Links sections must printMarkdownEditorControl { Id = … }.scripts/examples-smoke.pysmokes the smoke identity's Settings tabs (the built-in ones and every tab its menu links) and marks a frame string that matchesRECORD_TOSTRINGred.- The browser half (
e2e/examples-smoke.spec.ts) marks visible text that matches the same pattern red. The dump exists only in the browser, because the server frame carries the typed control.