Commit Graph

338 Commits

Author SHA1 Message Date
Joseph Doherty 7c2f28313d Merge B1-twincat: v3 RawPath read-path 2026-07-15 20:20:49 -04:00
Joseph Doherty 63b7d6ca5d Merge B1-ablegacy: v3 RawPath read-path 2026-07-15 20:20:49 -04:00
Joseph Doherty 1748af7261 Merge B1-abcip: v3 RawPath read-path 2026-07-15 20:20:49 -04:00
Joseph Doherty c0379742bc v3(b1-abcip): RawPath read-path for AbCip (multi-device)
Apply the reviewed Modbus exemplar to the AbCip multi-device driver:
- Mapper AbCipEquipmentTagParser -> AbCipTagDefinitionFactory; TryParse -> FromTagConfig(tagConfig, rawPath, out def); drop leading-{ heuristic + the deviceHostAddress key; add inverse ToTagConfig; round-trip arrays/members/safety for coverage.
- Options: Tags (typed) -> RawTags (RawTagEntry list); Devices list kept.
- Driver: byName-only resolver over _tagsByRawPath (Ordinal); table built from RawTags via FromTagConfig, threading entry.DeviceName (device routing key) + entry.WriteIdempotent onto the def. Multi-device routing keys on DeviceName-or-host via a _devicesByRoutingKey index; discovery groups _declaredTags by RoutesToDevice.
- Factory: retire the pre-declared Tags DTO/BuildTag; bind List<RawTagEntry> RawTags.
- Probe: derive probe tag path from RawTags via the mapper.
- Cli BuildOptions projects typed tags -> RawTagEntry (ToTagConfig).
- Tests: AbCipRawTags helper (typed def -> RawTagEntry); parser tests -> FromTagConfig; blob-fallback driver tests rewritten to author via RawTags + read by RawPath. Driver.AbCip.Tests 342/342, Cli.Tests 42/42.

TODO(v3 WaveC): DeviceName becomes a pure logical name once the device connection host moves to the Device row's DeviceConfig.
2026-07-15 20:18:28 -04:00
Joseph Doherty a53be2af65 v3(b1-twincat): apply Modbus exemplar pattern to TwinCAT (multi-device)
- Rename TwinCATEquipmentTagParser -> TwinCATTagDefinitionFactory; TryParse ->
  FromTagConfig(tagConfig, rawPath, out def). Drop leading-'{' heuristic + the
  deviceHostAddress key; def.Name = rawPath. Keep strict-enum + Inspect; add a
  Structure guard (sole tag path now) + inverse ToTagConfig.
- Options: Tags -> RawTags (RawTagEntry); keep Devices + protocol fields.
- Driver: _tagsByRawPath (Ordinal); byName-only resolver; build table from
  _options.RawTags via FromTagConfig, threading WriteIdempotent + DeviceName.
  Multi-device: ResolveDeviceHost matches entry.DeviceName against options.Devices
  (by name, then host) -> def.DeviceHostAddress (TODO(v3 WaveC): live host from the
  Device row's DeviceConfig). DiscoverAsync now emits from the table.
- Factory: retire pre-declared tags[] DTO path; bind rawTags; keep Devices.
- Cli: BuildOptions serialises typed defs -> RawTagEntry via ToTagConfig.
- Tests: TwinCATRawTags helper; migrate Tags= -> RawTags=; rename parser tests to
  FromTagConfig; equipment/resolve-host/array tests delivered via RawTags.

Gate: Contracts + Driver build green; Driver.TwinCAT.Tests 192 pass, Cli.Tests 70 pass.
2026-07-15 20:11:23 -04:00
Joseph Doherty 1e26b9ab58 v3(s7): apply Modbus RawTags exemplar to the S7 driver
Rename S7EquipmentTagParser -> S7TagDefinitionFactory; TryParse(reference)
-> FromTagConfig(tagConfig, rawPath, out def) (drops the leading-{ heuristic,
keys the def by RawPath, adds inverse ToTagConfig). Replace S7DriverOptions.Tags
(pre-declared S7TagDefinition list) with RawTags (IReadOnlyList<RawTagEntry>);
the driver builds its RawPath->def table from RawTags at Initialize via the
factory (skip+log on miss), resolver is byName-only, DiscoverAsync + init guards
+ address pre-parse now run off that table. Factory retires the pre-declared DTO
tag path (RawTags binding only). Cli BuildOptions serialises typed defs to
RawTagEntry. Tests migrated to a S7RawTags helper + FromTagConfig; parser test
files renamed. Coordinator's EquipmentTagConfigInspector S7 rename left to Wave-B
coordinator; Driver.S7.IntegrationTests (Docker-gated) left red per scope.

Driver.S7 + Cli build green; Driver.S7.Tests 264/264, S7.Cli.Tests 49/49.
2026-07-15 20:11:04 -04:00
Joseph Doherty 3878b51e97 v3(ablegacy): apply Modbus exemplar — RawTags delivery + RawPath mapper
- Rename AbLegacyEquipmentTagParser → AbLegacyTagDefinitionFactory; TryParse →
  FromTagConfig(tagConfig, rawPath, out def). Drop the leading-{ heuristic and the
  deviceHostAddress key; def.Name = rawPath. Add inverse ToTagConfig. Keep strict-enum
  guards + Inspect.
- Options: Tags(IReadOnlyList<AbLegacyTagDefinition>) → RawTags(IReadOnlyList<RawTagEntry>).
- Driver: _tagsByRawPath (Ordinal); byName-only resolver; build table from _options.RawTags
  via FromTagConfig, threading WriteIdempotent + resolving RawTagEntry.DeviceName to the
  owning device host (ResolveDeviceHost; TODO(v3 WaveC) for the live Device-row host).
  DiscoverAsync + family-profile validation iterate the built table. Probe picks its address
  from RawTags via the mapper.
- Factory: retire the pre-declared tag DTO path; bind List<RawTagEntry>? RawTags; keep Devices.
- Cli BuildOptions: deliver typed defs as RawTagEntry via ToTagConfig.
- Tests: AbLegacyRawTags helper; migrate Tags= → RawTags; parser tests → FromTagConfig;
  equipment-ref driver/ResolveHost tests re-authored as RawTagEntry + read by RawPath.

Driver+Contracts+Cli green; 213 driver + 36 Cli tests pass.
2026-07-15 20:09:46 -04:00
Joseph Doherty aafb9d4929 v3(b1-modbus): RawPath identity seam — RawTagEntry-driven Modbus driver
Wave-B EXEMPLAR. Retire the pre-declared/blob-parse tag model; the driver now
builds its RawPath -> definition table from the artifact's RawTagEntry set via a
pure mapper.

- Contracts: rename ModbusEquipmentTagParser -> ModbusTagDefinitionFactory;
  TryParse(reference) -> FromTagConfig(tagConfig, rawPath) (def.Name = rawPath,
  no leading-brace heuristic); add inverse ToTagConfig(def) serializer; keep all
  strict-enum reads + guards; also read stringByteOrder/deadband/coalesceProhibited
  so a RawTagEntry round-trips the full authored def. Inspect() unchanged.
- Options: Tags(IReadOnlyList<ModbusTagDefinition>) -> RawTags(IReadOnlyList<RawTagEntry>).
- Driver: _tagsByName -> _tagsByRawPath (Ordinal); resolver byRawPath-only; build
  the table from RawTags at Initialize threading entry.WriteIdempotent onto each def;
  Discover/Teardown updated.
- Factory: remove ModbusTagDto/BuildTag/ValidateStringLength + ConfigDto.Tags; bind
  RawTags from driver-config JSON.
- CLI ModbusCommandBase.BuildOptions: serialise typed defs -> RawTagEntry via ToTagConfig.
- Tests: migrate Tags= -> RawTags via ModbusRawTags.Entries helper; parser tests ->
  FromTagConfig(rawPath); factory String-length/enum tests re-seated on the mapper seam.
- Propagated rename to ControlPlane EquipmentTagConfigInspector (outside Modbus projects).

Modbus.Tests 315/315, Addressing.Tests 161/161, Cli.Tests 72/72 green.
Docker-gated Driver.Modbus.IntegrationTests left untouched (won't compile against
the new API; expected).
2026-07-15 19:50:10 -04:00
Joseph Doherty 679484ae78 feat(browse): Wave-0 Batch B — AbCip/TwinCAT/FOCAS opt into online discovery
Task 3/4/5: SupportsOnlineDiscovery => true on AbCipDriver, TwinCATDriver, FocasDriver
(their device enumeration is real, config-gated on EnableControllerBrowse / FixedTree.Enabled
which PatchForBrowse guarantees at open). One gate fact per driver test project.
2026-07-15 17:18:02 -04:00
Joseph Doherty e8db0a683f fix(drivers): flip runtime tag parsers to strict enum parsing (#457)
v2-ci / build (pull_request) Successful in 4m12s
v2-ci / unit-tests (pull_request) Failing after 10m15s
R2-11 Phase C. All six equipment-tag parsers (Modbus/S7/AbCip/AbLegacy/
TwinCAT/Focas) now read enum fields via the new TagConfigJson.TryReadEnumStrict:
absent -> fallback, valid -> parsed, present-but-invalid (typo) -> TryParse
returns false -> EquipmentTagRefResolver.TryResolve false -> driver surfaces
BadNodeIdUnknown, instead of the old lenient path that silently defaulted a
typo to a wrong-width Good.

Modbus flips all three enum fields (region/dataType/byteOrder); the other five
flip dataType. The deploy-time Deployment:TagConfigValidationMode=Error gate is
unchanged and remains the operator pre-flight.

Coverage:
- Six *EquipmentTagParserStrictnessTests inverted from Freeze_typo_* (lenient)
  to Typo_*_rejects_the_tag + Valid_*_still_parses.
- TagConfigJsonTests.TryReadEnumStrict_rejects_only_invalid matrix.
- Driver-level end-to-end proof:
  AbCipEquipmentTagTests.Driver_read_of_a_typod_dataType_ref_surfaces_BadNodeIdUnknown
  drives the real AbCipDriver.ReadAsync through resolve->status.

Golden parity corpus has no typo'd enums, so the flip is a no-op there.
Full solution builds clean; all six driver suites + core + parity + gate green.
2026-07-15 11:28:34 -04:00
Joseph Doherty a5a0f96d49 fix(drivers): harden operator TimeoutMs handling + AbLegacy evict parity
v2-ci / build (pull_request) Successful in 4m5s
v2-ci / unit-tests (pull_request) Failing after 10m45s
Follow-ups from the fleet-wide read-timeout audit that the S7 R2-01 read-leg
fix (PR #453) prompted. The audit confirmed S7 was the ONLY driver with the
async-read-ignores-socket-timeout hang; these are the two adjacent (non-hang)
findings it surfaced.

1. FOCAS TimeoutMs:0 footgun (the risky one): a non-positive Timeout made
   SynchronizedFocasClient DISABLE its per-call wall-clock ceiling, reverting to
   the caller's long-lived poll token — reintroducing exactly the frozen-peer
   wedge S7 just eliminated, under misconfig. Clamp non-positive TimeoutMs to the
   2s default at the config boundary so the deadline can never be authored away.

2. TimeoutMs validation symmetry: apply the same clamp in the AbCip + AbLegacy
   factories. libplctag's Tag.Timeout setter throws on <=0, faulting tag creation
   on every read/write; clamping keeps a misconfigured TimeoutMs:0 running on the
   default bound instead. (Shared PositiveTimeoutOrDefault helper per factory.)

3. AbLegacy reconnect parity with AbCip: AbLegacy evicted the cached libplctag
   runtime on neither the non-zero-status nor transport-exception read/write path
   (AbCip evicts on both), so a data-path fault recovered only via the probe loop
   / libplctag internals. Added EvictRuntime + wired it into both read and write
   failure paths so a fresh handle is created on the next call.

Tests: FOCAS 265->269 (clamp theory + positive), AbCip 336->339 (clamp theory +
positive), AbLegacy 209->212 (read-nonzero / read-exception / write-nonzero evict).
No production regressions; all three driver suites green.
2026-07-15 07:35:17 -04:00
Joseph Doherty 88e0977ae6 fix(s7): bound async wire ops with a wall-clock deadline (R2-01 read leg)
v2-ci / build (pull_request) Successful in 5m5s
v2-ci / unit-tests (pull_request) Failing after 13m50s
The R2-01 live gate (S7_1500ConnectTimeoutOutageTests, docker-pause blackhole)
surfaced a real gap the offline fakes couldn't: S7.Net's ReadTimeout/WriteTimeout
map to the TcpClient's ReceiveTimeout/SendTimeout, which govern only SYNCHRONOUS
socket calls — the async read/write paths S7.Net uses ignore them. On an
established-but-frozen peer (frozen PLC / firewall DROP / cable pulled mid-flow,
TCP session still open) a read blocked until the OS TCP stack gave up (minutes),
silently wedging the poll loop: no Bad tick, no reconnect. STAB-14 fixed only the
CONNECT leg (EnsureConnectedAsync CancelAfter); this is its READ-leg sibling.

- New S7OperationDeadline: bounds every data-plane wire op with a wall-clock
  ceiling (= _options.Timeout), surfacing an overrun as TimeoutException. Applied
  in S7PlcAdapter to Read/ReadBytes/Write/WriteBytes/ReadStatus. OpenAsync is left
  to EnsureConnectedAsync's own CancelAfter (not double-bounded).
- IsS7ConnectionFatal now classifies TimeoutException fatal → handle marked dead →
  next EnsureConnectedAsync reopens (connect-timeout fix takes over from there).
- Tests: 5 S7OperationDeadline unit tests (deadline / token-honouring / caller-
  cancel-passthrough / resultless), 1 driver-reaction test (read TimeoutException
  → reopen). Driver.S7.Tests 260/260.
- Live gate S7_1500ConnectTimeoutOutageTests now GREEN against the real snap7 sim
  (8s: baseline Good -> pause blackhole -> Bad tick -> unpause -> recovered Good).
2026-07-15 07:11:42 -04:00
Joseph Doherty d327243fd3 fix(s7-cli): restore OnDataChange CliFx-console rationale comment
v2-ci / build (pull_request) Failing after 15s
v2-ci / unit-tests (pull_request) Has been skipped
The 9cad9ed0 'strip tracking-ID comments' XML-doc sweep collaterally
removed the comment explaining why the S7 subscribe command routes
OnDataChange through the CliFx IConsole (not System.Console), leaving
SubscribeCommandConsoleHandlerCommentTests red. The Modbus copy still
carries the comment verbatim and the test guards S7-mirrors-Modbus
parity, so restore it (matching the Modbus wording) rather than weaken
the guard.

S7.Cli tests 49/49.
2026-07-14 14:49:10 -04:00
Joseph Doherty 9f62310b49 fix(historian): map Boolean historization to Int2, not degenerate Int1 (#441)
v2-ci / build (pull_request) Successful in 3m24s
v2-ci / unit-tests (pull_request) Failing after 8m19s
The historian's Int1 analog-tag creation path is server-degenerate:
EnsureTags(Int1) stores an unusable stub (live-probed on the deployed
2023 R2 historian; root-caused + documented gateway-side in
HistorianGateway #11 / PR #16). HistorianTypeMapper mapped
DriverDataType.Boolean -> HistorianDataType.Int1, so Boolean historized
tags failed auto-provisioning.

Map Boolean -> Int2 instead (store 0/1). Int2 is a proven-creatable
analog type, and the value-write path already sends every value as a
double on WriteLiveValues regardless of tag type, so Boolean 0.0/1.0
round-trips cleanly. Both Float and Int2 are supported, so a
pre-existing Float Boolean tag retypes cleanly on its next deploy.

- HistorianTypeMapper: Boolean => Int2 (+ remarks)
- Unit pins: HistorianTypeMapperTests, GatewayTagProvisionerTests
  (Boolean_definition_carries_explicit_Int2_presence)
- Live gate: EnsureTags_boolean_sandbox_provisions_as_Int2 (was _Int1);
  retype probe reframed Float->Int2 (drops the dead Int1 leg)
- Docs: docs/Historian.md 'Boolean historization maps to Int2', CLAUDE.md,
  archreview STATUS.md (R2-06 live gate -> 6/6)

Resolves #441.
2026-07-14 11:57:27 -04:00
Joseph Doherty 4da2d62b9a fix(historian): stamp a positive StorageRateMs on provisioned tags
v2-ci / build (pull_request) Successful in 4m7s
v2-ci / unit-tests (pull_request) Failing after 10m50s
GatewayTagProvisioner built HistorianTagDefinition without a StorageRateMs, so
the proto uint32 defaulted to 0. The gateway's EnsureTags maps StorageRateMs
straight to the AVEVA SDK's storageRateMs, which requires a positive value —
0 makes the SDK throw ArgumentOutOfRangeException ('Storage rate must be > 0 ms'),
so EVERY historized-tag auto-provision (the deploy-time IHistorianProvisioning
hook) silently failed at the gateway with failed=N.

Stamp DefaultStorageRateMs=1000 (matches the gateway's own live provisioner
convention; quantized 1000/5000/10000). Add a regression unit test asserting the
definition carries a positive rate, and set StorageRateMs on the live-suite's
inline definitions so the retype probe reaches a real EnsureTags.

Found via the R2-06 live gate against a real 0.2.0 gateway + AVEVA historian:
with this fixed, Float/Int2/UInt2/Int4/UInt4/Double all provision and the live
suite goes 3/6 -> 5/6. (Int1/Boolean remains blocked by the gateway histsdk's
ProtocolEvidenceMissingException for Int1 — a gateway-side type-support gap, not
this bug.)

Gateway driver unit 103/103.
2026-07-13 19:29:49 -04:00
Joseph Doherty 56ddf45e1c chore(core): flatten Historian sub-namespace to Core.Abstractions root
v2-ci / build (pull_request) Successful in 4m32s
v2-ci / unit-tests (pull_request) Failing after 10m2s
Four continuous-historization types (IHistorizationOutbox, HistorizationOutboxEntry,
IHistorianValueWriter/HistorizationValue, HistorizationCommitMode) drifted into a
ZB.MOM.WW.OtOpcUa.Core.Abstractions.Historian sub-namespace while the other six files
in the same folder correctly use the root namespace. This violated decision #59's
flat-public-surface rule and left InterfaceIndependenceTests.AllPublicTypes_LiveInRootNamespace
red on master (pre-existing, predates round-2 remediation).

Move all four to the root namespace and drop/retarget the now-redundant
'using ...Core.Abstractions.Historian;' in the ~11 consumers. No behavioral change.

Verified: Core.Abstractions.Tests 129/129 (was 128/129), Runtime.Tests Historian+DI 72/72,
Gateway driver unit 102/102, full solution build 0 errors.
2026-07-13 18:29:01 -04:00
Joseph Doherty 502a327f25 fix(twincat): probe recycle honors cancellation — no uncancellable gate wait (05/STAB-12 compounded part); B6 complete (TwinCAT suite 189 green) 2026-07-13 12:43:06 -04:00
Joseph Doherty 082ee99b6b fix(twincat): retract the fresh ADS notification when unsubscribe raced the replay (05/STAB-17) 2026-07-13 12:39:39 -04:00
Joseph Doherty de29759643 fix(twincat): retry failed replay intents on the next successful probe tick (05/STAB-16) 2026-07-13 12:37:20 -04:00
Joseph Doherty a06515ae91 fix(twincat): replay failure marks the registration dead, publishes Bad quality, degrades health (05/STAB-16) 2026-07-13 12:34:43 -04:00
Joseph Doherty e69d3b3b0f fix(modbus): equipment tags carry unitId; ResolveHost keys per-unit via the resolver (CONV-4) 2026-07-13 12:29:13 -04:00
Joseph Doherty 9262dd25b7 fix(drivers): ResolveHost resolves equipment-tag refs via EquipmentTagRefResolver — per-host breaker isolation (CONV-4) 2026-07-13 12:26:42 -04:00
Joseph Doherty dd01565d9a docs(s7): point R2-01 at the shipped ConnectionBackoff primitive for the S7 connect throttle (05/STAB-8 seam); B4 complete 2026-07-13 12:20:58 -04:00
Joseph Doherty 86742ad8cb fix(abcip): throttle evict-recreate Forward-Open attempts per device (05/STAB-8) 2026-07-13 12:20:10 -04:00
Joseph Doherty e54af9b948 fix(focas): per-device connect-attempt backoff across the three loops (05/STAB-8) 2026-07-13 12:16:31 -04:00
Joseph Doherty 962afed241 fix(twincat): per-device connect-attempt backoff, probe-driven recovery (05/STAB-8) 2026-07-13 12:13:23 -04:00
Joseph Doherty 85ef74ea86 refactor(s7): retire the bespoke poll fork onto PollGroupEngine v2 (CONV-1; fixes PERF-3 + STAB-6-S7; deletes the fork's bare-OCE poll-death — cross-ref plan R2-01) 2026-07-13 12:07:39 -04:00
Joseph Doherty b494d09d9b fix(drivers): wire PollGroupEngine onError->health + 30s poll backoff (05/STAB-9) [twincat,focas] 2026-07-13 11:59:49 -04:00
Joseph Doherty 9213c037b0 fix(drivers): wire PollGroupEngine onError->health + 30s poll backoff (05/STAB-9) [modbus,abcip,ablegacy] 2026-07-13 11:57:25 -04:00
Joseph Doherty 4231afb268 fix(focas): ConcurrentDictionary for fixed-tree caches (05/STAB-5) 2026-07-13 11:46:55 -04:00
Joseph Doherty 00dcb547e6 fix(abcip): serialize Read/GetStatus/Decode + Encode/Write/GetStatus per runtime (05/STAB-4) 2026-07-13 11:42:51 -04:00
Joseph Doherty e48609b000 feat(abcip): per-runtime operation lock on DeviceState (AbLegacy pattern) 2026-07-13 11:41:03 -04:00
Joseph Doherty 98f4860103 fix(modbus): classify per-op timeout + framing violations connection-fatal (05/STAB-3) 2026-07-13 11:37:13 -04:00
Joseph Doherty 1a698cbb97 Merge R2-11 TagConfig consolidation (arch-review round 2) [PR #434]
v2-ci / build (push) Successful in 3m55s
v2-ci / unit-tests (push) Failing after 8m14s
Findings 01/C-1, 01/P-1 (single TagConfigIntent.Parse in Commons, 4 copies
delegate, parse-once) + 05 CONV-2/UNDER-1/UNDER-6 (shared strict TagConfigJson
readers, per-driver Inspect() + writable key, FOCAS capability pre-flight,
Modbus probe timeoutMs, deploy-gate Warn|Error). Phase C runtime-strict flip
deferred. T22/T24 deferred. Auto-merged AddressSpaceApplier.cs + DriverHostActor.cs
with R2-04/R2-10; verified OpcUaServer.Tests 286/286 + Runtime.Tests 396/396.
Build clean.
2026-07-13 11:29:32 -04:00
Joseph Doherty b2a56ebf3b fix(driver-modbus): probe parses the factory DTO shape — timeoutMs no longer silently dropped (R2-11, 05/CONV-2) 2026-07-13 11:07:44 -04:00
Joseph Doherty 24435efa8b fix(driver-focas): equipment tags run the capability-matrix pre-flight authored tags get (R2-11, 05/UNDER-6) 2026-07-13 11:05:02 -04:00
Joseph Doherty e294416087 fix(driver-focas): equipment tags forced read-only until PMC writes exist; shared readers + Inspect (R2-11, 05/UNDER-1) 2026-07-13 11:01:55 -04:00
Joseph Doherty 4f5bbd253c feat(driver-twincat): shared TagConfigJson readers, writable key, Inspect warnings (R2-11) 2026-07-13 11:00:31 -04:00
Joseph Doherty 6e2415bb4c feat(driver-ablegacy): shared TagConfigJson readers, writable key, Inspect warnings (R2-11) 2026-07-13 10:59:27 -04:00
Joseph Doherty d374ba332b feat(driver-abcip): shared TagConfigJson readers + Inspect warnings (R2-11) 2026-07-13 10:58:17 -04:00
Joseph Doherty 1cceef406e feat(driver-s7): shared TagConfigJson readers, writable key, Inspect warnings (R2-11) 2026-07-13 10:57:01 -04:00
Joseph Doherty 64b9bba9e9 feat(driver-modbus): shared TagConfigJson readers, writable key, Inspect warnings (R2-11) 2026-07-13 10:55:41 -04:00
Joseph Doherty 084c6e3cee feat(r2-04): meter unconfirmed + advise-failed Galaxy writes 2026-07-13 10:53:17 -04:00
Joseph Doherty 610306734c fix(r2-04): Galaxy fail-closed write when AdviseSupervisory fails - Bad status feeds #5 node revert (06/S-1) 2026-07-13 10:51:16 -04:00
Joseph Doherty a537d29f44 refactor(r2-04): internal IMxWriteOps seam under GatewayGalaxyDataWriter (sealed SDK session untestable) 2026-07-13 10:48:30 -04:00
Joseph Doherty 46fedda3e4 Merge R2-02 ResilienceConfig hardening (arch-review round 2) [PR #431]
v2-ci / build (push) Successful in 3m53s
v2-ci / unit-tests (push) Failing after 8m15s
Findings 01/S-6 (High — clamp hostile ResilienceConfig, no ValidationException
driver-brick), 01/U-6 (delete dead bulkhead knob), 01/S-8=03/S12 (Write/Ack
non-idempotent + retry->0), 04/C-7 (JsonObject round-trip preserves unknown
keys), 01/S-7=03/S13 (per-Create options-generation kills respawn cache race).
T15/T18 deferred. STATUS.md conflict resolved additively. Build clean.
2026-07-13 10:29:43 -04:00
Joseph Doherty 07eec11d31 docs+test(r2-02): bulkhead doc sweep + parsed-knob-must-be-wired guard (01/U-6, OVERALL theme 1) 2026-07-13 10:15:32 -04:00
Joseph Doherty fea6477f45 Merge R2-01 S7 fault-path hardening (arch-review round 2) [PR #429]
v2-ci / build (push) Successful in 3m37s
v2-ci / unit-tests (push) Failing after 6m49s
Findings 05/STAB-14 (High regression — connect-timeout OCE no longer silently
kills S7 subscriptions), STAB-15 (framing/desync + mid-PDU cancel fatal
classification), STAB-8 seam note. All prod changes in S7Driver.cs; T11 live
outage gate deferred. STATUS.md conflict resolved additively. Build clean.
2026-07-13 10:14:59 -04:00
Joseph Doherty 4640ecb19b docs(r2-06): note WriteLiveValues Quality is a documented-dead input (0.2.0 C-002); keep 192 populated (06/C-7) 2026-07-13 10:04:29 -04:00
Joseph Doherty 92b2ac4891 docs(r2-01): mark EnsureConnectedAsync as the R2-09 connect-throttle seam (STAB-8 note, task 10) 2026-07-13 10:04:22 -04:00