Ships the check-everything PowerShell script + the human-readable exit-gate doc that
closes Phase 7 (scripting runtime + virtual tags + scripted alarms + historian sink
+ Admin UI + address-space integration).
## scripts/compliance/phase-7-compliance.ps1
Mirrors the Phase 6.x compliance pattern. Checks:
- Stream A: Roslyn sandbox wiring, ForbiddenTypeAnalyzer, DependencyExtractor,
ScriptLogCompanionSink, Deadband helper
- Stream B: VirtualTagEngine, DependencyGraph (iterative Tarjan),
SemaphoreSlim async-safe cascade, TimerTriggerScheduler, VirtualTagSource
- Stream C: Part9StateMachine, AlarmConditionState GxP audit Comments,
MessageTemplate {TagPath}, AlarmPredicateContext SetVirtualTag rejection,
ScriptedAlarmSource IAlarmSource, IAlarmStateStore + in-memory store
- Stream D: BackoffLadder 1-60s, DefaultDeadLetterRetention (30 days),
HistorianWriteOutcome enum, Galaxy.Host IPC contracts
- Stream E: Four new entities + check constraints + Phase 7 migration
- Stream F: Five Admin services + ScriptEditor + ScriptsTab + AlarmsHistorian
page + Monaco loader + DraftEditor wire-up + declared-inputs-only contract
- Stream G: NodeSourceKind discriminator + walker VirtualTag/ScriptedAlarm emission
+ DriverNodeManager SelectReadable + IsWriteAllowedBySource
- Deferred (flagged, not blocking): SealedBootstrap composition, live end-to-end
smoke, sp_ComputeGenerationDiff extension
- Cross-cutting: full-solution dotnet test (regression check against 1300 baseline)
## docs/v2/implementation/exit-gate-phase-7.md
Summarises shipped PRs (Streams A-G + G follow-up = 8 PRs, ~197 tests), lists the
compliance checks covered, names the deferred follow-ups with task IDs, and points
at the compliance script for verification.
## Exit-gate local run
2191 tests green (baseline 1300), 0 failures, 55 compliance checks PASS,
3 deferred (with follow-up task IDs).
Phase 7 ships.
5.4 KiB
5.4 KiB
Phase 7 Exit Gate — Scripting, Virtual Tags, Scripted Alarms, Historian Sink
Status: Open. Closed when every compliance check passes + every deferred item either ships or is filed as a post-v2-release follow-up.
Compliance script:
scripts/compliance/phase-7-compliance.ps1Plan doc:docs/v2/implementation/phase-7-scripting-and-alarming.md
What shipped
| Stream | PR | Summary |
|---|---|---|
| A | #177–#179 | Core.Scripting — Roslyn sandbox + DependencyExtractor + ForbiddenTypeAnalyzer + per-script Serilog sink + 63 tests |
| B | #180 | Core.VirtualTags — dep graph (iterative Tarjan) + engine + timer scheduler + VirtualTagSource + 36 tests |
| C | #181 | Core.ScriptedAlarms — Part 9 state machine + predicate engine + message template + ScriptedAlarmSource + 47 tests |
| D | #182 | Core.AlarmHistorian — SQLite store-and-forward + backoff ladder + dead-letter retention + Galaxy.Host IPC contracts + 14 tests |
| E | #183 | Config DB schema — Script / VirtualTag / ScriptedAlarm / ScriptedAlarmState entities + migration + 12 tests |
| F | #185 | Admin UI — ScriptService / VirtualTagService / ScriptedAlarmService / ScriptTestHarnessService / HistorianDiagnosticsService + Monaco editor + /alarms/historian page + 13 tests |
| G | #184 | Walker emits Virtual + ScriptedAlarm variables with NodeSourceKind discriminator + 5 tests |
| G follow-up | #186 | DriverNodeManager dispatch routes by NodeSourceKind + writes rejected for non-Driver sources + 7 tests |
Phase 7 totals: ~197 new tests across 7 projects. Plan decisions #1–#22 all realised in code.
Compliance Checks (run at exit gate)
Covered by scripts/compliance/phase-7-compliance.ps1:
- Roslyn sandbox anchored on
ScriptContextassembly withForbiddenTypeAnalyzerdefense-in-depth (plan #6) DependencyExtractorrejects non-literal tag paths with source spans (plan #7)- Per-script rolling Serilog sink + companion-forwarding Error+ to main log (plan #12)
- VirtualTag dep graph uses iterative SCC — no stack overflow on 10 000-deep chains
VirtualTagSourceimplementsIReadable+ISubscribableper ADR-002- Part 9 state machine covers every transition (Apply/Ack/Confirm/Shelve/Unshelve/Enable/Disable/Comment/ShelvingCheck)
AlarmPredicateContextrejectsSetVirtualTagat runtime (predicates must be pure)MessageTemplatesubstitutes{TagPath}tokens at event emission (plan #13); missing/bad →{?}- SQLite sink backoff ladder 1s → 2s → 5s → 15s → 60s cap (plan #16)
- Default 1M-row capacity + 30-day dead-letter retention (plan #21)
- Per-event outcomes Ack/RetryPlease/PermanentFail on the wire
- Galaxy.Host IPC contracts (
HistorianAlarmEventRequest/Response/ConnectivityStatusNotification) - Config DB check constraints: trigger-required, timer-min, severity-range, alarm-type-enum, JSON comments
ScriptedAlarmStatekeyed onScriptedAlarmId(not generation-scoped) per plan #14- Admin services: SourceHash preserves compile-cache hit on rename; Update recomputes on source change
ScriptTestHarnessServiceenforces declared-inputs-only contract (plan #22)- Monaco editor via CDN + textarea fallback (plan #18)
/alarms/historianpage with Retry-dead-lettered operator action- Walker emits
NodeSourceKind.Virtual+NodeSourceKind.ScriptedAlarmvariables DriverNodeManagerdispatch routes Reads by source; Writes to non-Driver rejected withBadUserAccessDenied(plan #6)
Deferred to Post-Gate Follow-ups
Kept out of the capstone so the gate can close cleanly while the less-critical wiring lands in targeted PRs:
- SealedBootstrap composition root (task #239) — instantiate
VirtualTagEngine+ScriptedAlarmEngine+SqliteStoreAndForwardSinkinProgram.cs; passVirtualTagSource+ScriptedAlarmSourceas the newIReadableparameters onDriverNodeManager. Without this, the engines are dormant in production even though every piece is tested. - Live OPC UA end-to-end smoke (task #240) — Client.CLI browse + read a virtual tag computed by Roslyn; Client.CLI acknowledge a scripted alarm via the Part 9 method node; historian-disabled deployment returns
BadNotFoundfor virtual nodes rather than silent failure. - sp_ComputeGenerationDiff extension (task #241) — emit Script / VirtualTag / ScriptedAlarm sections alongside the existing Namespace/DriverInstance/Equipment/Tag/NodeAcl rows so the Admin DiffViewer shows Phase 7 changes between generations.
Completion Checklist
- Stream A shipped + merged
- Stream B shipped + merged
- Stream C shipped + merged
- Stream D shipped + merged
- Stream E shipped + merged
- Stream F shipped + merged
- Stream G shipped + merged
- Stream G follow-up (dispatch) shipped + merged
phase-7-compliance.ps1present and passes- Full solution
dotnet testpasses (no new failures beyond pre-existing tolerated CLI flake) - Exit-gate doc checked in
SealedBootstrapcomposition follow-up filed + tracked- Live end-to-end smoke follow-up filed + tracked
sp_ComputeGenerationDiffextension follow-up filed + tracked
How to run
pwsh ./scripts/compliance/phase-7-compliance.ps1
Exit code 0 = all pass; non-zero = failures listed in the preceding [FAIL] lines.