Skip to content

P0 Phase-C3/C4: wire-first IEC101 RX, original HEX always visible, Unknown ASDU fail-closed - #95

Draft
masarray wants to merge 18 commits into
masterfrom
phase-c3-evidence-integrity-20261106
Draft

masarray wants to merge 18 commits into
masterfrom
phase-c3-evidence-integrity-20261106

Conversation

@masarray

@masarray masarray commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

Phase C3-A — source-owned IEC-101 process integrity (tested candidate)

Base: master at 0967a590ed1f07f103195c0a4a7233adfb934e4d (Phase-C2 merged). Exact tested HEAD: bc9dacc092a92d9957f7700658c6be6d7402f25e. Still Draft and unmerged: this is a substantial IEC-101 C3-A engineering slice, not a completed IEC-101/103/104 Phase-C3 or hardware FAT.

Structural issues and owning corrections

  1. The single passive writer previously recorded accepted/indexed events and WPF frame-preview losses, but not separate FT1.2 frame, ASDU, decoded-object, excluded-object and accepted-object accounting. Added writer-owned typed PassiveProcessDecodeCounters, exposed in live progress and capture manifest, with final invariant checks: observed frames = nonvariable + invalid + primary + secondary; secondary = decoded + rejected ASDUs; decoded objects = accepted + excluded; accepted writer objects = indexed + unindexed. No GUI queue can redefine source totals.
  2. Invalid/blocked/substituted/not-topical quality or TEST/negative observations could overwrite the remembered GI/cyclic digital baseline, causing a later good value to be misclassified as a change. The canonical classifier now updates credible digital state only for eligible quality/COT flags. Spontaneous bad-quality observations remain observable rather than being inferred as reliable physical state.
  3. Temporary indexed fact reads did not independently check accidental raw/index record corruption. Added CRC32 across the existing reserved 32-byte fact-header field plus original frame payload using the canonical journal CRC32; reads fail closed on mismatch. Not cryptographic authentication or a permanent capture.
  4. Passive UI calls counted facts SOE even when event policy included eligible process/measurement observations. Label is now OBS with decoded invalid FT1.2 / rejected ASDU diagnostics.

C3-A did not change protocol decoders, IEC-101 Master TX/poll/GI, IEC-104 APCI, release/stable, passive RX-only contract or visual design. Subsequent C3-B commits in this same draft PR extend active IEC-104 receive admission, shared Desktop trust projection, and IEC-101 dual-link source-frame identity. C3-A engineering scope: docs/P0C3_PROCESS_INTEGRITY_20261106.md.

Exact-head automated verification

  • Windows Release CI #42130022493: build, protocol smoke 19/19, six xUnit suites 591/591 (Core 63, Master 184, Monitor 106, Reporting 5, Desktop 218, Repository 15), automated OBS-C WPF acceptance and Windows GUI/field-tool artifact packaging — passed.
  • CodeQL #42130022531 — passed.
  • Dependency review #42130022684 — passed.
  • Repository hygiene #42130022568 — passed.
  • New 5,000 object integration case: real four-RX-tap capture-writer → FT1.2 framing → canonical ASDU → temporary event ledger, across independent A/B circuits, asserts 5,000 accepted/indexed, zero unindexed and preview rollover; live-only mode creates no permanent capture.
  • New 50,000 decoded object classifier test (12,500 mixed COT ASDUs): 30,000 accepted and 20,000 excluded as designed, with 13 invalid-variable-frame injections and 25 primary TX injections, exact counter balance and source/object uniqueness.
  • Existing 2,560 object durable-recording/preview rollover test stays green; corrupted temporary fact test and quality-safe baseline test added.
  • Independently checked reference-machine CPU, memory, allocations/object, event-to-GUI p50/p95 and Dispatcher p95; CI passing stress tests does not establish requested ≤150ms/<16ms targets.
  • Manual inspection of WPF artifact screenshots, real Windows physical DPI.
  • Whole-app 50k mixed IEC-101/103/104 through Master/Monitor/Replay/Reports parity, IEC-103 native event ledger and IEC-104 cancellation/reconnect/epoch stress.
  • Electrical isolated 2/4-COM 1200bps real-device FAT + SCADA side receipt of approximately 2,000 source SOEs. No upstream RTU/UART FIFO delivery guarantee.

CI artifacts (same Windows run)

  • ARIEC60870-Passive-Monitor-P0-GUI-win-x64, artifact ID 13463066615
  • ARIEC60870-IEC101-RX-Field-Tools-win-x64, ID 13463156437
  • ARIEC60870-OBS-C-ui-evidence, ID 13463321209
  • ARIEC60870-test-results, ID 13462368268

Next acceptance gate: IEC-103 and IEC-104 end-to-end canonical facts/provenance, stale-epoch rejection/reconnect stress, full 50k mixed pipeline measurements and WPF visible/retained version parity; physical 1200bps SOE FAT remains separate. Preserve Draft while these critical gates are not proven.

Phase C3-B integrated increment — IEC-104 trusted admission and IEC-101 dual-link original-frame parity

Exact-head verified: 86a0e422328c6e84c15f2268bc54db4a886d61d2 on phase-c3-evidence-integrity-20261106 relative to unchanged master 0967a590ed1f07f103195c0a4a7233adfb934e4d. Still Draft / unmerged. This is a substantial C3-integrated increment, not completed end-to-end IEC-101/103/104 Phase-C3 and not physical 1200-bps SOE FAT.

Production root causes and corrections

  1. IEC-104 acceptance error: ReceiveOneAsync warned about out-of-window peer N(R), but still promoted the same I-frame as valid if N(S) matched. It could advance receive N(S), user-data/GI counters and publish valid-looking process objects. Invalid S-format acknowledgements were counted as good. An unaccepted COT10 could prematurely stop the receive burst. Fixed in the existing IEC-104 Master session owner: N(R) window is part of the single I acceptance verdict; rejected I/S are accounted, not promoted; burst termination uses only accepted I-frame evidence. Raw rejected frames/findings remain, and in-sequence retransmission is accepted.
  2. Desktop trust boundary: WPF IsIec10xProcessValue independently fell back to Type/IOA even for engine-rejected RX Warning observations. Blocked typed untrusted IEC-101/104 RX at the existing projection entry, before Value/Event or GI/command projection. Trace retains rejected wire evidence; no second APCI decoder or protocol state machine was introduced in UI.
  3. IEC-101 dual-link physical provenance: sibling SQ0/SQ1 process objects got different event sequence numbers and lost the original physical frame identity when Iec101DualLinkRedundancySession enriched evidence. Channel now receives the real session-assigned parent frame sequence from the existing publish callback and passes PhysicalFrameSequenceNumber to siblings. Navigation matches exact original frame and Link A/B, not a fabricated shared IOA or merged source.

Deterministic regression

  • New simulated IEC-104 association: STARTDT con, invalid N(R) on S + two-object SQ0 spontaneous I, then valid N(S)=0 retransmission, plus mandatory TESTFR con. Verifies 2 rejected frames, only one accepted I carrying exactly 2 process objects, no rejected object promotion, exact same original physical frame root for both accepted objects, valid ACK and supervision accounting. No unplanned active TX/GI/reset.
  • Extended existing IEC-101 dual-link GI sequence-ASDU integration: IOA 100 and 101 must have different event sequences but identical physical-frame root and independent Link A identity.
  • Previously verified writer-owned 5,000-object monitor pipeline and 50,000-object classifier stress remain in the full regression suite.

Exact-head evidence on 86a0e422328c6e84c15f2268bc54db4a886d61d2

Gate Evidence / result
Windows Release build, warnings-as-errors PASS, 0 build warnings, 0 errors
IEC-101/103/104 protocol smoke 19/19 PASS
xUnit regression 592/592 PASS, 0 failed, 0 skipped: Core 63, Master 185, Monitor 106, Reporting 5, Desktop 218, Repository 15
OBS-C Windows real-WPF viewport and 1,400-frame acceptance PASS in CI; screenshot artifact generated (manual visual review remains separate)
GUI packaging, independent IEC-101 2/4-RX toolkit PASS, artifact ZIPs and package checksum files produced
Windows CI #42134716545 success
CodeQL #42134716317 success
Dependency review #42134716313 success
Repository hygiene #42134716349 success
Formatting clean NOT PROVEN: repo-wide advisory output emits 657 whitespace diagnostics despite the workflow step reporting success. No unrelated mass-format churn attempted.
Native whole-production IEC-101/103/104 50k mixed pipeline, CPU/memory/GC/latency p50/p95 NOT VERIFIED; the 50k regression covers the IEC-101 classifier, not every downstream layer
Physical 1200 bps SICAM CP8050 / SCADA delivery of ~2,000 SOE NOT TESTED in hardware; no lossless delivery claim

Previously covered C3-A workloads remain: 2,560 durable capture/preview observations, 5,000 accepted/indexed multi-source IEC-101 monitor objects with zero unindexed, and 50,000 IEC-101 decoded classifier objects (30,000 accepted, 20,000 excluded). This is not a new three-protocol 50,000 end-to-end performance measurement.

Exact-head CI artifacts: GUI ARIEC60870-Passive-Monitor-P0-GUI-win-x64 (13464799269), Windows WPF ARIEC60870-OBS-C-ui-evidence (13464654728), ARIEC60870-test-results (13464539646), 2/4-RX toolkit ARIEC60870-IEC101-RX-Field-Tools-win-x64 (13464499875).

Engineering analysis: docs/P0C3B_IEC104_TRUSTED_RECEIVE_20261106.md. Phase C4 readiness gates: cross-protocol IEC-103/104 typed event source-fact parity from ingest through Replay/Reports, reconnect/Stop/epoch isolation, 2k/5k/50k mixed full-pipeline reproducible CPU/alloc/latency/queue/retention measurements, and an independent electrical 1200-bps FAT instrumenting sender, RTU, serial RX and SCADA. Remain Draft until critical gates are proved. Never equate green CI with physical IED/RTU interoperability.


Phase C3-C — terminal Event/Values integrity (2026-11-06)

Production fix, exact PR head: 4bb79bafc00dee93b6ab33ff05406a2dabf082c0. master remains 0967a590ed1f07f103195c0a4a7233adfb934e4d; PR remains Draft. No protocol TX/poll/APCI semantics altered.

Verified root cause in Desktop finalization: ApplyFinalResult used to Clear() the live Event ring and Values dictionary then replace them with Iec103MasterRunResult.EventLog/ValuePoints. Those session result summaries are narrower than the source-qualified per-object Event/Values projections, and they lack some original-frame links. A complete active journal could therefore collapse to a handful of rows on session termination, independently of filters/auto-follow. The finalizer also flushed at most one bounded batch before reporting completion, allowing pending accepted observations to be stranded.

Owning correction: existing WPF projection is retained through completion; result remains available for report/assessment via _lastResult but does not overwrite the live event/value authority. Finalization drains pending evidence and findings with bounded FlushUiQueues() dispatch batches and Dispatcher.Yield(Background) until empty, before unlocking the next connection; no extra parser, archive writer, polling/GI/reset or timer. EVENT count at completion remains the projected actual observation count rather than the narrower engine edge-summary count.

Deterministic Windows WPF regression: synthetic terminal EventLog contains only 1 summary entry and ValuePoints contains 1 placeholder while live Event and Value stores contain multiple original facts. Enqueue 280 additional test evidence records (> one 42-item normal flush), invoke real terminal finalization, and assert: zero queue residue, unchanged retained Event objects, unchanged WPF Event/Value rows and source references, zero WPF Reset notifications, exact bound Event grid count, truthful EVENT counter.

Exact-head checks: CI #42137403583 SUCCESS: Windows Release build 0 warnings/0 errors, 19/19 protocol smoke, 592/592 xUnit (63 Core, 185 Master, 106 Monitor, 5 Reporting, 218 Desktop, 15 Repository), actual Windows WPF 1400-frame viewport and new terminal regression PASS, Windows GUI/independent IEC101 RX toolkit packaging PASS. Hygiene #42137403542 SUCCESS and Dependency review #42137403604 SUCCESS. CodeQL #42137403705 SUCCESS on the exact same head.

Artifacts: Windows GUI ZIP, independent 2/4-RX toolkit, WPF screenshots/metrics, test results.

Acceptance boundary: fixes a reproducible terminal result-vs-live authority conflict; DOES NOT prove that the separately reported in-flight Event full/one/full flicker has no other root cause, full unbounded Event history, mixed 2k/5k/50k IEC101/103/104 end-to-end parity, archive/replay/report equivalence, measured CPU/GC/p50-p95/Stop metrics, exceptional Stop stale-epoch convergence, or physical 1200bps SICAM/SCADA ~2000 SOE FAT. Source Event preview remains intentionally bounded to 420 rows, not a claim of complete history without recording. Remain Draft until these gates are met.


C3-D/E — active session epoch isolation and native IEC-103 original-frame parity (2026-11-06)

Exact integrated HEAD: 4d5cd82702b67a0e9d6c5a0e395e74a597984139, base master 0967a590ed1f07f103195c0a4a7233adfb934e4d. PR is still Draft/unmerged; no active IEC-101/103/104 TX, polling/GI/reset or passive RX-only contract was changed.

C3-D: independently proven lifecycle isolation. Active single-link IEC-101, IEC-103, IEC-104 and IEC-101 dual-link subscriptions now bind immutable session epoch to callbacks; source-stamped evidence/finding queue entries are validated at enqueue AND dequeue. Delayed dual-link WPF snapshots re-check epoch before presentation; all session handlers detach in finally on Stop, fault, or success. Existing passive RX-only callback remains available and stamps its current capture epoch. Source-scope resets explicitly invalidate older epochs. A real Windows WPF acceptance injects old callbacks, already-queued stale observations/findings, then a current-epoch IEC-104 process object; asserts old evidence/finding rejection without loss of the new Value/Event. Rejection counters are reported separately from low-value UI compression. See docs/OUTPUT_AND_PERFORMANCE_POLICY.md.

C3-E: IEC-103 source fact parity and verified decoder protection. The real IEC-103 Master now assigns native ValuePoints and EventLog the exact canonical physical RX frame reference already owned by the Master evidence event. In a real receive-path fixture, Type 1 COT1 spontaneous DPI and relay CP32Time2a retain identical origin references, event sequence and byte-for-byte frame HEX. An FT1.2-valid but truncated IEC-103 Type 1 previously returned DecodeStatus.Ok and could seed a false value; Core now requires FUN/INF + DPI (1) + CP32Time2a (4) + SIN (1). The full timestamp is decoded from its proper four bytes; SIN remains separate. Six zero-through-five-byte truncations plus positive full-payload test, and valid/invalid Master transport integration, prove fail-closed process promotion without erasing raw frame evidence.

Exact-head automated proof: Windows CI #42145368494 SUCCESS with Release 0 warnings, 0 errors, 19/19 protocol smoke and 601/601 xUnit (Core 70, Master 187, Monitor 106, Reporting 5, Desktop 218, Repository 15; 0 failed/skipped); Windows real-WPF viewport/1400+ synthetic frames and new epoch acceptance PASS; integrated GUI ZIP and independent IEC-101 2/4-RX toolkit packaging PASS. Dependency review #42145368646 SUCCESS; hygiene #42145368507 SUCCESS. CodeQL #42145368825 SUCCESS on the exact integrated HEAD.

User-testable exact-head artifacts:

Unverified critical acceptance remains: complete native IEC-103 and IEC-104 indexed durable event/replay/report equivalence, active IEC-104 real TCP reconnect/cancellation under timing pressure, 2k/5k/50k whole production mixed IEC-101/103/104 CPU/alloc/memory/latency p50/p95 measurements, manual multi-DPI screenshot review, and isolated physical 1200-bps 2/4-COM SICAM-CP8050/SCADA receipt of ~2,000 sender SOEs with RTU FIFO accounting. Never infer hardware lossless delivery from source-level or CI tests. Keep PR Draft until these are separately established.


P0 field report — Passive IEC-101 COM4/COM3: empty Raw Frames (2026-11-06)

Grounded field observation: the user photographed a live Windows application with the subtitle Passive IEC-101 Link A · COM4/COM3, Stop Monitor available and an entirely empty Raw Frames viewport. The screenshot does not expose COM byte counters or prove whether any UART byte reached COM4/COM3, nor does it prove which build SHA was executing. Do not conflate the pre-read "Passive monitor started" status with physical traffic or claim a wiring defect from this image.

Code-path audit: MainWindow.PassiveMonitor.RunPassiveMonitorAsync previously set Monitoring/logged "started" before the two physical COM taps had delivered data. The writer owns physical byte counters and FT1.2 frames, but those numbers were shown on a separate advanced passive panel, not the empty primary Raw Frames workspace. PassiveFt12Scanner correctly waits for complete framing; without explicit UART/decode/GUI provenance a blank grid could falsely look like a broken decoder.

Patch, exact head: 15c66d9957ba5f2aa8bc0c1a426d7701d3f468f7 (supersedes initial a416fa71 with an isolated WPF fixture). The raw receive writer now separately counts checksum-valid FT1.2 frames, untrusted/noise/partial candidates and pending incomplete frame bytes, besides original per-COM byte totals and preview published/consumed counts. PassiveNoFrameDiagnosis pure policy distinguishes (1) opening COM, (2) zero UART bytes (RX tap, wiring/ground or silent line), (3) physical bytes present but no accepted FT1.2 (baud/framing/partial/bad checksum), and (4) decoded frames awaiting/hidden by UI. Only the original raw-completed COM receive stream drives counts; no fabricated process events, no extra polling, no TX. The original Raw Frames ListBox displays a compact, unobtrusive empty-state explanation with selected COM identities and counts. A silent, opened capture after five seconds emits one session-scoped warning in Diagnostics, never repetitive polling or a claim that the remote RTU is down. Existing live 160ms UI timer owns refresh; no new timer.

Hard regression: PassiveMonitorP0Tests asserts honest no-data and separate undecoded/GUI-lag states. Real Windows --observation-ui-acceptance checks the actual visible WPF zero-state after clearing earlier synthetic smoke history (the first CI try on a416fa71 exposed harness fixture leakage; it was corrected without weakening the assertion). Exact-head Windows CI #42197612476: SUCCESS, Release 0 warnings/0 errors, 19/19 protocol smoke, 603/603 xUnit (70 Core, 187 Master, 108 Monitor, 5 Reporting, 218 Desktop, 15 Repository), real-WPF 1400-frame + empty-screen acceptance PASS, GUI and field-tools ZIP packaging PASS. Dependency review #42197612509 and repo hygiene #42197612477 SUCCESS. CodeQL #42197612465: SUCCESS.

Field-test artifacts:

Physical acceptance not asserted. To close the field issue, run this exact build with electrically isolated RX-only taps on existing SCADA TX and RTU TX (and signal ground), correct COM identity and configured baud/parity/stop (e.g. 1200 bps if this is the field setup); observe COM4/COM3 byte counts and FT1.2 validity separately. If both count zero while an independent capture shows traffic, investigate adapters/driver/tap. If positive bytes but zero valid frames, use byte sample/HEX and UART framing/width evidence. If valid frames but zero WPF rows, capture the new queue counters and logs; that is an application bug. Zero physical frames cannot be fixed by a GUI decoder or green CI. PR remains Draft until real RX field verification and outstanding C3 whole-path acceptance.


P0 COM4/COM3 regression: raw UART preview must bypass FT1.2 framing (2026-11-06, latest)

Field constraint, not a speculative hardware fault: operator reports the same two COM4/COM3 receive-only adapters and physical installation previously produced frames, and both adapter RX LEDs blink in the failing newer build. DO NOT tell the operator to rewire/recheck baud as the default diagnosis. Screenshot alone cannot establish the COM driver read count or exact build. Treat regression in acquisition-to-WPF evidence path as P0.

Code-level failure proven: the primary Raw Frames page was populated only when the FT1.2 scanner emitted a candidate; actual received PassiveChunkPreview UART samples were exclusively shown on a separate Setup panel. In addition, an incomplete 0x68... candidate can be buffered until a subsequent serial chunk or Stop; with bytes already read, the primary viewport could be entirely blank. This behavior remains possible even if the physical COM receiver is active. The earlier passive scanner was replaced with the shared Core framer in merged PR #93; don't claim that refactor itself broke real field RX without raw field data.

Actual corrective implementation on exact head 3b1687949dc25037a7358a2f0cec8be573105f8b within this same PR:

  • Primary Raw Frames now displays a bounded, selectable, real received UART HEX sample grid directly from original PassiveRawRows when no FT1.2 frame appears. These are explicitly raw COM chunks, not synthetic protocol frames or trusted process SOEs. This bypasses scanner/decode success altogether without duplicating the read or issuing TX.
  • New Start Monitor forcibly clears stale hidden Trace search and selects All frames.
  • The sole receive writer now uses one WaitToReadAsync read owner and one writer-owned 200-ms idle timer to publish incomplete scanner tails as original-byte Partial events after configured inter-frame silence while the monitor is still running, not only on Stop. Never promotes checksum-unverified partials to process values or silently discards wire bytes. No new serial reads, polls, RTS/DTR or GI.
  • A new deterministic integration fixture runs two active 1200-bps RX sources with byte-by-byte reads through the actual monitor session reader, channel, writer, canonical FT1.2 framer and live presentation feed, asserts byte-for-byte fixed frames from both COM streams before Stop. Negative fixture verifies raw samples and partial evidence before Stop. Actual Windows WPF acceptance asserts a received 68 0C physical raw sample appears on Raw Frames with zero completed FT1.2 candidates.

Validated exact head: Windows CI run #42201227894 SUCCESS: Release 0 errors, 0 warnings; protocol smoke 19/19; xUnit 605/605 (Core 70, Master 187, Monitor 110, Reporting 5, Desktop 218, Repository 15) with no failed/skipped; WPF 1400-frame + actual no-frame raw-HEX acceptance SUCCESS; GUI/IEC101 RX toolkit packaging SUCCESS. CodeQL #42201227740, dependency review #42201227774, hygiene #42201227842 all SUCCESS.

Field-test artifacts from EXACT head, not earlier broken package:

Open hardware gate: CI has no COM4/COM3 devices and cannot confirm real field 1200-bps reception or end-to-end SICAM SOE integrity. Have operator test new exact-HEAD ZIP, observe both per-COM real raw byte HEX/FT1.2 candidates and compare with earlier working version using same physical ports; if raw read counters remain zero despite blinking LEDs, inspect serial reader-driver behavior/software config and direct CLI at the same ports without reassigning a wiring fault. Keep PR Draft and master unchanged pending this evidence.


P0-C4 — Wire-first COM receive and fail-closed Unknown ASDU operator contract (2026-11-06)

Source root cause and operator principle: the user's exact same COM4/COM3 setup previously displayed IEC-101 frames, and both serial RX LEDs now show physical activity. No automatic inference that the plant wiring is broken is acceptable. The newly audited source in the same PR already provides original COM chunk HEX in Raw Frames before framing, publisher-owned idle partials, and reset to All frames on Start. This milestone closes the next layer: the previous GUI could promote a supported Type ID with incomplete information-element payload to Value/Event and could describe a malformed/noise candidate using incorrect fallback message text. The canonical ASDU decoder legitimately reports an object with Issues for a short element; a checksum-valid FT1.2 envelope is not proof of a complete process value.

Clean-room line-monitor architecture, no vendor source copied: src/ARIEC60870.Desktop/ViewModels/PassiveWirePresentation.cs is one stateless classification gate shared by background PassiveFramePreparation and the existing non-batch WPF path. It distinguishes Decoded, Link only, Unknown/invalid ASDU (checksum-valid FT1.2), Invalid frame, Partial, Noise and Link Width Mismatch. Every original raw physical observation continues to enter the same existing bounded Trace with original HEX, stream/offset, timestamp and an operator-readable reason. No false fallback A2 messages for other unrecognized material. Only confirmed supported, complete objects with Issues.Count == 0, exact VSQ object count and independently verified wire structure can project live process values. The independent writer-owned event ledger already rejects invalid process objects and remains authoritative. No new UART read, poll, GI, baud retune, RTS/DTR, parallel parser, WPF per-frame Dispatcher or unbounded GUI data store.

Deterministic tests: checksum-valid unknown/private Type, checksum-valid but truncated known Type 1, noise, partial, malformed and prohibited A2 all retain byte-for-byte original HEX but never advertise trusted process facts; the existing known good multi-object Type 1 and 19-object SICAM Type 31 remain decoded. Windows executable --observation-ui-acceptance checks unknown Type/Noise/Partial reach actual Raw Frames together and leave Values/EventLog empty. Historical noise and Link Width Mismatch operator text are preserved, with original WPF smoke assertions still enforced.

Agent handoff: append new mandatory wire-first release gate to AGENTS.md; detailed implementation/backpressure/security/field-gate acceptance specification in docs/P0_WIRE_FIRST_MONITOR_ARCHITECTURE_20261106.md and policy docs/OUTPUT_AND_PERFORMANCE_POLICY.md. This makes cross-thread direction durable and forbids a decoder dependency for raw visibility. Public docs deliberately vendor neutral.

EXACT integrated HEAD 8655b4fa7b286fc5e93cd44aae14e7b415d8ddfe validated by Windows CI #42205188671 SUCCESS: Windows Release 0 errors/0 warnings; 19/19 protocol smoke; 608/608 xUnit (Core 70, Master 187, Monitor 110, Reporting 5, Desktop 221, Repository 15) no failed/skipped; real-WPF 1400-frame + unknown/noise/partial acceptance PASS; GUI and 2/4-RX portable ZIP packaging PASS. CodeQL #42205189133, dependency #42205189710, hygiene #42205188946 all SUCCESS.

Exact-head artifacts: Windows GUI ZIP, independent IEC-101 2/4-RX tool ZIP, real-WPF screenshot/metrics, test TRX.

Not yet physically proven: direct real COM4/COM3 receive with this exact ZIP, 2k SOE 1200-bps field receipt/FIFO attribution, and full native 50k mixed-system fatigue/latency benchmark. CI proves the controlled wire-first software receive and WPF contract, not lossless delivery from the physical RTU. Keep PR Draft/unmerged and stable/main unchanged until field evidence.

…ines and indexed-fact integrity

Expose per-layer receive/decode/acceptance counters and terminal accounting invariants. Reject untrustworthy GI/cyclic state baselines, verify temporary indexed evidence with canonical CRC32, and add 5k dual-circuit full-writer plus 50k mixed-object deterministic stress and corruption tests.
Comment on lines +201 to +202
var dir = Path.Combine(Path.GetTempPath(),
"ariec-c3-crc-" + Guid.NewGuid().ToString("N"));
journal.Append(new PassiveObservedFrame(1, 0, DateTime.UtcNow,
"Variable", true, MakeFrame(0)), 1);
Assert.Single(journal.ReadAfter(0, 1)); // flush existing writer buffers
using (var disk = new FileStream(Path.Combine(dir, "process-events.bin"),
Comment on lines +288 to +289
var output = Path.Combine(Path.GetTempPath(),
"ariec-c3-dual-" + Guid.NewGuid().ToString("N"));
@masarray masarray changed the title Phase-C3A: verified passive process accounting, quality-safe state and indexed-fact integrity Phase-C3A/B: source-owned IEC-101 facts, IEC-104 RX trust, dual-link frame parity Nov 6, 2026
…I backlog

Prevent end-of-session engine summary replacement of source-qualified live observations; drain all accepted UI evidence in bounded yielding batches before terminal state, with real-WPF regression for 280 queued items and summary-vs-journal mismatch.
… epoch

Stamp bounded WPF ingress with source epoch, discard queued and callback stale data after a new Connect/Capture, detach active Master subscriptions on cancellation/fault, and reject delayed dual-link snapshots. Exercise negative and current-epoch paths in real Windows WPF acceptance; expose explicit source rejection telemetry.
…ture epoch

Restore PassiveMonitor's existing OnFindingRaised entrypoint through the same stamped UI finding queue; active Master delegates remain connection-owned.
Share canonical Master physical-frame reference across final IEC103 ValuePoints, EventLog and RX evidence. Fail closed for incomplete Type1 ASDU despite valid FT1.2 checksum; test real Master receive via deterministic secondary frames with positive and truncated negatives.
Require FUN/INF, DPI, four-byte CP32Time2a and SIN before promoting Type1; never decode partial CP32 from a DPI byte. Add six truncation negatives and exact time/SIN positive; retain original bytes for forensic inspection.
@masarray masarray changed the title Phase-C3A/B: source-owned IEC-101 facts, IEC-104 RX trust, dual-link frame parity Phase-C3 A–E: source integrity, trusted IEC104/103 receive, session-epoch isolation Nov 6, 2026
…Frames

Distinguish zero physical UART bytes from received but incomplete/invalid FT1.2 traffic, accepted writer frames waiting on GUI, and COM startup. Add measured source counters and one-shot no-UART diagnostic, compact zero-state within existing WPF viewport, deterministic health-state regressions and actual WPF acceptance. Zero protocol TX changes.
…prior smoke fixtures

App startup runs smoke/history probes on the same MainWindow. Begin the new-capture zero-byte check with ClearSessionView, matching actual passive Start semantics, before asserting visible no-UART diagnosis.

masarray commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner Author

Reconciliation — 2026-11-06 (latest audited head 8655b4fa7b286fc5e93cd44aae14e7b415d8ddfe)

Authoritative P0 branch remains Draft PR #95, based on master 0967a590ed1f07f103195c0a4a7233adfb934e4d. All four exact-head checks have completed successfully: Windows CI #42205188671, CodeQL #42205189133, dependency review #42205189710, and hygiene #42205188946. Windows Release build, protocol smoke, real WPF smoke, packaging and 608/608 xUnit tests pass (Core 70, Master 187, Monitor 110, Reporting 5, Desktop 221, Repository 15).

PR #96 is superseded/closed unmerged. No useful unique receive change is outstanding from it. Its missing quiet-UART ExpireIncomplete() behavior is now integrated directly into #95's single writer: 200-ms idle check, original RX bytes retained as Partial/untrusted, visible before Stop, with a dedicated deterministic 1200-bps two-COM test. Its duplicate empty-viewport UX should not be reintroduced. The #95 wire-first implementation additionally exposes real UART receive chunk HEX even before FT1.2 decoding and keeps unknown/invalid observations in Trace without manufacturing decoded Values/Events.

Concurrent PR #97 (commit a5dac7f2b16773cd310bd24509d4ce8210b0ed35) is documentation-only for a future Virtual Online Sniffer, using two VSPE Pair devices and an explicit inline forwarding mode. It has zero changed-file overlap with #95. Its runtime must never introduce Write/RTS/DTR to the strict read-only Passive Monitor. PR #97 can be reviewed independently; reconcile its future implementation against the then-current canonical code after #95.

One unresolved P0 release gate: there is no exact-head physical COM4/COM3/1200-bps proof yet. A green CI does not establish actual UART RX from the user's adapter, correct source direction, 2,000 SOE delivery or RTU FIFO accounting. Keep #95 Draft and do not change stable/master on that assumption. Bench the exact-head GUI ZIP artifact #13495703506 and independent RX toolkit #13495593828, recording per-COM byte counters, raw HEX, checksum-valid/unknown/partial totals, per-source Events, COM errors and source-verified journal. Distinguish 0 UART bytes vs bytes but no valid FT1.2 vs wire frames not projected in WPF.

Avoid duplicate PRs/CI churn. Future receive bugs belong to #95 until merged, VOS implementation belongs to a separate milestone after its docs are accepted.

@masarray masarray changed the title Phase-C3 A–E: source integrity, trusted IEC104/103 receive, session-epoch isolation P0 Phase-C3 A–E + passive COM receive visibility (field zero-frame diagnosis) Nov 6, 2026
Show bounded real UART HEX in the Raw Frames viewport when no decoded frame exists, reset stale Trace filters each Start, expire stalled FT1.2 partials live on writer-owned idle tick, retain source offsets without inventing protocol Values. Exercise actual dual-COM 1200-bps receive-to-writer-to-live-feed with byte-split fixtures plus real WPF raw fallback acceptance.
@masarray masarray changed the title P0 Phase-C3 A–E + passive COM receive visibility (field zero-frame diagnosis) P0 Phase-C3 A–E + restore live raw COM evidence independent of FT1.2 framing Nov 6, 2026
…id ASDU trust boundary

Keep all original COM RX frame bytes visible independently of IEC101 decode, classify unknown/partial/noise deterministically for operators, reject incomplete or unsupported ASDU objects from Values/SOE, preserve one raw physical frame row. Add tested positive/negative parser fixtures, real-WPF unknown/noise/partial acceptance and future-agent wire-first architecture contract. No passive TX, baud, or GUI thread ownership changes.
decode?.CauseOfTransmission == 3 && decode.TypeId is >= 1 and <= 44,
IsRelayValue = direction == FrameDirection.SlaveToMaster && first is not null && decode is not null &&
decode.TypeId is 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 or 10 or 11 or 12 or 13 or 14 or 15 or 16 or 30 or 31 or 32 or 34 or 35 or 36 or 37,
trustedDecode?.CauseOfTransmission == 3 && trustedDecode.TypeId is >= 1 and <= 44,
IsRelayValue = direction == FrameDirection.SlaveToMaster && first is not null && decode is not null &&
decode.TypeId is 1 or 2 or 3 or 4 or 5 or 6 or 7 or 8 or 9 or 10 or 11 or 12 or 13 or 14 or 15 or 16 or 30 or 31 or 32 or 34 or 35 or 36 or 37,
trustedDecode?.CauseOfTransmission == 3 && trustedDecode.TypeId is >= 1 and <= 44,
IsRelayValue = direction == FrameDirection.SlaveToMaster && first is not null && trustedDecode is not null &&
…s in wire-first classifier

Retain existing WPF smoke and parser reason terminology while explicitly distinguishing Unknown ASDU and untrusted physical bytes. Do not weaken integration assertions.
…r vocabulary

Keep exact raw hex and no-false-Values/Events hard checks; report independent unknown/noise/partial/trust states on failure for fast root cause.
@masarray masarray changed the title P0 Phase-C3 A–E + restore live raw COM evidence independent of FT1.2 framing P0 Phase-C3/C4: wire-first IEC101 RX, original HEX always visible, Unknown ASDU fail-closed Nov 6, 2026

masarray commented Nov 6, 2026

Copy link
Copy Markdown
Owner Author

Independent source audit — 2026-11-06, exact PR head 8655b4fa7b286fc5e93cd44aae14e7b415d8ddfe

Scope: physical RX-only COM path, writer, FT1.2 and Raw Frames evidence; no code change or merge during this audit. CI/CodeQL/dependency/hygiene all PASS, 608 xUnit + 19 protocol smoke + WPF pass, but original COM4/COM3 failure has no physical reproduction verdict yet.

P0 — current “No UART bytes received” diagnosis can be a false conclusion

In PassiveMonitorSession.ReadLoopAsync, tap.Read(...) successfully returns n > 0 and byteOffset += n at the receive worker, but there is no separate reader-stage ingress counter. GetProgress().ScadaBytes/RtuBytes uses _writtenBytes[], which is incremented only near the end of WriteLoopAsync, after the writer has performed FT1.2 scanning, source history/journal writes, typed classification, and GUI preview publishing. UpdatePassiveTraceEmptyState uses those late counters to show the exact label “No UART bytes received” and raises IEC101-PASSIVE-NO-UART after 5 s.

A blocked, delayed or failing writer can therefore display 0 UART bytes despite successful SerialPort.Read, misdirecting a field engineer toward wiring/baud and obscuring the actual pipeline failure. This is a proven counter ownership/semantic bug, not proof this was the exact field root cause.

Required correction: count ingress bytes/chunks on each reader immediately after successful Read, separately track queued-to-writer and writer-committed bytes, preserve the existing committed/archive counters, expose per-port stage and mismatch/backlog/fault state in the same compact Raw Frames view. Do not conflate Read with archived/verified bytes. Add a deterministic slow/failing writer fixture that shows reader>0, writer=0, and explicitly refuses the “No UART” diagnosis.

P1 — raw-preview loss is not independently accounted

_previews is Channel<PassiveChunkPreview> capacity 256 with DropOldest; TryReadPreview does not increment a raw-chunk consumed count. Existing PreviewFramesDropped is computed from the separate 512-frame presentation channel, not this raw-chunk channel. The new TracePassiveRawChunkGrid may therefore skip samples on UI pressure without a per-source raw-chunk skip counter. Label it an explicitly bounded preview and calculate ingress/preview/consumed deltas (not permanent raw loss); full verified journal still remains opt-in.

P1 — live idle-expired partial path needs source/replay parity test

WriteLoopAsync idle-timer branch now emits a Partial into session history and frame preview (good, tested), but bypasses correlator.Observe, giTracker.Observe and timelines.ObserveFrame, while chunk and EOF paths have their own downstream processing. Whether partial observations affect any derived timelines/correlation must be checked; consolidate or test source/replay parity for partial+timeout+next-valid-frame. Also mark timeout timestamp as observation finalization time, not receipt timestamp of the original partial bytes.

Release gate (unchanged)

  • Reproduce on exact-head two-COM COM4/COM3 1200-bps test, with a separate proven sender/peer and physical signal receipt before interpreting decoder or GUI. Capture per-input read, queued, writer, FT1.2 valid/invalid/partial, GUI counts, raw HEX, UART error counters, and optional verified journal.
  • Confirm WPF trace after transition from invalid/noise/partial to valid; sustained 2k SOE / 2-4 RX physical FAT and measured load still unverified.
  • Keep PR Draft and master/stable untouched until demonstrated; do not open another duplicate PR or repeat expensive CI for audit-only work.

masarray commented Nov 6, 2026

Copy link
Copy Markdown
Owner Author

P0 Frame Trace reliability milestone — ASE2000 behavioral benchmark (2026-11-06)

Exact tested head: a1ee2a7058324d232668e48c62e3c315c389ddeb (existing Draft PR #95). Source: commit.

Reference: Public ASE2000 V2 material describes raw+interpreted line monitoring, a compact message list, Unknown messages, communication status/error counters and linked source-value inspection. We adopt the operational evidence principles, not proprietary source code, artwork or literal design. See https://www.ase-systems.com/products/ase2000-v2/ and https://www.ase-systems.com/ase2000-v1-faq/.

Source-stage corrections

  • PassiveMonitorSession counts bytes immediately after actual successful driver Read for each physical RX input, separately from writer-queue admission and writer completion. An idle/open COM handle alone has no receive credit.
  • PassiveNoFrameDiagnosis now distinguishes 0 driver RX from positive read but lagging writer. Never report “No UART bytes received” when reader counters are >0, including the one-time five-second diagnostic.
  • The main Raw Frames toolbar displays compact per-COM actual RX and FT1.2 verified counts continuously (not just empty state); hover details show RX/queue/writer counters and untrusted candidates. It resets between sessions and does not add per-frame Dispatcher calls.
  • The 256-sample GUI DropOldest raw preview now exposes published/consumed/skipped independently of its 512-frame preview and source-verified raw journal. This is GUI sample omission, not proof of serial byte loss.

Exact-head acceptance

  • Windows CI #42224035522 SUCCESS: Release build + format; protocol smoke; 610/610 xUnit (Core 70, Master 187, Monitor 112, Reporting 5, Desktop 221, Repository 15); native real-WPF acceptance + packaging.
  • CodeQL #42224035640, dependency #42224035672, hygiene #42224035524 SUCCESS.
  • New negative regression: simulated RX 60 bytes / committed 0 → “writer pipeline pending,” not “No UART bytes”; existing two-lane replay checks RX/queued/writer accounting; 300 raw chunks through 256-slot preview prove 44 skipped samples while all 38,400 writer bytes are processed. WPF smoke checks the receive-versus-writer status.
  • New exact-head Windows GUI ZIP, 2/4-RX field tools, WPF screenshot/metrics, xUnit TRX.
  • Real-WPF screenshot/metrics inspected: 1,435 synthetic frames, 1,200 bounded visible, zero steady-state collection Reset, two decoded point rows, selected frame inspector visible. CI requested 1366x768 and 1540x960 but Windows runner clamped actual window width to ~1044 DIP; do not claim physical 1366/DPI certification.

Remain open — P0 field/behavioral parity

  • Real COM4/COM3 byte receipt and 2k SOE/FIFO physical acceptance not yet proven, even if the USB adapter LEDs blink and software CI is green.
  • ASE2000-style byte-span selection-to-decoded-field highlighting is not yet implemented; the current exact RawHex + selected frame inspector is a functional foundation. This should follow verified receive accounting, without duplicating the parser.
  • Physical 2/4-COM bench, no silent GUI preview gaps, authoritative/full archive verification, and full 50k mixed-case CPU/memory/latency acceptance still require independent evidence.
  • Keep PR P0 Phase-C3/C4: wire-first IEC101 RX, original HEX always visible, Unknown ASDU fail-closed #95 Draft; master/stable unchanged. No new competing PR and no extra CI rerun for notes.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants