From c55128d83d4f12d1ec522143ec9652b6cecdf315 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 22 Oct 2026 08:45:51 -0400 Subject: [PATCH] docs(centralui): 2026-10-21 review hardening (review 2026-10-21 07, 04/C4) --- docs/requirements/Component-CentralUI.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/requirements/Component-CentralUI.md b/docs/requirements/Component-CentralUI.md index 3431c89b..0bf5b954 100644 --- a/docs/requirements/Component-CentralUI.md +++ b/docs/requirements/Component-CentralUI.md @@ -32,6 +32,15 @@ Central cluster only. Sites have no user interface. - `TemplateFolderTree` compares immutable value snapshots of folder/template hierarchy and extra children before rebuilding. In-place renames, moves, derived-state changes, and composition edits remain visible; filter and reveal changes are applied independently. - Navigation grants Flow Debugger its own policy and hides Notifications unless the principal holds a role that admits a child. Route-policy coverage freezes every CentralUI route and the external Secrets route. +## Review hardening (2026-10-21) + +- **Test Run bound to a deployed instance** (`BindInstanceUniqueName`, `SandboxBoundRunPolicy`) performs real site operations, so it requires the **Deployer** role (a Designer may still run an *unbound* Test Run), is confined to the caller's permitted sites (`SiteScopeService.Resolve` over the request principal; a scoped caller whose instance site cannot be resolved is refused), and writes one best-effort operator-attributed `ScriptTrace`/`RouteCall` audit row (`Extra.source = "test-run"`) via `ICentralAuditWriter`, mirroring the Flow Debugger. Denials return 403 before any worker is started. +- **Script-analysis endpoints** reject script text over 256 KB (and bodies over 1 MB) with 413 and cap concurrent requests per user at 6 (429 beyond that; `ScriptAnalysisRequestGuard`, map entries live only while a request is in flight). The `Diagnose` cache key now includes a hash of the editor context (declared parameters, sibling scripts, self attributes, children, parent), so identical code with different context never shares a cached answer. `BlockingWaitAnalyzer` advisories surface as Monaco **warning** markers (`SCADA_BLOCKING_WAIT`) on the offending line. +- **Template reads:** the Dashboard counts templates from `GetTemplateStubsAsync`; Instance Create, Template Create and Transport Export use the stub projection (adapted to childless `Template` shells); Templates and Template Edit use `GetAllTemplatesForAnalysisAsync` (no tracking, no script bodies, compositions retained). No page loads the full tracked template graph except where it edits one template (`GetTemplateWithChildrenAsync`). +- **Topology live tick** runs behind `PollGate` and a refresh never raises `_loading` (the tree stays mounted); the refreshed lists are swapped in atomically. +- **Pending-updates faults** are remembered for 10 s (`SingleFlightMemo` fault back-off) so the rail indicator's polling does not start a fleet flatten per tick against a sick database; an operator Re-check bypasses it. +- **Circuit hygiene:** `ConfigurationAuditLog` disposes its browser-time JS module; `SessionExpiry` skips the redirect after disposal; `AlarmSummary` and `TreeView` fire-and-forget calls swallow disposal/disconnect races; `FlowDebugService`'s per-circuit script cache is bounded (8 instances); the Site Event Log page retains at most 1000 rows (oldest dropped, with a note) and hints that keyword search covers at most the last 7 days unless a narrower From/To is set (server clamp `ScadaBridge:SiteEventLog:KeywordSearchMaxWindow`). + ## Failover Behavior - A **load balancer** sits in front of the central cluster and routes to the active node.