Commit Graph

92 Commits

Author SHA1 Message Date
Joseph Doherty
4002f4197b docs(plan): Audit Log ExecutionId implementation plan 2026-05-21 14:37:12 -04:00
Joseph Doherty
6ffa47f258 docs(design): Audit Log ExecutionId universal correlation 2026-05-21 14:34:12 -04:00
Joseph Doherty
40955bbca6 docs(plan): mark audit-log follow-up tasks complete 2026-05-21 06:41:53 -04:00
Joseph Doherty
7a386a80ce docs(auditlog): mark follow-ups complete in roadmap; refresh stale comments 2026-05-21 06:39:49 -04:00
Joseph Doherty
5fe08eaceb docs(plan): audit-log deferred follow-ups implementation plan 2026-05-21 03:17:59 -04:00
Joseph Doherty
5492c94e2f docs(audit): roadmap closeout — all 8 milestones complete (#23)
Audit Log #23 implementation complete. M1-M8 merged to main. Full
solution 2,993 tests green, 0 failures. Records final state, the
v1.x deferrals (hash chain, Parquet, per-channel retention), and the
follow-ups noted during implementation (real gRPC push client, mapper
consolidation, Site Calls UI, multi-value filters, grid drag UX).
2026-05-20 22:16:53 -04:00
Joseph Doherty
263884fa63 docs(audit): add M8 CLI implementation plan (#23)
3 bundles: CLI audit command group (scaffold/query/export/verify-chain),
ManagementService endpoints, formatters + audit-config rename + README.
verify-chain is a v1 no-op stub; hash chain deferred to v1.x.
2026-05-20 21:39:29 -04:00
Joseph Doherty
2d13886286 docs(audit): add M7 Central UI implementation plan (#23)
8 bundles: page scaffold + rename, filter bar + grid, drilldown drawer,
drill-ins, KPI tiles, CSV export, permissions, Playwright. UI memory
constraints locked: Blazor Server + Bootstrap, no third-party libs.
2026-05-20 19:43:30 -04:00
Joseph Doherty
8c2382c2bc docs(audit): roadmap corrections after M6
M7 head records M6 realities:
- IAuditCentralHealthSnapshot exists; M7 dashboard reads it.
- SiteHealthReport.SiteAuditBacklog ready for per-site tiles.
- IAuditLogRepository.QueryAsync is the page's data source.
- Pre-existing AuditLog.razor rename to ConfigurationAuditLog.razor
  needs verification.
- OperationalAudit + AuditExport permission strings need to exist.
- Real gRPC pull client still deferred; doesn't gate M7.
2026-05-20 19:42:54 -04:00
Joseph Doherty
b0584f7a08 docs(audit): add M6 reconciliation+purge+partition+health plan (#23)
6 bundles: proto+site handler, reconciliation actor, purge actor with
drop-and-rebuild around UX index, partition maintenance, four health
metrics, integration tests. M5 realities baked in.
2026-05-20 17:44:12 -04:00
Joseph Doherty
db05af897e docs(audit): roadmap corrections after M5
M6 head records M5 realities:
- IOptionsMonitor hot-reload pattern verified; M6 retention config can
  reuse.
- AuditRedactionFailure counter site-only in M5; M6 wires central side.
- Filter integration is at 3 writer entry points; purge actor doesn't
  emit so no filter integration needed.
- SwitchOutPartitionAsync drop-and-rebuild dance required (M1 reality
  + M6-T4 already documents it).
- M6 should land the real ISiteStreamAuditClient (Option A) so push
  telemetry leaves NoOp behind.
2026-05-20 17:43:44 -04:00
Joseph Doherty
e7b40c1c50 docs(audit): add M5 payload+redaction implementation plan (#23)
4 bundles: filter+truncation, redactors (header/body/SQL-param), wire
into all emission paths + health metric, config+perf+safety-net.

Vocabulary translation locked: error-row cap (64 KB) on Status NOT IN
(Delivered, Submitted, Forwarded). Filter integration point in each
writer (FallbackAuditWriter, CentralAuditWriter, AuditLogIngestActor)
BEFORE storage call.
2026-05-20 16:56:56 -04:00
Joseph Doherty
dae6de2c48 docs(audit): roadmap corrections after M4
M5 head records M4 realities:
- AuditingDbConnection/Command/DataReader decorators need filter plug-in
  at WriteAsync emission point.
- CentralAuditWriter + FallbackAuditWriter are both filter integration
  points for the direct-write + chained-write paths.
- InboundAPI middleware RequestSummary populated, ResponseSummary=null
  pending response-body buffering decision in M5.
- UseWhen(/api/) path-scoped middleware gives natural per-target
  redaction hook.
- Error-row cap raised on Status IN (Failed, Parked, Discarded,
  Attempted, Skipped) per M1 vocab reconciliation.
2026-05-20 16:56:18 -04:00
Joseph Doherty
c410fc6d43 docs(audit): add M4 remaining-boundaries implementation plan (#23)
5 bundles: DB sync emissions, NotificationOutbox central, site Notify.Send,
Inbound API middleware, integration tests. M3-reality vocab baked in
(DbWrite/NotifyDeliver/NotifySend/InboundRequest/InboundAuthFailure).
2026-05-20 15:41:13 -04:00
Joseph Doherty
f48efa7ca8 docs(audit): roadmap corrections after M3
M4 head now records M3 realities:
- Vocabulary translation table from pre-M1 spec strings to M1-aligned
  enum values (DbWrite vs SyncWrite/SyncRead; NotifyDeliver vs
  Notification.Attempt/Terminal; InboundRequest/InboundAuthFailure vs
  ApiInbound.Completed; Failed vs PermanentFailure).
- Mapper consolidation: 4 DTO mappers exist; extract single helper
  before M4 adds more channels.
- OnCachedTelemetryWithoutDualWriteAsync test-mode fallback may be
  deprecated in M4.
- Site SQLite drain for OperationTrackingStore: only dual-write
  transaction writes central today; plan drain if M4 needs in-flight
  tracking visibility.
- SiteCallAuditActor wired but unused on M3 hot path; M4/M6 natural
  first direct caller.
2026-05-20 15:40:33 -04:00
Joseph Doherty
4ca0b3ce2a docs(audit): add M3 cached-operations implementation plan (#23)
7 bundles, 18 tasks. Vocabulary alignment baked in (CachedSubmit /
ApiCallCached / DbWriteCached / CachedResolve, statuses Submitted /
Forwarded / Attempted / Delivered / Failed / Parked / Discarded).

Recommended defaults locked: new IngestCachedTelemetry RPC, separate
CachedCallTelemetryForwarder reusing the SiteAuditTelemetryActor drain
loop, ScriptRuntimeContext wrapper for provenance, component-level e2e
in AuditLog.Tests via the extracted DirectActorSiteStreamAuditClient
helper.
2026-05-20 13:43:46 -04:00
Joseph Doherty
db34ba66bf docs(audit): roadmap corrections after M2
M3 head now records M2 realities:
- enum vocabulary (M1-aligned) drives CachedSubmit/ApiCallCached/etc.
- NoOpSiteStreamAuditClient stays until M6; M3 e2e tests reuse Bundle H's
  DirectActorSiteStreamAuditClient (extract to Integration/Infrastructure/).
- Mapper duplication note (gRPC handler inlines DTO->entity decoding;
  consider moving AuditEventMapper to Commons in M3).
- AuditIngestAskTimeout=30s hardcoded; M3 may expose via options.
- CachedCallTelemetry message MUST be created from scratch (additive
  per Commons REQ-COM-5a; never renamed CachedOperationTelemetry).
- Central dual-write AuditLog + SiteCalls in one tx; reuse Bundle A
  duplicate-key swallow pattern for CachedCallId.
2026-05-20 13:41:34 -04:00
Joseph Doherty
eb22d3740f docs(audit): add M2 site-sync-pipeline implementation plan (#23)
8 bundles (A: race-fix + tiebreaker, B: SQLite writer + ring fallback,
C: gRPC proto + mapper, D: telemetry actor + ingest actor + gRPC handler,
E: host wiring, F: ESG audit emission via ScriptRuntimeContext wrapper,
G: SiteAuditWriteFailures health metric, H: component-level e2e test).

Brainstorm decisions locked: provenance via ScriptRuntimeContext wrapper,
push-primary telemetry, component-level e2e (no factory expansion),
mirror SiteEventLogger Channel<T> pattern for SqliteAuditWriter.
2026-05-20 12:08:01 -04:00
Joseph Doherty
ed442c7c8c docs(audit): roadmap corrections after M1
- M2 head: honor M1 vocabulary (ApiCall/Delivered), harden InsertIfNotExistsAsync
  (race window — first concurrent writer arrives in M2), add keyset-tiebreaker
  test (Bundle D reviewer's deferred recommendation), reuse MsSqlMigrationFixture
  + Xunit.SkippableFact pattern.
- M6-T4 (AuditLogPurgeActor): replace M1's NotSupportedException stub with the
  drop-and-rebuild dance for the non-aligned UX_AuditLog_EventId unique index;
  acknowledge the small outage window during partition SWITCH.
- M6-T5 (partition maintenance): note M1 ships 24 monthly boundaries (Jan 2026 -
  Dec 2027); service rolls the function forward via SPLIT RANGE.
2026-05-20 11:58:56 -04:00
Joseph Doherty
3f8b41182a docs(audit): add M1 foundation implementation plan (#23)
Bundles A-F per cadence memory. Brainstorm decisions locked:
infra/mssql test harness, single AuditEvent record (nullable IngestedAtUtc
+ ForwardState), PRIMARY filegroup, explicit index names.
2026-05-20 09:53:02 -04:00
Joseph Doherty
e8ae0e8544 docs(audit): add /goal prompt for autonomous milestone execution
Self-contained prompt to drive M1..M8 execution against the roadmap.
Embeds the per-milestone loop: branch -> brainstorm -> writing-plans ->
subagent-driven-development -> verify (tests gate) -> merge --no-ff ->
update downstream roadmap sections with realities learned -> proceed.

Honors the user-memory feedback (bundling cadence, UI rules, form layout,
recommendations-first) and the locked invariants (prose anchors not §N,
CachedCallTelemetry not CachedOperationTelemetry, audit failures never
abort actions, append-only, ForwardState purge invariant, no push, v1.x
features stay deferred). Termination criteria are explicit.
2026-05-20 09:39:33 -04:00
Joseph Doherty
39a3ca3347 docs(audit): expand M2-M8 to full TDD-detail tasks
Per user request: every milestone now carries bite-sized TDD tasks
(write failing test -> run failing -> implement -> run passing -> commit),
matching M1's density. Each task lists exact file paths, numbered steps,
and a commit message.

Task counts per milestone:
- M1 Foundation: 11
- M2 Site pipeline (sync-only): 12
- M3 Cached operations + dual-write (inlines #22 + cached-call tracking): 18
- M4 Remaining boundary emission: 12
- M5 Payload + redaction policy: 10
- M6 Reconciliation, purge, partition maintenance, metrics: 12
- M7 Central UI: 16
- M8 CLI: 9
Total: ~100 bite-sized tasks.

The roadmap remains the contract; per-milestone execution still goes
through brainstorm -> writing-plans -> subagent-driven-development to
produce a milestone-specific .tasks.json. Tasks in this roadmap will
shift slightly as M1 reveals codebase realities; treat them as the
intended shape rather than immutable IDs.
2026-05-20 09:32:59 -04:00
Joseph Doherty
d3d4a5b13d docs(audit): add 8-milestone code implementation roadmap
Roadmap covering Audit Log (#23) code implementation across 8 milestones
(M1 Foundation → M8 CLI). Reflects the actual state of the codebase —
all 22 prior components have source + tests, but Site Call Audit (#22)
and cached-call tracking are design-only despite being on main; their
minimum surface is inlined into M3.

M1 is laid out at full TDD-level task detail (11 bite-sized tasks).
M2–M8 are at milestone-shape detail (goals, files, task headlines,
acceptance criteria, risk callouts). Per-milestone bite-sized plans
will be generated by brainstorm + writing-plans when each milestone is
about to execute — locking 80 task cards now would mostly be stale by
M5 as M1 reveals codebase realities.

Critical path: M1 → M2 → (M3 ∥ M4 ∥ M5) → M6 → (M7 ∥ M8).

Spec: docs/requirements/Component-AuditLog.md + alog.md (commit
fec0bb1).
2026-05-20 09:22:18 -04:00
Joseph Doherty
acb160ecce docs(audit): fix plan reference to existing CachedCallTelemetry message
Task 2's spec reviewer flagged that the plan used a non-existent name
'CachedOperationTelemetry' when describing the additively-evolved cached
telemetry message. The existing message is 'CachedCallTelemetry'; renaming
would violate Commons REQ-COM-5a (additive-only). Plan now reflects the
in-place additive evolution and warns against rename.
2026-05-20 07:53:23 -04:00
Joseph Doherty
d93ca4c56e docs(audit): add implementation plan for centralized audit log
See docs/plans/2026-05-20-centralized-audit-log.md and peer .tasks.json.
17 tasks covering Component-AuditLog.md plus cross-references across
11 affected component docs, README, HighLevelReqs, and CLAUDE.md.
Spec is alog.md at commit fec0bb1.
2026-05-20 07:32:47 -04:00
Joseph Doherty
a08ad09514 docs(plans): add cached-call tracking implementation plan 2026-05-19 11:30:21 -04:00
Joseph Doherty
e7ed858920 docs(plans): design cached-call tracking with trackable IDs
Add TrackedOperationId handles to CachedCall/CachedWrite under a unified tracking model. New site-local tracking table is the status source of truth; new central Site Call Audit component (#22) mirrors status via telemetry, exposes KPIs and a Site Calls UI page with central->site Retry/Discard.
2026-05-19 11:26:37 -04:00
Joseph Doherty
7ecf6448e3 docs(plans): mark Notifications nav group tasks complete 2026-05-19 06:31:23 -04:00
Joseph Doherty
0904401f1e docs(plans): implementation plan for Notifications nav group 2026-05-19 05:08:52 -04:00
Joseph Doherty
113f00a6fa docs(plans): design for Notifications nav group 2026-05-19 05:01:58 -04:00
Joseph Doherty
b88c75c116 docs(plans): track Task 19 follow-ups (site DI cleanup, sandbox API, SourceScript) 2026-05-19 02:35:47 -04:00
Joseph Doherty
435c853dce docs(plans): track SMTP-helper deduplication follow-up 2026-05-19 01:33:21 -04:00
Joseph Doherty
dff9e0aa76 docs(plans): code implementation plan for the notification outbox
26 TDD tasks across six phases — data layer, message contracts, the new
ScadaLink.NotificationOutbox project (actor, dispatcher, Email adapter),
site S&F retarget and central wiring, Central UI, and verification.
2026-05-19 00:39:10 -04:00
Joseph Doherty
f177b58699 docs(plans): implementation plan for the notification outbox
11 bite-sized documentation tasks covering the new component doc and
revisions across the affected component docs, README, and CLAUDE.md,
with a final cross-reference consistency sweep.
2026-05-18 23:01:30 -04:00
Joseph Doherty
bbfa0c515e docs(plans): fold refinement decisions into notification outbox design
Resolves the six open questions: host-level forward-retry config,
Notify.Status returns a status record, 10-min stuck threshold, a
site-local Forwarding state, site-side logging of forward failures
only, and point-in-time KPIs computed from the Notifications table.
2026-05-18 22:57:45 -04:00
Joseph Doherty
d4e86c1b1d docs(plans): design for central notification outbox
Captures the basic design for a reliable notification outbox: sites
store-and-forward notifications to the central cluster, which logs
them to a type-agnostic Notifications table (single audit source) and
delivers them via per-type adapters with retry, parking, and KPIs.
2026-05-18 22:54:17 -04:00
Joseph Doherty
06462a0100 feat(template-engine): contained names for composition-derived templates
A composition-derived template now stores its contained name — the
composition slot's InstanceName (e.g. "Pump"), unique only within its
owner — instead of the dotted global path ("Motor Controller.Pump").
The qualified hierarchical name is computed on read.

- TemplateNaming.QualifiedName: walks the OwnerCompositionId chain to
  build the dotted path; null-safe, cycle-guarded.
- TemplateConfiguration: the unique index on Template.Name becomes
  filtered (WHERE IsDerived = 0) — base templates stay globally unique;
  derived templates' uniqueness is the existing (TemplateId,
  InstanceName) index on TemplateComposition.
- Migration ContainedDerivedTemplateNames: rewrites derived rows to the
  contained name; Down rebuilds the dotted names via a recursive CTE
  before restoring the global index.
- TemplateService: composition create/rename store the contained name;
  the dotted-name collision pre-checks and cascade-rename are removed
  (a slot rename no longer touches nested derived templates).
- TemplateEdit: title shows the contained name; the qualified path is a
  breadcrumb subtitle; "composed inside" uses the owner's qualified name.

TDD: 4 TemplateNaming tests + updated composition tests. TemplateEngine
293, ConfigurationDatabase 114, CentralUI 316 green. Migration applied to
the dev cluster and verified in the browser (Motor Controller.Pump now
titled "Pump"; nested Motor Controller.Pump.TempSensor resolves).

Design: docs/plans/2026-05-18-contained-template-names-design.md
2026-05-18 17:50:30 -04:00
Joseph Doherty
2d4b287ab2 docs(plans): design for contained names on composition-derived templates 2026-05-18 17:37:43 -04:00
Joseph Doherty
437fe154e7 feat(triggers): add WhileTrue fire mode for Conditional/Expression script triggers
Conditional and Expression script triggers gain an optional `mode` field
in their TriggerConfiguration JSON:

- OnTrue (default): unchanged edge/per-change firing. An absent mode field
  parses as OnTrue, so every existing trigger config behaves identically.
- WhileTrue: fires on the false->true edge, then re-fires on a periodic
  timer while the condition holds; stops on the true->false edge. The
  re-fire cadence is the script's MinTimeBetweenRuns; with none configured
  the trigger degrades to a single edge fire and logs a warning.

ScriptActor tracks condition truth state and manages a dedicated
"whiletrue-trigger" timer. ScriptTriggerConfigCodec and ScriptTriggerEditor
round-trip the mode and expose an OnTrue/WhileTrue selector for the two
trigger kinds. Design: docs/plans/2026-05-18-whiletrue-trigger-mode-design.md

Tests: 7 ScriptActor runtime tests (edge fire, timer re-fire, stop,
re-arm, no-MinTimeBetweenRuns degrade, OnTrue regressions) + 14 codec /
editor tests. SiteRuntime suite 206 green, CentralUI suite 295 green.
2026-05-18 10:44:11 -04:00
Joseph Doherty
19870d1f8f docs(plans): design for WhileTrue conditional/expression trigger mode 2026-05-18 10:30:22 -04:00
Joseph Doherty
8050a1996f docs(plans): implementation plan for expression triggers 2026-05-16 05:25:10 -04:00
Joseph Doherty
c94d3b7570 docs(plans): design for expression-based script & alarm triggers
Captures the brainstormed design for a new Expression trigger: a read-only
boolean C# expression evaluated on attribute updates, edge-triggered for
scripts and level-based for alarms, compiled against a restricted read-only
globals type.
2026-05-16 05:21:57 -04:00
Joseph Doherty
7044791a55 docs(plans): scrub LmxProxy references from design plans
Remove the LmxProxy work package (WP-8) from phase-3b, the CD-DCL-1..6
protocol details, Q9/Q-P3B-2 from the questions log, the LmxProxy
component-design rows in requirements-traceability, and the inline
mentions across phase-0, phase-4, the gRPC streaming plans, and the
primary/backup data-connection plans.
2026-05-13 13:30:07 -04:00
Joseph Doherty
783da8e21a feat(ui): structured editors for script schemas and alarm triggers
Replace raw-JSON text inputs with rich UI: script parameter/return types use
a JSON Schema builder (SchemaBuilder + JsonSchemaShapeParser, with a migration
to convert existing definitions); alarm trigger config uses a type-aware
editor with a flattened attribute picker (AlarmTriggerEditor). AlarmActor
gains optional direction (rising/falling/either) on RateOfChange triggers.
2026-05-13 00:33:00 -04:00
Joseph Doherty
552c9e4065 docs(templates): record phase 4-9 completion + verification TODOs
All nine derive-on-compose phases are now implemented. The status doc
captures what shipped per phase, what was deferred (LockedInDerived
override warning toast, SCADA008 base-Parent hint), and the live-DB /
UI smoke checks worth running before merge.
2026-05-12 08:59:19 -04:00
Joseph Doherty
8b8b85c839 docs(templates): record phase 2+3 completion in status doc
Phase 1 → 3 marked done; remaining work is phases 4-9. Sanity script now
targets the post-Phase-3 commit (03a8c4a) and notes the pre-existing
NU1608 build error in IntegrationTests / Host.Tests so future sessions
don't chase a phantom regression.
2026-05-12 08:31:20 -04:00
Joseph Doherty
91b786eb1c docs(templates): derive-on-compose phase status + resume plan
Companion to the design doc — captures current state, the four
decisions already made, what's done (phase 1, commit 5615f3d),
and a full play-by-play for phases 2 through 9 with exact files,
methods, and tests to touch. Written so a future session after
context compaction can pick up cleanly.
2026-05-12 08:18:43 -04:00
Joseph Doherty
a968cefbc2 docs(templates): record derive-on-compose decisions (naming, migration, tree UX) 2026-05-12 08:13:11 -04:00
Joseph Doherty
68548432b3 docs(templates): design for derive-on-compose specialization
Aveva-style composition: composing $Sensor into $Pump creates a
derived template Pump.TempSensor that inherits from $Sensor and can
override values, override script bodies, add new fields, with
LockedInDerived on the base preventing specific overrides.

Schema sketch: Template gains IsDerived + OwnerCompositionId;
TemplateAttribute/Script gain IsInherited + LockedInDerived.
TemplateComposition.ComposedTemplateId pivots to point at the
derived template (the base is reachable via derived.ParentTemplateId).

Phased rollout (9 phases), starting from additive schema, then
flow change for new compositions, then EF Core migration of
existing data, then resolution, lock semantics, tree UI, derived
template edit UI, base template lock-toggle UI, editor metadata
simplification (multi-parent picker becomes mostly obsolete —
derived templates always have a single owner).

Open questions captured at the end for review before phase 1.
2026-05-12 08:12:12 -04:00
Joseph Doherty
3ed05f0595 docs(scripts): design for template-script scope access
Self / Children / Parent accessors with sync-indexer + async-method
shape. Flattening pipeline emits ScriptScope per resolved script;
ScriptCompilationService seeds the accessors at execution time with
no new actor messages or lookup paths.

Phased: (1) runtime accessors + Scope on ResolvedScript, (2)
flattening + deploy round-trip, (3) editor metadata fetch for child
+ parent shapes, (4) Monaco completion / hover / diagnostics
(SCADA006 unknown attribute, SCADA007 unknown composition).

Out of scope: per-template Roslyn-generated typed accessors,
locking-aware writes (covered by lock-enforcement pass), and
sibling-of-sibling chained navigation.
2026-05-12 05:38:58 -04:00