Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions src/ARIEC60870.Core/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# ARIEC60870 Core — Protocol Parsing Contract

These instructions apply to `src/ARIEC60870.Core/**` and extend the repository root `AGENTS.md`.

## 1. Core authority

Core owns byte-level FT1.2/link/ASDU decoding, protocol primitives, mapping primitives, and offline analysis semantics. UI and Master may consume Core results; they must not duplicate parser truth.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit parser ownership to the protocols Core implements

This unqualified ownership statement conflicts with the repository's existing architecture: docs/ARCHITECTURE.md:61-75 assigns IEC-10x ASDU and IEC-104 APDU parsing to Master, where Protocol/Iec10x/Iec10xAsduDecoder.cs and Iec104ApduParser.cs already provide the canonical implementations. As written, future work on those parsers is simultaneously required to remain in Master and prohibited as duplicated parser truth; qualify this contract as IEC-103/Core-hosted parsing, or migrate the IEC-10x parsers and architecture documentation together.

Useful? React with 👍 / 👎.


Parsing is evidence-preserving protocol work. Malformed input must be observable and bounded, never converted into invented protocol meaning.

## 2. Root-cause-first parser workflow

For decoding defects:

1. reduce the failing frame to the smallest lawful synthetic fixture that reproduces the issue;
2. identify the exact protocol layer/field whose invariant is violated;
3. fix the parser/model at that layer;
4. add deterministic positive + negative regression coverage;
5. verify existing valid frames remain unchanged.

If three patches still chase parsing symptoms, STOP before patch four and re-audit framing boundaries, length rules, field offsets, type/COT interpretation, and parser ownership.

## 3. Try/Result parsing, not exception control flow

Untrusted frame bytes must not use exceptions for expected malformed-data control flow.

Prefer explicit parse outcomes such as `TryParse`, typed Result objects, or equivalent discriminated status carrying:

- success/failure;
- failure code/layer;
- safe offset/field context;
- parsed value when valid;
- raw evidence needed by higher layers.

Exceptions remain appropriate for genuine programming/invariant bugs, not normal invalid field values, short frames, unsupported ASDU types, checksum mismatch, or truncated input.

## 4. Defensive byte parsing

Before every indexed/length-dependent read validate the required bounds.

Explicitly handle where applicable:

- minimum frame length;
- declared vs available length;
- checksum/end byte;
- control/link fields;
- ASDU header availability;
- FUN/INF/data payload boundaries;
- time fields;
- unsupported/unknown Type or COT;
- numeric conversion and overflow;
- partial captures/noise before a frame.

Never trust a length byte enough to index memory without checking actual buffer bounds.

## 5. Preserve unknown evidence

Unknown/unsupported values must remain visible as unknown/raw evidence. Do not guess vendor semantics, signal names, or unsupported protocol meaning.

Friendly interpretation is allowed only when supported by protocol data or user mapping. Raw bytes and numeric protocol identity remain available to callers.

## 6. Bounded work

Parser complexity must be bounded by supplied data. Do not add unbounded search/retry loops for malformed streams.

For stream resynchronization, make progress explicit: every iteration must consume input, find a bounded candidate, or terminate with a structured result.

Avoid unnecessary per-frame allocations on high-frequency paths. Pool/reuse only when measurement justifies the complexity.

## 7. Numerical/time correctness

Validate protocol time/date fields before constructing host date/time values. Invalid relay timestamps must remain invalid/diagnostic evidence rather than silently becoming PC arrival time.

Numeric decoding must define byte order, signedness, scale/representation, and invalid/overflow behavior explicitly.

## 8. Diagnostics separation

Core should return compact structured parse evidence/errors. It should not synchronously format large UI/report strings on hot parsing paths.

Higher layers may turn structured results into operator diagnostics. Error storms from repeated malformed/noise input must be bounded/aggregated by the owning runtime layer.

## 9. Regression contract

Parser changes should cover applicable cases:

- valid fixed/variable frame;
- shortest valid frame;
- truncated frame at each meaningful boundary;
- declared-length mismatch;
- checksum/end failure;
- unknown Type/COT/FUN/INF;
- invalid timestamp;
- boundary numeric values;
- back-to-back frames/noise/resynchronization;
- round-trip/golden bytes where the project owns lawful fixtures.

Do not change expected bytes/interpretation in golden tests merely to make a new implementation pass without explaining why the old expectation was wrong.

## 10. Completion evidence

For substantial Core changes report root cause, protocol layer, structured failure behavior, regression fixtures/tests, performance/allocation impact where relevant, and any unsupported ambiguity that remains.

## Final rule

One malformed or unknown frame must never crash the analyzer, hang the stream parser, erase raw evidence, or force higher layers to invent meaning.
130 changes: 130 additions & 0 deletions src/ARIEC60870.Master/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# ARIEC60870 Master Runtime — Scoped Engineering Contract

These instructions apply to `src/ARIEC60870.Master/**` and extend the repository root `AGENTS.md`. The root file remains authoritative for product identity, IEC-103 polling policy, UI/output policy, clean-room rules, release behavior, and simulator boundaries.

## 1. Runtime authority

`ARIEC60870.Master` owns active-master session state, transport orchestration, polling policy, FCB/FCV behavior, Class 1 drain decisions, startup sequence, timeout/backoff, and live protocol evidence.

Desktop/CLI code may command and observe the master; it must not become a second master state machine.

## 2. Root-cause-first rule

For session, polling, serial, GI, ACD, DFC, timeout, Stop, or reconnect defects:

`reproduce → capture evidence → trace state transition → identify owner → fix → regression test → validate cleanup`

Do not add timers, delays, extra polling, duplicate flags, or UI-side recovery until the actual state transition and transport behavior are understood.

If three patches in the same runtime subsystem still chase symptoms, STOP before patch four and re-audit the state machine, transport ownership, cancellation path, and protocol evidence.

## 3. Explicit state machines

Connection/session progress must be represented explicitly rather than inferred from scattered booleans.

Expected states should cover the actual lifecycle, for example:

`Idle → Opening → Starting → Running → Stopping → Completed/Faulted → Idle`

Protocol subflows such as GI and Class 1 drain should also have explicit bounded progression.

Unexpected input must produce a defined state/result, not an undefined transition or infinite retry.

## 4. Result-oriented failure architecture

Expected runtime outcomes should use typed Result/status values rather than exception-driven normal control flow.

Examples:

- timeout/no data;
- DFC busy;
- invalid or rejected secondary response;
- transport closed during Stop;
- cancellation;
- exhausted bounded retry/drain;
- unsupported operation;
- malformed frame reported by Core;
- serial device unavailable.

Infrastructure exceptions from serial/.NET I/O should be caught at the transport/session boundary and converted into a structured runtime result plus diagnostic evidence where recovery is possible.

Do not silently swallow exceptions. Do not use throw/retry loops as a protocol state machine.

## 5. Stop/cancellation is a first-class path

Stop is not an exceptional afterthought.

- one owner controls the active session cancellation token;
- transport close may be used to release blocked serial operations;
- close/dispose races during cancellation are expected boundary conditions and must be contained;
- finalization must run exactly once;
- session state must converge to a stable terminal/idle state;
- no stale callback may revive a stopped session.

Never rely on arbitrary sleeps to make Stop reliable.

## 6. Polling and backpressure

Preserve the root polling contract:

- Class 2 is normal/background polling;
- Class 1 drain is triggered by protocol evidence such as ACD or bounded GI follow-up;
- NO DATA/ACD clear ends unnecessary Class 1 drain;
- DFC causes bounded backoff;
- timeout retry/reset policy remains controlled.

Do not create an unbounded request queue. If command production can outrun the transport, use explicit bounded scheduling/backpressure.

## 7. Diagnostic pipeline

Detailed operator diagnostics are important, but the polling/receive path must remain lightweight.

Hot runtime paths should emit compact structured evidence/events into bounded storage or queues. Expensive formatting, report text, and UI-ready message composition should happen outside the critical receive/polling path where practical.

- bound queues/retained evidence;
- aggregate or rate-limit repeated normal timeout/no-data conditions;
- preserve counters for full-session totals;
- never allow diagnostic persistence/UI consumers to block master progress;
- diagnostic failure must not corrupt protocol state.

## 8. Transactional configuration/session promotion

New transport/session/polling configuration should be validated before it replaces active/last-known-good state.

`parse → validate → prepare → activate`

If opening or preparation fails, do not leave a half-active session whose UI state claims Running.

## 9. Resource ownership

Every session has deterministic ownership for:

- serial/network transport;
- timers;
- cancellation sources;
- receive loops;
- polling tasks;
- event subscriptions;
- retained evidence buffers.

Repeated Start/Stop and reconnect cycles must not accumulate tasks, timers, handlers, or open handles.

## 10. Performance and regression evidence

For changes to polling, receive loops, evidence retention, or cancellation, measure/inspect relevant before/after behavior when practical:

- poll cadence/jitter;
- command/response latency;
- CPU during sustained monitoring;
- memory/evidence growth;
- Stop latency;
- retained queue depth;
- repeated Start/Stop resource count.

A repeatable regression of roughly >10% in a relevant metric requires investigation and explicit justification.

Every bug fix should add the highest-value deterministic Master test practical, especially for timeout, DFC, ACD/Class 1 drain, cancellation, transport close, and finalization.

## Final rule

The master runtime must remain deterministic even when the relay is silent, slow, busy, malformed, disconnected, or the operator presses Stop at the worst possible moment.
Loading