Wires the bespoke MqttDriverBrowser into AdminUI's IDriverBrowser
registrations alongside OpcUaClient/Galaxy, overriding the universal
DiscoveryDriverBrowser fallback for driverType "Mqtt".
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
Register SqlDriverBrowser as an IDriverBrowser beside the OpcUaClient/Galaxy
lines, project-reference Driver.Sql.Browser, and add SqlAddressPickerBody.razor:
a DriverBrowseTree schema/table/column picker (DriverOperator-gated) with a
column attribute side-panel, manual-entry model/selector fields retained, that
composes the per-tag TagConfig blob (KeyValue / WideRow) matched to
SqlEquipmentTagParser. Pasted ad-hoc connection strings are session-only and
never persisted into the composed blob or driver config.
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
The Secrets.Ui RCL's routable page was routed directly, but this host's
router is deliberately static (cookie SignInAsync needs SSR) with per-page
@rendermode opt-in - a directive the RCL page cannot carry, because the
other three family hosts render it under a globally interactive router
where a nested render mode throws. Routing straight to the RCL page
therefore rendered /admin/secrets with no circuit: it displayed, but every
@onclick was silently dead.
Fix: a host-side wrapper page (Pages/SecretsAdmin.razor) now owns
/admin/secrets, carries the standard per-page InteractiveServer render
mode, re-states the RCL page's own authorization policy (the router only
enforces [Authorize] on the routed component, which is now the wrapper),
and renders the RCL page as an ordinary child. The RCL assembly is
de-registered from both AdditionalAssemblies sites (router + endpoint) so
the route is unambiguous.
Guards: SecretsPageWiringTests pins the render mode (the #483 regression),
route parity with the RCL page, and policy parity; the page census in
PageAuthorizationGuardTests classifies the new page and admits the
secrets:manage policy. AdminUI.Tests 665/665.
Live-verified on the rebuilt docker-dev rig with Playwright: /_blazor
circuit negotiated on /admin/secrets and Add secret opens the editor
(before the fix: zero interactive markers, no circuit, dead click).
Closes#483.
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
Mount the shared ZB.MOM.WW.Secrets.Ui RCL page at /admin/secrets on admin nodes:
- Register secrets:manage/secrets:reveal policies additively via
Configure<AuthorizationOptions>(o => o.AddSecretsAuthorization()) in AddAdminUI
(the admin-only composition layer that already references the RCL — avoids forcing
an RCL dependency into the core Security lib; mirrors HistorianGateway)
- Register the RCL routable assembly in BOTH the SSR endpoint (AddAdditionalAssemblies)
and the interactive Router (App.razor AdditionalAssemblies) or the route 404s
- Add a Secrets nav item; the page's own [Authorize(Policy=...)] gates access
Claim-type MATCH: AdminRole=Administrator reads the same ClaimTypes.Role as FleetAdmin,
so existing Administrators are authorized with no new role mapping. Full clean boot
verified; interactive reveal deferred to the live gate (shared UI already proven).
Implements IEffectiveNameGuard (the Wave-A contract): per-equipment effective-name
uniqueness across UnsTagReferences (DisplayNameOverride else backing raw tag Name),
VirtualTags (Name), and ScriptedAlarms (Name). Ordinal comparison, self-row exclusion
by (kind, id), one pooled context per CheckAsync call. Registered Scoped in
EndpointRouteBuilderExtensions so WP1's UnsTreeService consumer goes live.
Verified the deploy-time DraftValidator UnsEffectiveNameCollision rule already
computes reference effective names from the backing raw tag's current Name (catches
rename-induced collisions), spans all three sources, compares ordinal, and names both
colliding sources + the equipment — no hardening needed. DraftSnapshotFactory already
loads Tags/UnsTagReferences/VirtualTags/ScriptedAlarms.
Tests: 8 guard tests (in-memory EF) + 3 deploy-gate tests (rename-induced collision,
override-vs-VT, ordinal case-sensitivity). All green.
Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
Adds <summary>/<param>/<returns>/<inheritdoc> where missing and removes
project bookkeeping IDs (task/tracking refs) from shipped code comments,
so the docs read cleanly and CommentChecker is quiet except for known
false positives (PLC/protocol terms, event/IEqualityComparer inheritdoc).
Doc/comment-only; no logic changed; solution builds clean.
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
Six interlocking fixes surfaced while smoke-testing the fused Host in a browser:
- Host/Program.cs: UseStaticWebAssets() opts into the RCL static-asset pipeline
in any environment (auto-only in Development), MapStaticAssets().AllowAnonymous()
exempts CSS/JS from the AddOtOpcUaAuth fallback policy, and
AddCascadingAuthenticationState() lets <AuthorizeView/> work inside interactive
components (NavSidebar's session block).
- Security/ServiceCollectionExtensions: ILdapAuthService Scoped → Singleton —
consumed by the Singleton LdapOpcUaUserAuthenticator on driver-role nodes.
Crash only surfaced in Development (ValidateOnBuild=true).
- Security/Endpoints/AuthEndpoints: /auth/login now dispatches on Content-Type —
application/json keeps the original 204/401/503 contract for tests, and
application/x-www-form-urlencoded (the browser <form>) gets a redirect dance.
DisableAntiforgery on the login endpoint (it's the entry point, no prior session)
and AllowAnonymous to override the fallback policy.
- Security/Ldap/LdapOptions + LdapAuthService: real DevStubMode property; when
true the auth service bypasses the LDAP bind and returns a FleetAdmin role so
dev/test can navigate the full Admin UI without GLAuth running.
- AdminUI/EndpointRouteBuilderExtensions: doc-comment update about static-asset
flow (the actual MapStaticAssets call lives in Host/Program.cs).