Commit Graph

676 Commits

Author SHA1 Message Date
Joseph Doherty 48813bba6f fix(localdb): sync endpoint enforces the configured ApiKey (scadaproj#36)
LocalDbSyncService never read request headers, so setting
LocalDb:Replication:ApiKey on both nodes left the passive endpoint open
(LocalDb-001). It now requires Authorization: Bearer <key> (constant-time)
before the handshake and before newest-wins takeover, refusing anything else
with Unauthenticated. A blank key keeps the old accept-all behaviour and
logs a startup warning. Wire-compatible: the initiator has always sent the
header when the key is set; ScadaBridge/OtOpcUa interceptors use the same key.

LocalDb 0.3.5 -> 0.3.6 (unpublished).

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:02:16 -05:00
Joseph Doherty d549a782fd fix(auth): user bind proves the bound account is the entry whose groups it returns (scadaproj#40)
BindMode=User bound as UserBindTemplate({0}) and then returned the groups of
whatever entry (UserNameAttribute={0}) found - two directory lookups that can
name two accounts (Auth-007). Reproduced live against GLAuth: mallory bound as
herself and signed in with boss's Admins group.

After the bind the service now asks RFC 4532 Who-am-I and requires the entry
to be that account (dn: -> DN match; AD u:DOMAIN\name -> sAMAccountName match).
Without a Who-am-I answer only a DN template is provable (bind DN must equal
the entry DN); anything else fails closed as GroupLookupFailed. {0} is
RFC 4514-escaped in DN templates; validator message leads with EXAMPLE\{0}.

Auth 0.4.3 -> 0.4.4 (unpublished).

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:02:16 -05:00
Joseph Doherty 5f5d684c44 fix(secrets): CLI set/rotate read the value from stdin or a masked prompt (scadaproj#35)
`secret set`/`rotate` took the plaintext as a positional argument (shell
history, process list), and Program handed argv to
Host.CreateApplicationBuilder, whose command-line config provider crashed on
a value like "-p=Hunter2Secret" with the value in the unhandled
FormatException on stderr.

- SetCommandLine: `set|rotate <name> [-|--value-stdin]`; no value reads piped
  stdin or prompts twice (masked) at a TTY; one trailing newline dropped;
  empty refused. The positional <value> still works and warns on stderr.
- host built from HostApplicationBuilderSettings without argv; startup
  failures print `secret: startup failed: <msg>` and exit 1
- unexpected trailing arguments reported by position, not echoed
- tests: SetCommandLineTests incl. two child-process runs of the built CLI;
  the -p=Hunter2Secret probe fails (leaks) on the old Program.cs
- docs: README, clustered-secrets runbook, CHANGELOG
- code-reviews: Secrets-017 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:06 -05:00
Joseph Doherty e1487fbf10 fix(secrets): UI reports a committed change as saved when its audit throws (scadaproj#34)
SecretEditor and ConfirmDeleteModal wrote the success audit inside the same
try as the store write, so a throwing IAuditWriter after a committed
add/rotate/delete showed an error, wrote a Failure audit and skipped the
resolver-cache eviction - the old plaintext kept resolving for the TTL.

The mutation try now covers only the cipher + store call; the success audit
is best-effort afterwards and logs a warning (no value) on failure, so the
change is reported saved, the callback fires and the cache is evicted.

- tests: AuditFailureAfterCommitTests (add, rotate, delete) with a throwing
  success audit; all fail without the fix
- code-reviews: Secrets-013 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:06 -05:00
Joseph Doherty 6112a9f447 fix(secrets): Akka replication actor starts with the host (scadaproj#33)
The replication actor was created only when something resolved ISecretStore
(store -> replicator -> SecretReplicationActorProvider.ActorRef). A node that
expanded its ${secret:} references pre-host and never touched the store at
runtime had no actor: no topic subscription, no announces, no pull replies.

AddZbSecretsAkkaReplication now registers SecretReplicationActorStarter, which
creates the actor on IHostApplicationLifetime.ApplicationStarted (after every
hosted service, incl. an Akka.Hosting AkkaHostedService registered later), or
in StartAsync when no lifetime is registered.

- tests: EagerActorStartTests (generic host in both registration orders with a
  no-actor-before-start control; bare container) - all fail without the fix
- docs: README, CHANGELOG
- code-reviews: Secrets-010 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:06 -05:00
Joseph Doherty f60f641069 fix(secrets): ${secret:} expansion survives configuration reload (scadaproj#32)
ExpandConfigurationAsync wrote through config[key] = ..., which only sets the
providers' in-memory Data; a file provider's reload replaces it, so editing
appsettings.json on a running service reverted every expanded key to its
${secret:...} placeholder.

- ConfigurationManager: expanded values are pinned in an internal overlay
  source appended last; its provider's Load() is a no-op and Build always
  returns the same provider, so reloads (and source rebuilds) keep the pins
- any other IConfigurationRoot: in-place write as before, re-applied from the
  root's reload token
- all references resolve before anything is written (a fail-closed miss no
  longer leaves the config half expanded)
- tests: SecretExpansionReloadTests (real JSON file + Reload(), options
  monitor, plain root); three fail without the fix
- docs: README reload note, CHANGELOG
- code-reviews: Secrets-003 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:06 -05:00
Joseph Doherty c3b07d6766 fix(secrets): SqlServer/Akka extensions take contentRootPath; last explicit root wins (scadaproj#31)
The SQL-Server extensions called the 3-arg AddZbSecrets, and the store-path
validator captured the content root of whichever call registered it first.
In hub mode no registration order applied the pre-host under-content-root
guard: extension-first dropped the app's 4-arg root, core-first lost the
replicator (#30). That is the 0.6.0 store-path hole, reopened.

- AddZbSecretsSqlServerStore / AddZbSecretsSqlServerReplication /
  AddZbSecretsAkkaReplication gain a trailing optional contentRootPath,
  forwarded to the 4-arg AddZbSecrets
- the root now lives in one SecretsContentRoot instance holder per service
  collection; the last explicit root wins, null never erases one; the
  IHostEnvironment fallback is unchanged
- tests: ContentRootTests (hub mode, three compositions, artifact asserted
  before the throw, positive control), last-explicit-wins theory in
  SecretsStorePathRulesTests, Akka forwarding test
- code-reviews: Secrets-002 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:06 -05:00
Joseph Doherty 015c556ced fix(secrets): SqlServer/Akka registration is order-independent; bump 0.6.6 (scadaproj#30)
AddZbSecretsSqlServerStore / AddZbSecretsSqlServerReplication (and the Akka
extension, same defect class) TryAdd-ed their ISecretStore, ISecretsStoreMigrator
and ISecretReplicator ahead of their internal AddZbSecrets call. An application
that had already called AddZbSecrets therefore silently kept the SQLite store
(shared-store mode) or NoOpSecretReplicator (hub/Akka modes).

They now call AddZbSecrets first and RemoveAll + AddSingleton their own
registrations; a later AddZbSecrets only TryAdds, so either order yields the
same graph with exactly one ISecretStore registration.

- tests: RegistrationOrderTests (SqlServer + Akka) build and resolve the real
  container in both orders; core-first fails without the fix
- Directory.Build.props 0.6.5 -> 0.6.6 (NOT packed or published)
- code-reviews: Secrets-001 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:00:05 -05:00
Joseph Doherty 1786bab0a0 fix(telemetry): redact after ConfigureLogger enrichers, keep colliding dictionary keys, require http(s) OTLP endpoint and validate the options overload; bump 0.2.1 (scadaproj#48)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:57:48 -05:00
Joseph Doherty 5bf2f2caf1 fix(theme): nav-state.js tracks wired sections in a WeakSet so enhanced navigation cannot stack toggle listeners; bump 0.5.2 (scadaproj#48)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:56:33 -05:00
Joseph Doherty ab3fa9ca83 fix(health): AkkaActiveNodeGate.ForOptions shares the probe's RolePreference scope; bump 0.3.1 (scadaproj#48)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:55:45 -05:00
Joseph Doherty 3ef43ca926 fix(overview): P3 review fixes 003-005 - no redirects on probes, per-target publish so a hung instance cannot stale the rest, probe docs (scadaproj#48)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:54:12 -05:00
Joseph Doherty 362ba8d75f fix(galaxyrepository): P3 review fixes 003-008 - cycle re-rooting, scaled stale threshold, notifier ordering, DI tests, Pkcs 9.0.21 pin, snapshot docs; bump 0.2.2 (scadaproj#47)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:52:00 -05:00
Joseph Doherty 90b691af84 docs(deployment): correct the QA leader-resolution note after scadaproj#43
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:48:49 -05:00
Joseph Doherty 414050935e fix(galaxyrepository): scoped browse surfaces ancestors as navigation-only nodes (scadaproj#42)
A browse-subtree scope such as Plant/LineA/* filtered out Plant and
Plant/LineA, so scoped lazy browse from the root was empty and
DiscoverHierarchy returned orphans. Out-of-scope ancestors of an allowed
object are now returned as navigation-only containers: attributes stripped,
child_has_children = true, and only their children on the path to an allowed
object are listed (siblings stay hidden). DiscoverHierarchy reports them in
GalaxyHierarchyQueryResult.NavigationAncestorCount; WatchDeployEvents
subtracts it so its scoped counts stay allowed-objects-only.

- tests: ScopedAncestorBrowseTests; deploy-event scope test updated
- IGalaxyBrowseScopeProvider documents the semantics; CLAUDE.md 0.2.1 notes
- code-reviews: GalaxyRepository-002 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:48:02 -05:00
Joseph Doherty 20cf3bfec7 fix(galaxyrepository): cap the per-entry filter memo at 64 signatures; bump 0.2.1 (scadaproj#41)
Both projectors memoized one full result list per filter signature with no
limit, and the signature is client-controlled (tag_name_glob '*', '**', ...),
so distinct globs retained memory until the next Galaxy deploy. A bounded FIFO
ProjectionMemo<T> now backs both memos; an evicted signature is recomputed.

- tests: ProjectionMemoBoundTests (500 distinct globs per projector, re-query
  after eviction)
- Directory.Build.props 0.2.0 -> 0.2.1 (NOT packed or published)
- code-reviews: GalaxyRepository-001 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:45:32 -05:00
Joseph Doherty a2e2f4b463 fix(theme): TechButton honours a caller-supplied disabled; bump 0.5.1 (scadaproj#44)
The trailing disabled="@Busy" overrode the splatted attribute, so a caller's
disabled was dropped whenever Busy was false and the button stayed clickable.
The effective state is now Busy || caller disabled (Blazor splat semantics).

- tests: caller disabled (true/"disabled"/""), false, and re-render toggle
- Directory.Build.props 0.5.0 -> 0.5.1 (NOT packed or published)
- code-reviews: Theme-007 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:44:14 -05:00
Joseph Doherty 6c81c0bc0a fix(overview): resolve a group's leader against its own members (scadaproj#43)
LeaderResolver searched every instance of the application, so co-located
groups (QA: ScadaBridge central-a and site-a both on vq03) got the
first-listed group's instance name on their leader chip. Resolution is now
scoped to the group's members; an unmatched address keeps its raw form.

- tests: QA co-located central/site layout; cross-group host not borrowed
- CLAUDE.md: correct the leader-resolution / 127.0.0.1 guidance
- code-reviews: Overview-002 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:43:18 -05:00
Joseph Doherty c25acd6093 fix(overview): one bad health response no longer stops the dashboard host (scadaproj#29)
ZbHealthReportClient caught only HttpRequestException/OperationCanceledException,
so an unknown Content-Type charset (InvalidOperationException from
ReadAsStringAsync) escaped the probe, faulted Task.WhenAll and the poller
BackgroundService, and StopHost took the dashboard down.

- client: final catch-all returns an Unreachable result for that instance
- poller: per-target Guarded probe + catch-and-log per sweep in ExecuteAsync
- tests: unknown charset (both tiers), arbitrary handler exception, sweep with
  one bad instance, and the BackgroundService survival loop
- code-reviews: Overview-001 Resolved; README regenerated

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:42:23 -05:00
Joseph Doherty 477245f5db docs(code-reviews): review Secrets, LocalDb, GalaxyRepository, Overview; re-review changed libs (scadaproj#15)
First reviews of Secrets, LocalDb, GalaxyRepository and Overview, and
2026-11-09 re-reviews of Auth, Health, Theme, Telemetry, Audit and
Configuration, all at 91fb2b4. 49 new open findings, each with a P1/P2/P3
priority and a failure scenario: 1 P1, 13 P2, 35 P3. README regenerated;
REVIEW-PROCESS, template and regen script now cover all ten libraries and
the Priority field.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:33:14 -05:00
Joseph Doherty 91fb2b48dd docs(health): GrpcDependencyHealthCheck has no consumers - keep it, correct SPEC/README (scadaproj#28)
SPEC 2.4 named OtOpcUa and MxGateway as users; neither is (verified against every
app's default branch; MxGateway worker IPC is named pipes, OtOpcUa deliberately
does not gate readiness on gateway channels). Keep the check (removal is a breaking
API change; small, tested building block), mark it as no-current-consumers in SPEC
2.4/6/7, both READMEs, the shared-contract doc and GAPS.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:19:09 -05:00
Joseph Doherty 726fbccf54 test(secrets): fix Windows failures - skippable permission tests, release SQLite pools before delete (scadaproj#27)
The two Unix-permission tests called Skip.IfNot from a plain [Fact] (xunit v2 +
Xunit.SkippableFact) so they failed on Windows; now [SkippableFact].
Resolver_Works_EndToEnd_ThroughDi calls SqliteConnection.ClearAllPools() before
deleting its temp DB.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:17:11 -05:00
Joseph Doherty 1baa384cbe fix(deps): Microsoft.Data.Sqlite 10.0.7 -> 10.0.13 in Secrets, LocalDb, Auth (scadaproj#26)
10.0.7's pool can hand one handle to two users (efcore#39009). 10.0.13 floors
SQLitePCLRaw 2.1.13, so the transitive lib.e_sqlite3 pin moves 2.1.12 -> 2.1.13
(the old pin is a NU1109 downgrade error). NuGetAudit clean; all suites green.
Versions bumped, NOT published: Secrets 0.6.5, LocalDb 0.3.5, Auth 0.4.3.
GalaxyRepository and Overview do not use Microsoft.Data.Sqlite.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:17:11 -05:00
Joseph Doherty 2fea619da3 docs(health): historiangw#87 fixed on main 5de3dd9 (not yet deployed)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:09:39 -05:00
Joseph Doherty 9cbe65c844 docs(health): re-verify per-app probe coverage against the SPEC; refresh GAPS and current-state (scadaproj#11)
Audited the registration and mapping code of each app at its origin default branch: OtOpcUa
c904472a, MxAccessGateway de86f8b8, ScadaBridge c7d868c5, HistorianGateway 4d6a9ad (all on Health
0.3.0, all MapZbHealth with default paths: /healthz runs nothing, /health/ready and /health/active
by tag). GAPS.md now carries the verified per-endpoint coverage matrix and a SPEC-by-app table;
each current-state doc lists exactly what each endpoint runs, and HistorianGateway gets its first
current-state doc.

Recorded: mxaccessgw#206 (worker-executable + galaxy-sql readiness checks, on /health/ready) is
merged; historiangw#87 (session-pool check latches 503 until restart) is still open at 4d6a9ad.
Deferred P2 downstream probes are now per-app; ScadaBridge IActiveNodeGate unification stays
deferred (shared rule already used, only the interface is local).

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 14:00:44 -05:00
Joseph Doherty f21f2e551d fix(secrets): make concurrent SQLite store initialisation safe; bump to 0.6.4 (unpublished) (scadaproj#25)
Several setups migrating one SQLite secret store at once (mxaccessgw's parallel test hosts on the
per-process store) failed intermittently on Windows with "cannot start a transaction within a
transaction" from SqliteSecretsStoreMigrator.MigrateAsync. That message means the BEGIN ran on a
handle that was already in a transaction: the pinned Microsoft.Data.Sqlite 10.0.7 can hand one
pooled handle to two owners (dotnet/efcore#39009, fixed in 10.0.13) and no 10.0.x refuses to
re-pool a handle whose rollback failed (dotnet/efcore#39082).

The migrator now serialises migrations of one store per process behind a gate keyed on the
normalised path, runs each attempt on a dedicated unpooled connection inside BEGIN IMMEDIATE, and
retries SQLITE_BUSY/SQLITE_LOCKED (another process past the busy timeout) with equal-jitter
exponential backoff. Store operations and the Microsoft.Data.Sqlite pin are unchanged.

Tests: concurrent-init (16 hosts x 20 rounds on fresh and existing stores, two path spellings,
16 AddZbSecrets containers running the pre-host migrator + hosted service), gate sharing/waiting,
BUSY retry past the 5 s busy timeout, transient-code classification.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 13:57:53 -05:00
Joseph Doherty 01c6dc1d02 build(libs): point package RepositoryUrl/PackageProjectUrl at scadaproj (the zb-mom-ww-* repos do not exist)
All 22 package projects named gitea.dohertylan.com/dohertj2/zb-mom-ww-<lib>,
which return 404; the libraries live as plain directories in scadaproj.
Takes effect on each library's next published version.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 13:42:29 -05:00
Joseph Doherty b4c38d0f0c docs(libs): refresh stale adoption-status text in hosted library docs (scadaproj#17)
Verified 2026-11-09 against each app's origin default branch pins
(OtOpcUa origin/master c904472a, mxaccessgw origin/main de86f8b8,
ScadaBridge origin/main 7153a197, HistorianGateway origin/main 4d6a9ad)
and the Gitea NuGet feed:
- Audit README: consumer matrix no longer says "adoption deferred" (all four
  apps pin 0.1.0, DEEP-adopted 2026-06-02; HistorianGateway row added);
  dead ~/Desktop/scadaproj-audit paths fixed.
- Configuration CLAUDE.md: drop "not yet pushed to remotes"; all four pin 0.1.0.
- Telemetry README/CLAUDE.md: all four apps call AddZbTelemetry and pin 0.1.0;
  HistorianGateway added to the consumer matrix.
- Secrets CLAUDE.md: lead now says family 0.6.3 on the feed, all four apps pin
  0.6.2, none pins 0.6.3 or Replicator.Grpc.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 13:40:30 -05:00
Joseph Doherty 1ab5ca026b feat(telemetry): AddZbSerilog minimum-level governance hook; bump to 0.2.0 (scadaproj#12)
Adds an additive AddZbSerilog(builder, configure, configureSerilog) overload
taking ZbSerilogOptions:
- MinimumLevelGovernor: Func<IConfiguration, LogEventLevel?> applied with
  MinimumLevel.Is AFTER ReadFrom.Configuration, so an app-owned key (e.g.
  ScadaBridge:Logging:MinimumLevel) outranks Serilog:MinimumLevel; null defers
  to config; Serilog:MinimumLevel:Override:* entries still apply.
- ConfigureLogger: runs last, for app filters/enrichers/sinks (ScadaBridge's
  standby active-node exclusion).

The two-argument overload is unchanged. 8 new tests (4 fail with the hook
neutralized). Telemetry + Telemetry.Serilog 0.1.0 -> 0.2.0, not published.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 13:39:06 -05:00
Joseph Doherty a05ce8c86d fix(secrets): pin transitive AngleSharp 1.5.0 to clear NU1902 from bunit 1.40.0 (scadaproj#24)
bUnit 2.x drops RenderComponent/AddTestAuthorization, so pin the parser
instead of migrating the Ui tests.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 12:37:33 -05:00
Joseph Doherty 243c3fa619 feat(auth): typed ApiKeyPepperUnavailableException for missing API-key pepper; bump to 0.4.2 (scadaproj#23)
Derives from InvalidOperationException so existing catches keep working.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 12:30:04 -05:00
Joseph Doherty 8bb2679e43 fix(secrets,auth): pin patched System.Security.Cryptography.Pkcs 9.0.21 so restore passes NuGetAudit (scadaproj#22)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 12:28:42 -05:00
Joseph Doherty d0d3058651 fix(secrets): jittered exponential backoff in SqlServerSecretsStoreMigrator + 8-way concurrent-migrate live test
Flat 100*attempt backoff let concurrent boots retry in lockstep (Auth's twin loop
exhausted retries ~1/60 eight-way boots). Backoff is now exponential with equal
jitter, via an injectable Random for deterministic tests.

Refs scadaproj#18
2026-11-09 12:05:33 -05:00
Joseph Doherty a16567b8bf docs(index): HistorianGateway native event reads return rows on gRPC-only main (historiangw#24 closed); SysTimeSec #29 still open 2026-11-09 11:44:33 -05:00
Joseph Doherty 66c3e060ae docs(index): mxaccessgw Contracts/Client 0.3.0 published; main e5e7f18 2026-11-06 16:01:22 -05:00
Joseph Doherty 19d59915ea docs(index): HistorianGateway main now gRPC-only (swap-historian-client merged, 99b4a7d) 2026-11-06 15:52:22 -05:00
Joseph Doherty 6b737b12b1 docs(index): refresh ScadaBridge/HistorianGateway rows; record HG 2026-10-16 diagnostics + Int1 milestones 2026-11-06 15:38:26 -05:00
Joseph Doherty 484d530e65 docs: point remaining Auth/observability GAPS items at issues (#20, ScadaBridge#81) 2026-11-06 15:36:33 -05:00
Joseph Doherty fd180e576f docs: point markdown backlogs at Gitea issues (status lives in Gitea)
Adds the family backlog standard to the root index (open work = Gitea issues,
shared label set, BTDB excluded) and converts components/*/GAPS, hosted-lib
CLAUDE.md notes, code-reviews, infra/glauth and plan docs to banner + issue
pointers (scadaproj #5-#19, ScadaBridge#79).
2026-11-06 15:34:30 -05:00
Joseph Doherty 967da5b43b feat(theme): upstream router controls and responsive shell fixes 2026-11-04 00:52:25 -05:00
Joseph Doherty 6ad15fcf93 Index: record mxaccessgw operator-authenticated alarm ack (proto change)
Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
2026-10-20 08:52:46 -04:00
Joseph Doherty 795af1da71 Record merged-main verification and remaining issue gates 2026-10-13 22:10:40 -04:00
Joseph Doherty 65c6b99cf2 Record verified driver fixture cleanup merge 2026-10-13 21:08:01 -04:00
Joseph Doherty 420c2e0783 Record verified freshness merge and pending cleanup follow-ups 2026-10-13 19:04:46 -04:00
Joseph Doherty 3cacdca861 Record verified ServiceLevel fixes and freshness follow-up 2026-10-13 16:00:45 -04:00
Joseph Doherty ade49c0935 Record verified OPC UA CI closures and pending ServiceLevel work 2026-10-13 13:58:03 -04:00
Joseph Doherty 02480d4fbc docs: record verified OPC UA CI and S7 evidence corrections 2026-10-13 11:57:35 -04:00
Joseph Doherty 87bb647efb Record verified keepalive survival test fix 2026-10-13 11:02:00 -04:00
Joseph Doherty a0cec479b6 Record explicit live-test gates and CI follow-ups 2026-10-13 10:37:45 -04:00
Joseph Doherty 91baba8fcc Record verified corrected Gateway rollout 2026-10-13 10:15:45 -04:00