docs: site artifact revision, pending-updates indicator, rail scroll pane

Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB
This commit is contained in:
Joseph Doherty
2026-09-14 14:01:07 -04:00
parent c724ed6ee2
commit d8979664f1
6 changed files with 123 additions and 2 deletions
+5 -1
View File
@@ -19,6 +19,8 @@ Central cluster only. Sites have no user interface.
- **The decision.** *Approve* is `Caution` (amber): approving executes a real write to live plant equipment, so it is not a benign primary action, but it is not destroying anything either. *Reject* **keeps** `Destructive` (red): it is terminal and irreversible for the request — the Operator must resubmit — and holding it red is what keeps the two opposite decisions visually distinct. Dropping *Reject* to a neutral button was considered and rejected: it would simply invert the bias, making declining look like the safe default.
- Colour is never the only signal — the button always carries its verb (see the label contract above), so the mapping is an accessibility affordance, not the meaning.
- **`DialogSeverity` is an additive overload**, not a replacement: the boolean form remains the signature for the ~34 existing call sites, which are genuinely two-state. Adding a fifth optional parameter to the boolean form instead would have silently un-matched every existing four-argument NSubstitute stub — the row-28 gotcha, where a stub that stops matching returns a null `Task` and the caller NREs with no compile error. Reach for the overload only when `Caution` is what you mean.
- **Pending updates rail indicator (2026-09-14, `PendingUpdatesIndicator`, design `docs/plans/2026-09-14-pending-updates-design.md`).** A block in the rail footer, placed above the user name (`MainLayout`'s `RailFooter`, `.rail-user`), summarising the fleet-wide `PendingUpdatesSnapshot` from Component-DeploymentManager.md's *Pending-updates aggregate*. Rendered only for a user holding **Deployer or Administrator** — the two roles that can act on it — and hidden for everyone else. States: *Checking for updates…* (first load), *Up to date* (muted, check icon), **Pending updates** with a warning count pill and a breakdown line (`N instances`, linking to `/deployment/topology` for a Deployer; `M sites`, linking to `/admin/sites` for an Administrator, plain text otherwise), or *Updates unavailable* (error styling, tooltip carries the failure message) on a faulted evaluation. A tooltip on the block reads *as of HH:mm UTC*. It polls `IPendingUpdatesCache.GetAsync()` every 30 s through `PollGate` — cheap, since the cache's own 60 s TTL means the fleet is actually re-flattened at most once per TTL fleet-wide, no matter how many circuits (or how often each one) poll it — disposes its timer with the circuit, and renders through `InvokeAsync`. A **site-scoped** user sees counts narrowed to their permitted sites only (`PendingUpdatesSnapshot.ForSites`, the same scope `SiteScopeService` applies to Topology), so the indicator and the Sites page never disagree about which sites a given operator can see.
- **Rail nav scroll pane (2026-09-14).** `NavMenu.razor` wraps its items in a `<div class="rail-scroll">`; `site.css` (≥ lg only) fills the sticky `100vh` `#theme-rail` (`#theme-rail > .side-rail { height: 100% }`) and makes `.rail-scroll` the flexible, independently-scrolling region (`flex: 1 1 auto; min-height: 0; overflow-y: auto`), so a long expanded nav scrolls under the brand bar instead of pushing the rail footer (and now the pending-updates indicator) off-screen. Below lg the rail is inline and scrolls with the page, unchanged. The `ZB.MOM.WW.Theme` kit itself is untouched — this is app-local CSS/markup layered on top of it (deferred-work register row 123 proposes upstreaming it for the kit's other consumers).
## Review hardening (2026-09-09)
@@ -107,6 +109,8 @@ Central cluster only. Sites have no user interface.
- **Move data connection between sites** (M9/T24): a **Move…** action on a data connection opens `MoveDataConnectionDialog`. The underlying `MoveDataConnectionCommand` guards against: target site does not exist, name collision at the target, any `InstanceConnectionBinding` referencing the connection, and any `InstanceNativeAlarmSourceOverride.ConnectionNameOverride` reference at the source site's templates. Blocked connections display the blocking reason.
- **Server certificate management** (`/design/connections/{id}/certificates`, Admin role): a per-connection page that lists the contents of the site's OPC UA trusted-peer and rejected certificate stores (Subject / Issuer / Thumbprint / validity / Trusted-or-Rejected status) with a **Remove** action. The page makes clear the store is **node-wide for the site** (shared by every site node), not per data connection — trusting or removing a certificate affects all OPC UA connections at that site. Trust and Remove are central commands relayed to **both** site nodes so the node-local PKI stores stay consistent across failover (see Component-SiteRuntime.md, Component-DataConnectionLayer.md).
- The site detail page exposes a new **"Audit feed"** tab that hosts the Audit Log page pre-filtered to `Site = <site>` — an in-context view of every operational audit event for that site.
- **Artifacts badge (2026-09-14, design `docs/plans/2026-09-14-pending-updates-design.md`).** Each site card carries an **Artifacts** badge next to the identifier, one of three states: `Current` (light — the site's `Site.ArtifactRevisionHash` matches what a deploy would send today), `Out of date` (warning, `bi-exclamation-triangle`, tooltip = the last successful deploy's UTC timestamp), or `Never deployed` (secondary — the site has no stamp at all, including every site immediately after the migration that added the columns, until one artifact deploy). Below the badge a muted `N of M instances out of date` line links to `/deployment/topology` whenever the site has any deployed instances, since the same fleet snapshot already carries the count.
- The page header gains a **Re-check** button (forces a fresh fleet evaluation, disabled while running) and an **"Updates as of HH:mm:ss UTC"** caption next to the page title. The snapshot loads **after** the site list, never blocking it — cards render immediately with a *checking…* placeholder for the badge/line, and a snapshot failure renders **unavailable** on the badge rather than hiding it or failing the page. A successful **Deploy Artifacts** (single-site or all-sites) forces a refresh of the snapshot so the badge flips to `Current` immediately rather than waiting out the cache TTL.
### Schema Library (Design Role)
- A **Schema Library** page (M9/T32, under the Design nav group) provides CRUD for `SharedSchema` entries — named, reusable JSON Schema definitions. Each entry has a name (globally unique) and a JSON Schema body.
@@ -184,7 +188,7 @@ Central cluster only. Sites have no user interface.
- **Per-site deployment**: A "Deploy Artifacts" button on the Sites admin page allows deploying all artifacts to an individual site.
- **Deploy all**: A bulk action deploys artifacts to all sites at once.
- This is a **separate action** from instance deployment — system-wide artifacts are not automatically pushed when definitions change.
- Track per-site deployment status.
- **Track per-site deployment status**, backed by `Site.ArtifactRevisionHash` / `ArtifactsDeployedAt` (Component-DeploymentManager.md → *Per-site artifact revision*), stamped **only on a successful deploy to that site**. A failed site keeps its previous stamp (or none) and keeps reading out of date on the Sites page; after an upgrade that ships the stamp columns, every site reads *Never deployed* until one artifact deploy runs — see the Artifacts badge above.
### Debug View (Deployment Role)
- Select a deployed instance and open a live debug view — the instance picked through the same shared **`InstancePicker`** combobox as the Flow Debugger, with an `ItemLabel` suffix of `(State)` on each row.