Commit Graph

1915 Commits

Author SHA1 Message Date
Joseph Doherty 22d5239039 test(commons): update AuditKind lock-in test for 15th member (ReconciliationAbandoned)
Task 17 (durable record for permanently-abandoned reconciliation rows) added
AuditKind.ReconciliationAbandoned but did not update the enum member-count
lock-in test AuditEnumTests, which asserted exactly 14 members. The per-task
filtered test runs did not include this Commons enum guard, so the gap surfaced
only in the PLAN-04 Task 26 full-solution sweep — exactly what the sweep exists
to catch. Rename to *FifteenExpectedMembers, add ReconciliationAbandoned to the
expected set, bump the count assertion to 15.

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
2026-07-09 14:57:28 -04:00
Joseph Doherty a9da1ed804 docs(archreview): sync PLAN-04 tasks.json + tracker — Task 24 completed (25/26, only T26 sweep remains) 2026-07-09 10:05:24 -04:00
Joseph Doherty 7f1b59ff84 docs: reconcile design docs with shipped code (enums, Sms, per-channel retention, SiteCalls schema, conventions)
PLAN-04 Task 24. Component-Commons: AuditChannel gains SecuredWrite (5), AuditKind full
15-value list (+SecuredWrite*/ReconciliationAbandoned), NotificationType Email/Sms (Teams
evaluated+dropped), AuditLogRow persistence-only-projection deviation note. ConfigDB:
Type discriminator Email/Sms, data-layer conventions (UTC DateTime + Utc converter, MSSQL-
only repos/fixtures, AuditLog clustered-key + P3/P4/P6 tracked follow-ups). SiteCallAudit:
schema table rewritten to shipped columns (Channel/Target/AuditStatus-Status/SourceNode/
HttpStatus/IngestedAtUtc; no Kind/TargetSummary/provenance) + stored-vs-tracking-view note.
AuditLog: per-channel retention config lists all 5 channels incl SecuredWrite. Also fixed
two stale 'Teams planned' refs surfaced by the verification grep (HighLevelReqs, CentralUI).
2026-07-09 10:03:13 -04:00
Joseph Doherty 3a21e77c03 docs(archreview): add cross-plan parallelization map for incomplete tasks + sync stale PLAN-04 status
Corrects the Status Board (PLAN-04 24/26, not 0), and adds a Parallelization Map to
00-MASTER-TRACKER plus a per-plan Parallelization block to PLAN-04/05/06/07/08. Derives
ready sets from the live .tasks.json blockedBy graphs and the Files metadata, identifies
the initiative-wide serialization mutexes (ManagementActor.cs single-writer, EF migration
snapshot, Host/Program.cs, ArtifactDeploymentService.cs, SiteStorageService.cs,
NotificationOutboxActor.cs, AlarmSummary.razor), and recommends the safe concurrent
subagent lanes per plan. Flags PLAN-07 T33 as a dedupe-skip (CLI.Tests already in slnx).
2026-07-09 09:53:05 -04:00
Joseph Doherty 05e36d8665 docs(archreview): sync PLAN-04 tasks.json — Tasks 22, 23, 25 completed (Batch 8, purge first-tick + data-layer cleanups + bounded parallel delivery) 2026-07-09 09:43:42 -04:00
Joseph Doherty 6567216e14 perf(notification-outbox): bounded parallel delivery within a dispatch sweep (default 4) 2026-07-09 09:41:48 -04:00
Joseph Doherty acd8a6b8b5 chore(data-layer): low-severity cleanups — SQLite param chunking, KPI metric catalog, computed-column predicate guard 2026-07-09 09:32:04 -04:00
Joseph Doherty b58d810dbe fix(purge): short first tick on all three purge timers so daily-recycled nodes still purge 2026-07-09 09:26:42 -04:00
Joseph Doherty 7cd62933a6 docs(archreview): sync PLAN-04 tasks.json — Tasks 19-21 completed (Batch 7, KpiSample batched purge + NotificationOutbox single-query KPI + SiteCalls OPTION RECOMPILE) 2026-07-09 09:13:51 -04:00
Joseph Doherty 34337a8714 perf(site-call-audit): OPTION (RECOMPILE) on the optional-parameter query page 2026-07-09 09:12:50 -04:00
Joseph Doherty 6f5d60070c perf(notification-outbox): single-query KPI aggregation; oldest-pending no longer materializes the live queue 2026-07-09 09:12:00 -04:00
Joseph Doherty 71189298a2 perf(kpi-history): time-sliced batched purge replaces the single daily mega-DELETE 2026-07-09 09:06:42 -04:00
Joseph Doherty 5332912f26 docs(archreview): sync PLAN-04 tasks.json — Tasks 16-18 completed (Batch 6, reconciliation keyset cursor + abandoned-row audit + NodeB failover) 2026-07-09 08:58:39 -04:00
Joseph Doherty bfd2cc7e6f fix(audit-log): reconciliation pulls fail over to site NodeB when NodeA is unreachable
SiteEntry gains an additive FallbackGrpcEndpoint; SiteEnumerator now picks NodeA
else NodeB as primary (a NodeB-only site is no longer skipped) and carries a
distinct NodeB as the fallback. Both GrpcPull{AuditEvents,SiteCalls} clients dial
the primary first and, on a transport fault (Unavailable/DeadlineExceeded/
Cancelled/HttpRequestException/SocketException/OperationCanceled), fail over to
NodeB once before collapsing to empty — a mapping/unexpected fault does NOT fail
over. Keeps the reconciliation loss-recovery net available during a NodeA outage.
2026-07-09 08:57:06 -04:00
Joseph Doherty 0385942c3f feat(audit-log): permanently-abandoned reconciliation rows leave a durable ReconciliationAbandoned audit record
When a pulled AuditEvent fails to insert on every retry up to the permanent-
abandon threshold, the reconciliation actor now writes ONE synthetic
ReconciliationAbandoned row (new AuditKind) alongside the Critical log line —
fresh EventId, Status=Failed, channel preserved from the lost row, Extra carrying
the abandoned EventId + source site + final error — via the same
ScadaBridgeAuditEventFactory the ingest path uses. Best-effort in its own
try/catch so it never blocks the cursor twice. The permanent loss is now
queryable in the Audit Log, not only in a rotating log file.
2026-07-09 08:50:44 -04:00
Joseph Doherty 20098c6108 fix(site-call-audit): composite keyset cursor eliminates the single-timestamp reconciliation pin; pinned state now a published event
Reconciliation cursor becomes composite (UpdatedAtUtc, TrackedOperationId);
IPullSiteCallsClient/GrpcPullSiteCallsClient forward the Task-15 after_id keyset
(additive param, null preserves the legacy inclusive >= contract). A burst
sharing one exact UpdatedAtUtc now drains via the id tiebreak instead of pinning
forever. A legacy site that ignores after_id is latched + published as
SiteCallReconciliationPinnedChanged on the EventStream (transition-only),
replacing the prior silent log line.
2026-07-09 08:44:07 -04:00
Joseph Doherty 1f4c0b67ca docs(archreview): sync PLAN-04 tasks.json — Tasks 13-15 completed (Batch 5, operator-identity audit + reconciliation keyset) 2026-07-09 08:32:06 -04:00
Joseph Doherty a608d7a79b feat(site-call-audit): additive (UpdatedAtUtc, TrackedOperationId) keyset in the reconciliation pull contract
Site side: additive proto after_id field, ReadChangedSinceAsync gains an optional
afterId cursor and deterministic (UpdatedAtUtc, TrackedOperationId) ordering — a
batch fully inside one UpdatedAtUtc instant no longer re-reads the same page
forever. Absent/empty afterId preserves the exact legacy inclusive >= contract, so
an older central is unaffected. Regenerated the checked-in gRPC C#.
2026-07-09 08:31:42 -04:00
Joseph Doherty 2c45c3238b feat(site-call-audit): relay Retry/Discard emit operator-identity audit rows
Additive RequestedBy on Retry/DiscardSiteCallRequest, plumbed from the Central UI.
On an Applied relay, SiteCallAuditActor emits one best-effort CachedResolve central
direct-write row (Submitted/Discarded) with the operator as Actor. Gated on an
injected ICentralAuditWriter (null in existing tests → no mirror read, unchanged);
the site remains the source of truth for the state change.
2026-07-09 08:21:41 -04:00
Joseph Doherty 50bb1ef8ab feat(notification-outbox): operator Retry/Discard emit audit rows with operator identity
Additive RequestedBy on Retry/DiscardNotificationRequest, plumbed from the Central
UI. RetryAsync emits a NotifyDeliver Submitted row (records who un-parked); Discard
stamps the operator on the Terminal row. Best-effort — audit failure never aborts
the action.
2026-07-09 08:12:47 -04:00
Joseph Doherty f2f196aa6d docs(archreview): sync PLAN-04 tasks.json — Tasks 10-12 completed (Batch 4, backlog KPI + SiteCalls freshness + EF retry) 2026-07-09 08:04:11 -04:00
Joseph Doherty 1dd89d523b fix(config-db): EnableRetryOnFailure fleet-wide + execution-strategy wrap of the combined-telemetry dual-write
Installs SqlServerRetryingExecutionStrategy (5 retries, 30s cap) so transient SQL
faults retry transparently on every read-side path. Manual transactions aren't
auto-retried, so AuditLogIngestActor's idempotent dual-write is wrapped in an
explicit CreateExecutionStrategy().ExecuteAsync to become retriable. Verified
empirically that existing manual transactions (BundleImporter) are unaffected —
EF skips the strategy while a transaction is already active.
2026-07-09 08:03:30 -04:00
Joseph Doherty 6b06d1efcb fix(site-call-audit): same-rank freshness tiebreaker — retrying calls no longer freeze RetryCount/LastError at first write
Within an equal NON-terminal rank the newest UpdatedAtUtc wins, unfreezing the
Attempted-phase RetryCount/LastError/HttpStatus. Terminal ranks (>=3) are
excluded from the tiebreaker so terminal immutability is preserved (Delivered
never overwrites Parked); equal stamps stay idempotent, lower rank stays a no-op.
2026-07-09 07:56:39 -04:00
Joseph Doherty 1b53de1933 fix(kpi-history): backlogTotal trend records the real site-backlog aggregate instead of a hardwired zero 2026-07-09 07:51:55 -04:00
Joseph Doherty 1dd993ec2f docs(archreview): sync PLAN-04 tasks.json — Tasks 7-9 completed (Batch 3, append-only-honesty docs + SiteCalls index + exec-tree window bound) 2026-07-09 07:11:26 -04:00
Joseph Doherty 5d0fcd4e66 perf(audit-log): bound GetExecutionTreeAsync edge scan to a root-anchored time window (partition elimination) 2026-07-09 07:10:35 -04:00
Joseph Doherty f1957606ff perf(site-call-audit): filtered IX_SiteCalls_NonTerminal index — KPI predicates seek the live queue instead of scanning 365 days 2026-07-09 07:06:50 -04:00
Joseph Doherty eb69b93947 docs(audit-log): document append-only enforcement honestly (CI guard default; DB roles are optional DBA hardening; purger now has CREATE TABLE + scoped DELETE) 2026-07-09 07:03:10 -04:00
Joseph Doherty 2a080a3e12 docs(archreview): sync PLAN-04 tasks.json — Tasks 4-6 completed (Batch 2, maintenance-timeout + purge-failure health + purger grants) 2026-07-09 06:53:46 -04:00
Joseph Doherty 7555e65746 fix(config-db): grant CREATE TABLE + scoped DELETE to scadabridge_audit_purger so a segregated maintenance principal can actually run the purge 2026-07-09 06:52:44 -04:00
Joseph Doherty 14c4df54f9 feat(audit-log): surface partition-purge failure as health event + counter 2026-07-09 06:49:28 -04:00
Joseph Doherty b59aa2d717 fix(audit-log): explicit long CommandTimeout on partition-switch and per-channel purge maintenance paths 2026-07-09 06:43:23 -04:00
Joseph Doherty 1b6b0aab2a docs(archreview): sync PLAN-04 tasks.json — Tasks 1-3 completed (Batch 1, site retention chain) 2026-07-09 06:22:42 -04:00
Joseph Doherty 74aa872c07 feat(audit-log): daily site SQLite retention purge job (closes unbounded site DB growth)
SiteAuditRetentionService (site-only IHostedService) ticks PurgeExpiredAsync on
InitialDelay(5m)/PurgeInterval(24h, clamped >=1m), cutoff = UtcNow - RetentionDays.
Registered site-only in AddAuditLogHealthMetricsBridge; options bound from
AuditLog:SiteRetention. Per-tick failures swallowed. Added a test-only
PurgeIntervalOverride (mirrors SiteCallAuditOptions) so the ms-cadence tick test
can bypass the 1-min production clamp. Doc updated. (PLAN-04 Task 3, S1/U2)
2026-07-09 06:22:19 -04:00
Joseph Doherty b5271da5c8 feat(audit-log): SiteAuditRetentionOptions (7-day default, clamped 1-90)
RetentionDays clamped [1,90]; PurgeInterval clamped to >= 1 min (Timer spin
footgun); 5-min InitialDelay so a daily-recycled node still purges. (PLAN-04 Task 2)
2026-07-09 06:13:05 -04:00
Joseph Doherty 731ee69797 feat(audit-log): add site SQLite PurgeExpiredAsync honoring the ForwardState invariant
Retention purge deletes Forwarded/Reconciled rows older than the cutoff and
reclaims pages via incremental_vacuum; Pending rows are never purged on age
alone (would lose audit data that never reached central). Temp-table purge set
avoids the SQLite 999-param limit; runs under _writeLock. (PLAN-04 Task 1, S1/U2)
2026-07-09 06:11:54 -04:00
Joseph Doherty 1586e791f7 docs(archreview): sync stale Status Board — PLAN-01 & PLAN-03 now Complete/merged (78/192)
The Status Board still showed PLAN-01 as 'branch not yet merged' and PLAN-03
as 'In progress, 0 done'; both are 100% complete and on main (PLAN-01
ec47cb56, PLAN-03 c7a0c922). Update the two rows, correct the stale
'NOT yet merged' PLAN-01 narrative, and add a PLAN-03 completion line with
the corrected initiative total (78 of 192).
2026-07-09 06:00:37 -04:00
Joseph Doherty 9049690bdb fix(centralui-tests): register stub IBrowseService in SecuredWritesTests harness (PLAN-07 known-issue resolved)
The Secured Writes page embeds NodeBrowserDialog (@inject IBrowseService) for
the OPC UA tag picker; bUnit resolves the injected service at render time, so
three SecuredWritesTests failed with 'no registered service of type
IBrowseService'. Register a Substitute.For<IBrowseService>() singleton in the
fixture (mirrors NodeBrowserDialogSearchTests). Suite now 7/7 green.

Marks the PLAN-07 'Post-plan known issues' entry and the 00-MASTER-TRACKER
registry cross-reference RESOLVED.
2026-07-09 05:55:53 -04:00
Joseph Doherty 765ff131d8 docs(archreview): log SecuredWrites IBrowseService bUnit-harness failures against PLAN-07 (pre-existing, surfaced during PLAN-03 full-suite run) 2026-07-09 05:46:29 -04:00
Joseph Doherty c7a0c92244 docs(archreview): sync PLAN-03 tasks.json — Tasks 25-26 completed (Batch 9, plan complete 26/26) 2026-07-09 02:13:13 -04:00
Joseph Doherty 1db0c32115 docs(requirements): sync SiteRuntime + DCL specs with hardening changes (C3/C4/C5 + S1-S6, P2/P4/P6, UA1/UA4 behavior notes) 2026-07-09 02:13:13 -04:00
Joseph Doherty c9ac075e39 fix(site-runtime,dcl): low-severity cleanups — observed adapter dispose (S9), alarm-filter overwrite warning (S10), bounded-channel comment (C2), invariant-culture condition fallback (C6) 2026-07-09 02:09:06 -04:00
Joseph Doherty 30886c9426 docs(archreview): sync PLAN-03 tasks.json — Tasks 22-24 completed (Batch 8) 2026-07-09 01:27:21 -04:00
Joseph Doherty 29ee9176a7 feat(site-runtime): wire ConfigFetchRetryCount into the standby replicated-config fetch (UA2) 2026-07-09 01:26:58 -04:00
Joseph Doherty c457e8f464 feat(site-runtime): cert-trust reconcile-on-join — singleton pushes trusted certs to (re)joining site nodes (UA1) 2026-07-09 01:22:13 -04:00
Joseph Doherty 39976772d9 feat(site-runtime): CertStoreActor exports trusted certs (thumbprint+DER) for node reconciliation (UA1 groundwork) 2026-07-09 01:15:51 -04:00
Joseph Doherty 667d863e58 docs(archreview): mark PLAN-03 Tasks 19-21 (Batch 7) completed in tasks.json 2026-07-09 01:10:18 -04:00
Joseph Doherty 2efe1a4202 feat(site-runtime): persist native-alarm display metadata so rehydrated conditions render fully (UA4) 2026-07-09 01:09:13 -04:00
Joseph Doherty 326d945d85 perf(site-runtime): coalesce native-alarm mirror persistence into batched flushes (P4) 2026-07-09 01:05:20 -04:00
Joseph Doherty d2feb92bfd fix(site-runtime): site SQLite runs WAL with an explicit busy-timeout floor (S8) — concurrent writers stop serializing on the rollback journal 2026-07-09 01:00:12 -04:00