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
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
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
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
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
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
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
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
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
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
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
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
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
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
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).