259d9621ba
The /admin/secrets link was already in the side rail, but ungated — every authenticated user saw it, and a Designer/Operator clicking it was bounced by the page's own [Authorize(Policy = SecretsAuthorization.ManagePolicy)]. Hide the advertised dead end. Gated on that same policy constant rather than Roles="Administrator". The two are equivalent today (the RCL's AdminRole is "Administrator", read off the same ClaimTypes.Role claim FleetAdmin uses), so this is not about reach — it is that the rail cannot drift if the RCL ever changes its policy. Live-gated on docker-dev, both directions, because the positive half alone proves nothing here: the link rendered for an admin BEFORE this change too, so "admin still sees it" is satisfied equally by a working gate and by an inert one. Negative control — gate swapped to Roles="NoSuchRoleFalsifyProbe", rebuilt, item went to 0 while /role-grants and /apikeys stayed at 1 (the item is hidden, not the rail broken). Restored build hashed to the same image ID as the pre-falsification one, proving the restore was byte-exact. This also rules out AuthorizeView being inert inside ThemeShell's <Nav> slot under this host's static router, which would have silently hidden the link from everyone. SecretsNavLinkTests covers it in the AdminUI's actual idiom — source scan + metadata cross-check, NOT bUnit (the bunit PackageVersion pin has zero PackageReferences repo-wide). The load-bearing assertion is index ordering (AuthorizeView open < NavRailItem < close): a scan that merely finds both tags in the file passes when they are not nested, which is the ungated state. Scope: Secrets only. /role-grants and /apikeys are FleetAdmin (Administrator- only) too and stay visible to everyone, so the rail is now inconsistent — deliberate, as hiding those is a UX call for the whole rail rather than something to smuggle into this change. Noted in the markup. Family-wide task from the scadaproj index session (ScadaBridge ee193cd2, ignitionoee 3627bb8, HistorianGateway 58b6418, mxgw 87d575d).