Skip to content

feat(iso-on-tcp): add RFC 1006 TPKT header parser (STORY-184) - #466

Merged
Zious11 merged 14 commits into
developfrom
feature/STORY-184-tpkt-header-parser
Sep 7, 2026
Merged

feat(iso-on-tcp): add RFC 1006 TPKT header parser (STORY-184)#466
Zious11 merged 14 commits into
developfrom
feature/STORY-184-tpkt-header-parser

Conversation

@Zious11

@Zious11 Zious11 commented Sep 7, 2026

Copy link
Copy Markdown
Owner

[STORY-184] S7comm TPKT Core Parser: parse_tpkt_header Pure-Core Free Function + VP-048 Kani Skeleton

Epic: E-23 — feature-s7comm
Mode: feature (F4 delta-implementation)
Convergence: CONVERGED after 3 adversarial passes (per-story adversarial 3/3 clean; BC-5.39.001 satisfied)

Tests
Coverage
Kani
Holdout

Adds parse_tpkt_header, the pure-core RFC 1006 TPKT header parser that anchors the new
S7comm ISO-on-TCP framing layer (src/analyzer/iso_on_tcp.rs, SS-20). This is the first
implementation story of Epic E-23 and lands ADR-014 (S7comm ISO-on-TCP stream dispatch and
parser design, proposed) alongside it, discharging the F4-OBLIGATION-ADR014-CLAUDEMD
carry-forward. The parser rejects under-length input, non-0x03 version bytes, and
declared lengths below the RFC 1006 §6 minimum of 7, and accepts TpktHeader { version: 3, length } for length in [7, 65535]. A #[cfg(kani)] VP-048 skeleton is included; full
proof execution is deferred to STORY-194 per ADR-014 Decision 9's scope note.


Architecture Changes

graph TD
    dispatcher["dispatcher.rs<br/>(protocol dispatch)"] -.->|no dependency yet| iso_on_tcp
    iso_on_tcp["iso_on_tcp.rs (NEW)<br/>parse_tpkt_header, TpktHeader"] -.->|consumed later by| s7comm["s7comm.rs<br/>(STORY-186, not yet created)"]
    style iso_on_tcp fill:#90EE90
Loading
Architecture Decision Record

ADR-014: S7comm ISO-on-TCP (TPKT/COTP) Stream Dispatch and Parser Design

Context: Epic E-23 adds S7comm (Siemens, TCP/102) protocol support. S7comm PDUs are
carried inside ISO-on-TCP framing (RFC 1006 TPKT + ISO 8073/COTP), which is itself shared
by other ICS/SCADA protocols on TCP/102 (IEC 61850 MMS, ICCP/TASE.2). A stream-dispatch and
module-boundary design was needed before any parser code could be written.

Decision: iso_on_tcp.rs (SS-20) is frozen as a standalone, protocol-agnostic framing
module exporting pure free functions only (no impl StreamAnalyzer, no per-flow state of
its own); s7comm.rs (SS-21, STORY-186) is the sole consumer. TpktHeader { version: u8, length: u16 } is the frozen struct — exactly these two fields. Parsers are implemented
directly from RFC 1006/ISO 8073 (no borrowed/GPL code — Decision 4). Port-102 protocol
identification uses a per-entry Support enum (Supported, KnownUnsupported,
DetectionOnly) on KnownProtocol (Decision 3, human-ratified 2026-09-06).

Rationale: Separating the framing layer (SS-20) from the PDU dissector (SS-21) keeps
parse_tpkt_header and the future parse_cotp_header independently Kani-provable
pure-core functions, mirroring the IEC-104 parse_apci_header/VP-044 precedent
(STORY-167) that this story's shape closely follows.

Alternatives Considered:

  1. Single combined s7comm.rs module doing TPKT+COTP+S7 in one pass — rejected: couples
    framing (shared by other TCP/102 protocols) to S7-specific PDU semantics, and produces
    a much larger, harder-to-verify Kani surface.
  2. Name-keyed protocol-exclusion list for port-102 identification — rejected in favor of
    the Support enum (Decision 3) for finer-grained detection-vs-support signaling.

Consequences:

  • New framing code is reusable by future TCP/102 protocols without modification.
  • parse_tpkt_header and parse_cotp_header (STORY-185) each get independent, narrowly
    scoped VP-048/VP-049 Kani obligations instead of one large combined proof.
  • ADR-014 status remains proposed pending the full SS-20/SS-21 build-out across
    STORY-184–194; this PR ships only the SS-20 TPKT slice.

Story Dependencies

graph LR
    STORY184[STORY-184<br/>🟡 this PR] --> STORY185[STORY-185<br/>⬜ not started<br/>parse_cotp_header/VP-049]
    style STORY184 fill:#FFD700
Loading

depends_on: [] — STORY-184 has no upstream story dependencies (first story in Epic
E-23). blocks: [STORY-185].


Spec Traceability

flowchart LR
    BC1[BC-2.20.001<br/>len < 4 to None] --> AC1[AC-184-001]
    BC2[BC-2.20.002<br/>version != 0x03 to None] --> AC2[AC-184-002]
    BC3[BC-2.20.003<br/>length < 7 to None] --> AC3[AC-184-003]
    BC4[BC-2.20.004<br/>valid input to Some] --> AC4[AC-184-004]
    BC4 --> AC5[AC-184-005<br/>4-way partition]
    AC1 --> T1[test_BC_2_20_001_*]
    AC2 --> T2[test_BC_2_20_002_*]
    AC3 --> T3[test_BC_2_20_003_*]
    AC4 --> T4[test_BC_2_20_004_*]
    AC5 --> T5[four_way_partition_is_exhaustive]
    T1 --> S1[src/analyzer/iso_on_tcp.rs]
    T2 --> S1
    T3 --> S1
    T4 --> S1
    T5 --> S1
    S1 --> VP[VP-048 Kani skeleton<br/>AC-184-006]
Loading

Test Evidence

Coverage Summary

Metric Value Threshold Status
Unit tests 30/30 pass 100% PASS
AC coverage 6/6 ACs (30 tests + 1 source-level check) 100% PASS
Kani skeleton Compiles (#[cfg(kani)], full proof deferred to STORY-194) N/A PASS
Holdout satisfaction N/A — evaluated at wave gate >= 0.85 N/A

Test Flow

graph LR
    Unit["30 Unit Tests"]
    Proptest["2 Proptests"]
    Holdout["4 RFC-1006-§6 holdouts"]
    Kani["VP-048 Kani skeleton"]

    Unit -->|100% AC coverage| Pass1["PASS"]
    Proptest -->|independent oracle| Pass2["PASS"]
    Holdout -->|canonical RFC vectors| Pass3["PASS"]
    Kani -->|compiles clean| Pass4["PASS"]

    style Pass1 fill:#90EE90
    style Pass2 fill:#90EE90
    style Pass3 fill:#90EE90
    style Pass4 fill:#90EE90
Loading
Metric Value
New tests 30 added (tests/iso_on_tcp_tests.rs, new file), 0 modified
Total suite 30 tests PASS in 0.02s (cargo test --test iso_on_tcp_tests)
Regressions 0 — new module, no existing files behaviorally changed
Lint/fmt cargo fmt --check clean; cargo clippy --all-targets -- -D warnings clean
Detailed Test Results — Per-AC Mapping (row-verified against evidence-report.md)

Coverage Map (from docs/demo-evidence/STORY-184/evidence-report.md)

AC BC Test Count Verdict
AC-184-001 (len < 4 → None) BC-2.20.001 5 PASS
AC-184-002 (version != 0x03 → None) BC-2.20.002 5 PASS
AC-184-003 (length < 7 → None) BC-2.20.003 9 PASS
AC-184-004 (valid input → Some) BC-2.20.004 9 PASS
AC-184-005 (4-way exhaustive partition) BC-2.20.004 inv. 3 2 PASS
AC-184-006 (VP-048 skeleton compiles) VP-048 0 (source-level: grep + cargo check/clippy) PASS

Cross-check: 5 + 5 + 9 + 9 + 2 = 30, matching cargo test --test iso_on_tcp_tests
output exactly (30 passed; 0 failed).

Representative Test Rows (row-verified, PG-W74-PRDESC-ROW-VERIFY)

Test Result
test_BC_2_20_001_returns_none_for_three_bytes_canonical_vector ok
test_BC_2_20_003_returns_none_for_length_six_boundary_below_rfc_minimum ok
test_BC_2_20_004_valid_input_returns_some_header_length_65535_max_canonical_vector ok
proptests::test_BC_2_20_004_proptest_matches_independent_oracle ok

Full raw cargo test --test iso_on_tcp_tests output re-verified by pr-manager during this
PR's creation (30 passed; 0 failed; 0 ignored). cargo fmt --check and
cargo clippy --all-targets -- -D warnings both re-verified clean.


Holdout Evaluation

N/A — evaluated at wave gate (feature-s7comm Epic E-23 has not yet reached its wave
gate). Four RFC-1006-§6 holdout unit tests (test_rfc1006_s6_*) are included in this
story's own test file as an independent-oracle sanity check, but formal holdout-scenario
evaluation against the >=0.85 satisfaction threshold happens at the wave/epic boundary,
not per-story.


Adversarial Review

Convergence: CONVERGED — 3/3 clean per-story adversarial passes (BC-5.39.001
satisfied). Findings were resolved across the branch's development history prior to PR
creation (commits 903a947b, 953e1f14, dead410e, c253f9ea, a23fb6ba,
3209e70c — doc-tense/provenance fixes, RFC-1006 §6 correction (human ruling: minimum
length 7, not the initially-drafted value), holdout-divergence documentation, and
CHANGELOG churn cleanup).

Key resolutions prior to this PR

RFC 1006 §6 minimum-length human ruling

  • Category: spec-fidelity
  • Problem: initial draft used an incorrect RFC 1006 §6 minimum-length floor.
  • Resolution: corrected to 7 (4-byte TPKT header + 3-byte minimum COTP) per explicit
    human ruling; BC-2.20.003/BC-2.20.004 boundaries and all EC-005/006/007 edge cases
    updated to match; tests and doc-comments made RFC-conformant.
  • Test added/updated: test_BC_2_20_003_returns_none_for_length_six_boundary_below_rfc_minimum,
    test_BC_2_20_004_valid_input_returns_some_header_length_7_canonical_vector

Doc-tense / provenance sweep

  • Category: test-quality / documentation
  • Problem: RED-phase-tense doc comments and test provenance language left over from
    TDD scaffolding.
  • Resolution: past-tense provenance corrected across src/analyzer/iso_on_tcp.rs and
    tests/iso_on_tcp_tests.rs doc comments (adversary F-184-P1-002/004, P3
    DF-SIBLING-SWEEP).

Security Review

graph LR
    Critical["Critical: 0"]
    High["High: 0"]
    Medium["Medium: 0"]
    Low["Low: 0"]

    style Critical fill:#90EE90
    style High fill:#90EE90
    style Medium fill:#90EE90
    style Low fill:#90EE90
Loading
Security Scan Details

Dedicated security-reviewer pass completed (Step 4 of this PR's lifecycle) against
PR #466's diff (src/analyzer/iso_on_tcp.rs::parse_tpkt_header +
tests/iso_on_tcp_tests.rs). Result: NO FINDINGS at any severity
(CRITICAL/HIGH/MEDIUM/LOW).

Manual/AI Security Review

  • Bounds safety (CWE-125 / CWE-787): if data.len() < 4 { return None } precedes
    every indexed access; data[0]/data[2]/data[3] are provably in-bounds after the
    guard. NOT VULNERABLE.
  • Integer overflow/panic (CWE-190): u16::from_be_bytes over exactly 2 bytes is a
    total, non-panicking function; no arithmetic, casts, or shifts elsewhere in the
    function; no unwrap/expect/unsafe/panic! anywhere in the module. NOT
    VULNERABLE.
  • Unbounded allocation / resource consumption (CWE-789 / CWE-400): the untrusted
    length field is returned as data only — never used to size an allocation in this
    story. NOT VULNERABLE at this story's scope. Forward note (non-blocking): the
    declared-length-vs-actual-buffer reassembly check must land in the STORY-185/186
    COTP/S7comm consumer that actually allocates/advances buffers using this field —
    tracked as that story's obligation, not a gap in this PR.
  • Injection, auth, OWASP Top 10: NOT APPLICABLE — pure byte-field decode; no
    strings, queries, deserialization, authentication, or I/O surface.
  • INFO (non-blocking): the VP-048 Kani harness is scoped to no-panic/bounds-safety
    only, with full proof execution deferred to STORY-194 per the module's documented
    scope note — an honest, already-documented deferral, not an undisclosed gap.

Verdict: nothing blocks merge on security grounds.

Dependency Audit

  • No Cargo.toml changes in this PR — no new dependency surface.

Formal Verification

Property Method Status
No panic for any symbolic [u8; N] input (N <= 300) Kani (verify_parse_tpkt_header_safety) Skeleton compiles; full proof execution deferred to STORY-194
Four-way partition exhaustive/non-overlapping (AC-184-005) Unit test (four_way_partition_is_exhaustive) + proptest oracle VERIFIED at unit level; formal Kani assertion is STORY-194

Risk Assessment & Deployment

Blast Radius

  • Systems affected: New module src/analyzer/iso_on_tcp.rs only; src/analyzer/mod.rs
    gains one pub mod iso_on_tcp; line. No existing analyzer, dispatcher, or CLI behavior
    is touched.
  • User impact if failure occurs: None in production — the module is not yet wired into
    dispatcher.rs or any StreamAnalyzer (that wiring is STORY-186's scope). Dead code
    from the CLI's perspective until then.
  • Data impact: None — pure function, no state, no I/O.
  • Risk Level: LOW

Performance Impact

Not applicable — new, unwired pure-core module; no existing hot path is touched.

Rollback Instructions

Immediate rollback (< 5 min):

git revert <MERGE_COMMIT_SHA>
git push origin develop

Verification after rollback:

  • cargo build succeeds (module removal doesn't break mod.rs — revert removes both
    the file and the pub mod iso_on_tcp; line atomically).
  • cargo test --all-targets green.

Feature Flags

None — no runtime surface exists yet for this module.


Demo Evidence

Pure-core library story — no CLI/web surface exists yet for this module (the S7comm
dispatch wiring that would expose it via the CLI is STORY-186's scope, per ADR-014
Decision 1). Per the demo-recording skill's library/test-harness mode, evidence is
captured as annotated cargo test output transcripts grouped by AC, plus inline
canonical-vector tables and source-level grep/cargo check/cargo clippy verification
for the VP-048 Kani skeleton — mirroring the STORY-167 (IEC-104 parse_apci_header)
precedent.

Location: docs/demo-evidence/STORY-184/ (committed on this branch)

File AC Coverage
AC-001-short-input-rejection.md AC-184-001 (BC-2.20.001)
AC-002-bad-version-byte.md AC-184-002 (BC-2.20.002)
AC-003-length-floor-rejection.md AC-184-003 (BC-2.20.003)
AC-004-valid-accept-path.md AC-184-004 (BC-2.20.004)
AC-005-four-way-partition.md AC-184-005 (BC-2.20.004 invariant 3)
AC-006-vp048-kani-skeleton.md AC-184-006 (VP-048)
evidence-report.md Index — full 30/30 test run, per-AC coverage map, PG-W70-DEMO-SCRUB path-scrub gate result (PASSED, zero absolute-host-path matches)

All 6 ACs have at least one recording. Gate satisfied.


Traceability

Requirement Story AC Test Verification Status
BC-2.20.001 AC-184-001 test_BC_2_20_001_returns_none_for_three_bytes_canonical_vector (+4 more) unit PASS
BC-2.20.002 AC-184-002 test_BC_2_20_002_returns_none_for_version_0x04_off_by_one_canonical_vector (+4 more) unit PASS
BC-2.20.003 AC-184-003 test_BC_2_20_003_returns_none_for_length_three_off_by_one_canonical_vector (+8 more) unit PASS
BC-2.20.004 AC-184-004 test_BC_2_20_004_valid_input_returns_some_header_length_7_canonical_vector (+8 more) unit + proptest PASS
BC-2.20.004 inv. 3 AC-184-005 test_BC_2_20_004_four_way_partition_is_exhaustive unit + proptest oracle PASS
BC-2.20.001-004 (no-panic) AC-184-006 verify_parse_tpkt_header_safety Kani (skeleton; full run STORY-194) SKELETON PASS
Full VSDD Contract Chain
BC-2.20.001 -> AC-184-001 -> test_BC_2_20_001_* (5 tests) -> src/analyzer/iso_on_tcp.rs:111-113 -> ADV-PASS-3-CLEAN -> KANI-SKELETON
BC-2.20.002 -> AC-184-002 -> test_BC_2_20_002_* (5 tests) -> src/analyzer/iso_on_tcp.rs:114-116 -> ADV-PASS-3-CLEAN -> KANI-SKELETON
BC-2.20.003 -> AC-184-003 -> test_BC_2_20_003_* (9 tests) -> src/analyzer/iso_on_tcp.rs:117-126 -> ADV-PASS-3-CLEAN (RFC-1006-§6 human ruling) -> KANI-SKELETON
BC-2.20.004 -> AC-184-004/005 -> test_BC_2_20_004_* (11 tests incl. proptests) -> src/analyzer/iso_on_tcp.rs:127-130 -> ADV-PASS-3-CLEAN -> KANI-SKELETON
VP-048 -> AC-184-006 -> verify_parse_tpkt_header_safety -> src/analyzer/iso_on_tcp.rs:145-162 -> full proof deferred -> STORY-194

AI Pipeline Metadata

Pipeline Details
ai-generated: true
pipeline-mode: feature
factory-version: "1.0.0"
pipeline-stages:
  spec-crystallization: completed
  story-decomposition: completed
  tdd-implementation: completed
  holdout-evaluation: deferred-to-wave-gate
  adversarial-review: completed
  formal-verification: skeleton-only (full proof deferred to STORY-194)
  convergence: achieved
convergence-metrics:
  adversarial-passes: 3
  adversarial-status: CONVERGED (3/3 clean)
generated-at: "2026-09-06T00:00:00Z"

Pre-Merge Checklist

  • All CI status checks passing (test, clippy, fmt, semantic PR, action-pin-gate,
    changelog-gate, green-doc-tense, help-provenance)
  • Coverage delta is positive (new module, 30 new tests, 0 regressions)
  • No critical/high security findings unresolved
  • Rollback procedure documented above
  • Feature flag configured — N/A, no runtime surface yet
  • Demo evidence committed (docs/demo-evidence/STORY-184/, 6/6 ACs covered)
  • CHANGELOG [Unreleased] entry present (touches src/)

https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW

…proposed) with first S7comm implementation story

Discharges the F4-OBLIGATION-ADR014-CLAUDEMD carry-forward from Phase-2
architect work (STORY-184, wave 87, feature-s7comm). ADR-014 was held
uncommitted on develop pending the first implementation story so the
ADR review artifact lands as part of the story's own review, per this
repo's ADR-with-first-implementation convention.

- Add docs/adr/0014-s7comm-iso-on-tcp-stream-dispatch-and-parser-design.md
  (status: proposed -- moves to accepted once implementation completes).
  Byte-identical to the canonical committed copy in the factory-artifacts
  mirror (architecture/decisions/ADR-014-....md).
- Update CLAUDE.md docs/adr/ index note: add 0014 entry (port-102 model =
  Support enum, ratified; Decision 3 supersedes the original name-keyed-
  exclusion-list recommendation, human-ratified 2026-09-06).

No src/ or test changes in this commit -- stub-architect/test-writer/
implementer pick up TPKT header parsing (BC-2.20.001..004, VP-048) next.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
Files created: src/analyzer/iso_on_tcp.rs
Files modified: src/analyzer/mod.rs (add pub mod iso_on_tcp;)
todo!() functions: 1 (parse_tpkt_header)

Stubs the SS-20 ISO-on-TCP TPKT (RFC 1006) header parser per ADR-014
Decision 1 (frozen interface) and Decision 9 (pure-core free-fn design):
- `TpktHeader { pub version: u8, pub length: u16 }` — frozen struct, no
  `reserved` field surfaced.
- `pub fn parse_tpkt_header(data: &[u8]) -> Option<TpktHeader>` — body is
  `todo!()`; self-check (BC-5.38.005 invariant 1) applied: "if I include
  this real implementation, will the test for this function pass
  trivially without any implementer work?" — yes (the function has
  branching over 3+ distinct reject/accept paths per BC-2.20.001-004), so
  it stays `todo!()`.
- `#[cfg(kani)] mod kani_proofs { verify_parse_tpkt_header_safety }` —
  VP-048 harness skeleton, copied verbatim from STORY-184's spec; compiles
  under `cargo +nightly kani` (not exercised by plain `cargo check`/
  `cargo test` since it is `#[cfg(kani)]`-gated). Full proof run deferred
  to STORY-194 per the story's VP-048 obligation.

Scope: STORY-184 covers parse_tpkt_header only. COTP types
(CotpHeader/CotpTpduType/parse_cotp_header) are STORY-185's VP-049
obligation per ADR-014 Decision 9's explicit scope note and are
deliberately NOT stubbed here.

No tests authored (test-writer's stage, not stub-architect's — Red Gate
temporal ordering). `tests/iso_on_tcp_tests.rs` from the story's file list
is left for test-writer.

## GREEN-BY-DESIGN
none

## WIRING-EXEMPT
none

cargo check: PASS (crate compiles with the stub).
cargo clippy --all-targets -- -D warnings: PASS, zero warnings (the
`let _ = data;` line ahead of `todo!()` avoids an unused-parameter lint
without adding any real logic).

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
Adds tests/iso_on_tcp_tests.rs covering parse_tpkt_header per BC-2.20.001
(len < 4), BC-2.20.002 (version != 0x03), BC-2.20.003 (decoded length < 4),
and BC-2.20.004 (accept path, reserved-byte independence, length=65535
ceiling, trailing-bytes tolerance). Includes an AC-184-005 four-way
partition spot check and a proptest oracle cross-check. All 24 tests
compile and fail against the todo!() stub in src/analyzer/iso_on_tcp.rs
(Red Gate verified per BC-5.38.001).

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
Also refreshes the stale "skeleton only / todo!()" doc-comment above the
VP-048 Kani proof now that parse_tpkt_header is todo!()-free.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
… (adversary F-184-P1-002/004)

Rewrites the stale Red-phase provenance block (asserted todo!() stub
still existed and tests MUST fail) into past-tense GREEN-state
provenance, and softens the proptest oracle docstring to describe it
accurately as a mutation-catching re-derivation rather than an
independent-correctness proof. No test logic, assertions, or names
changed.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…ope + EC framing (adversary P2 F-1/F-2/NIT)

- F-1: reword the parse_tpkt_header doc comment so it no longer claims
  exhaustiveness "is proven by the VP-048 Kani harness below" -- the harness
  is a no-panic skeleton only; the full exhaustiveness proof is STORY-194's
  obligation.
- F-2 (DF-CANONICAL-FRAME-HOLDOUT-001): add two RFC-1006-derived holdout
  tests (test_rfc1006_s5_canonical_minimal_tpkt_holdout and a 10-byte
  companion), citing RFC 1006 SS5 byte semantics independently of any
  project BC. Soften the proptest oracle rationale comment, which
  overstated that BC-derived vectors "guard against a shared logic error"
  -- both the oracle and the BC vectors trace to the same spec text, so the
  new RFC-1006 holdout is what provides spec-independent grounding.
- NIT: reorder test_BC_2_20_004_trailing_bytes_beyond_declared_length_
  still_accepted_canonical_vector so its first assertion is the genuine
  EC-004 trailing-bytes case; the EC-005 exact-length-match assertion now
  follows as a labeled companion check. No coverage removed.

cargo test --test iso_on_tcp_tests: 26/26 pass (was 24, +2 new holdout
tests). cargo test --all-targets, cargo fmt --check, and cargo clippy
--all-targets -- -D warnings all clean.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…nce doc + VP-048 scope (adversary P3)

Remediates adversarial Pass-3 findings on the TPKT header parser:

- M1: iso_on_tcp.rs no longer claims the deferred #[cfg(kani)] harness
  "proves" anything -- reworded to "scoped to check", with proof execution
  explicitly attributed to STORY-194.
- M2: test file header no longer mis-cites DF-CANONICAL-FRAME-HOLDOUT-001
  as requiring BC vectors verbatim -- clarified that BC vectors cover
  BC-conformance tests while the policy separately requires the
  spec-independent test_rfc1006_s6_* holdout set below.
- L1: corrected RFC 1006 citation from §5 to §6 ("Packet Format")
  throughout the holdout tests; renamed test_rfc1006_s5_* -> test_rfc1006_s6_*.
  Added a genuinely RFC-valid minimum-length (7) holdout vector, and
  relabeled the existing length=4 vector as a documented wirerust/ADR-014
  layering divergence rather than an RFC-conformant vector.
- L2: added an input-independence holdout using length=517 (0x0205),
  a bit pattern absent from every BC-2.20.00x vector.
- N1: CHANGELOG [Unreleased] entry now says "proof harness" with execution
  deferred to STORY-194, not "proof".
- Documented the length>=4 (not >=7) accept-threshold design rationale
  inline at the length guard in parse_tpkt_header, pointing to the new
  divergence-holdout test.

parse_tpkt_header's acceptance logic (length >= 4) is unchanged -- this is
a documentation and test-holdout correction only.

cargo test --test iso_on_tcp_tests: 28 passed, 0 failed (was 26; +2 new
holdout tests, 2 renamed).
cargo test --all-targets: all green.
cargo fmt --check / cargo clippy --all-targets -- -D warnings: clean.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…ing holdout ref (adversary P4 DF-SIBLING-SWEEP)

Prior remediation fixed the RFC 1006 §5→§6 citation in test docstrings but
missed three sibling occurrences in src/analyzer/iso_on_tcp.rs's module and
TpktHeader doc comments, plus left a dangling proptest-oracle docstring
reference to a non-existent test_rfc1006_s5_canonical_minimal_tpkt_holdout
function. Repointed that reference to the actual
test_rfc1006_s6_minimum_valid_length_holdout holdout, fixed its section
number and "below"→"above" direction, and tightened the VP-048 Kani harness
docstring to state its len <= 300 bound instead of claiming "any length".

Citation/reference text only — no logic, test assertions, or test names
changed. 28/28 iso_on_tcp_tests pass unchanged; full suite green; fmt/clippy
clean.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…; tests + docs RFC-conformant

parse_tpkt_header's length-floor guard is now `length < 7` (was `< 4`),
matching RFC 1006 §6's stated minimum TPKT packet length (4-byte header +
3-byte minimum COTP). The structural read-guard (data.len() < 4) and version
check (data[0] != 0x03) are unchanged. Accept range is now [7, 65535].

Retires the earlier documented layering divergence (length=4 accepted as a
TPKT-structural-only floor); this re-opens STORY-184 from its converged state
per human ruling.

Tests (tests/iso_on_tcp_tests.rs):
- BC-2.20.003 reject set extended: length 4, 5, 6 now assert None (new tests
  test_BC_2_20_003_returns_none_for_length_{four,five}_below_rfc_minimum and
  the 6-vs-7 boundary test test_BC_2_20_003_returns_none_for_length_six_boundary_below_rfc_minimum).
- BC-2.20.004 accept set: dropped the length=4 canonical-accept test (moved to
  the BC-2.20.003 reject set above); length=7 is now the genuine RFC-conformant
  accept floor. Updated the reserved-byte and exact-length-match tests to use
  length=7 instead of length=4/6.
- Renamed test_rfc1006_s6_length_four_wirerust_divergence_holdout to
  test_rfc1006_s6_length_four_below_minimum_returns_none; now asserts None
  instead of Some (RFC 1006 §6 min=7; length=4 has no room for COTP).
- test_rfc1006_s6_minimum_valid_length_holdout (length=7 -> Some) is unchanged
  and is now the genuine RFC-conformant accept-floor vector.
- Updated the four-way-partition exhaustiveness test and the proptest oracle
  (both unit-test and property-test sides) to the length >= 7 threshold.
- Removed all "documented divergence" / ADR-014-layering framing from doc
  comments; replaced with accurate RFC-conformant wording throughout.

CHANGELOG.md: folded the earlier intra-dev Fixed §5->§6 entry into the Added
entry, updated to describe the [7, 65535] accept range, and removed internal
process references (adversarial Pass 4, DF-SIBLING-SWEEP-001).

cargo test --test iso_on_tcp_tests: 30 passed (was 27), 0 failed.
cargo test --all-targets: all green. cargo fmt --check and
cargo clippy --all-targets -- -D warnings: clean.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…versary NIT)

The §5→§6 citation correction was intra-development churn (the module's
INTRODUCING changelog entry never shipped with §5 citations in any
release), not a user-facing change. Removed per adversarial NIT F2;
substantive Added entry describing the TPKT header parser is unchanged.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
…e-87)

Library/test-harness demo evidence (no CLI/web surface yet at this story's
scope). Captures the 30/30-green cargo test --test iso_on_tcp_tests run and
maps AC-184-001..006 to their exercising tests, mirroring the STORY-167
(IEC-104 parse_apci_header) precedent this story's shape follows. Path-scrub
gate (PG-W70-DEMO-SCRUB) run clean against docs/demo-evidence/STORY-184/.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
@Zious11

Zious11 commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Fresh-eyes review — STORY-184 (parse_tpkt_header)

Verdict: APPROVE. Clean pure-core free function, exhaustive BC-traceable tests, CI-green locally (30/30 tests pass, clippy --all-targets -D warnings clean, fmt --check clean). PR description accurately reflects the diff (BC-2.20.001–004, VP-048 skeleton, ADR-014 landed alongside). No blocking or major findings.

Two NIT items (non-blocking, no change required to merge):

NIT-1 — RFC attribution precision. The phrase "RFC 1006 §6's stated minimum packet length of 7" recurs in src/analyzer/iso_on_tcp.rs, tests/iso_on_tcp_tests.rs, and the CHANGELOG. The value 7 is derived (4-byte TPKT header per RFC 1006 + 3-byte minimum COTP TPDU per ISO 8073), not a figure RFC 1006 §6 states on its own. The floor itself is correct and human-ratified; only the "§6 stated" attribution is slightly loose. Consider "RFC 1006 4-byte header + ISO 8073 minimum 3-byte COTP TPDU (= 7)" if these strings are ever touched again.

NIT-2 — stale proptest-regression seeds (informational). tests/iso_on_tcp_tests.proptest-regressions is committed with two seeds recorded during the length-floor 4→7 re-opening (shrinks to len_hi=0, len_lo=4 = the retired length-4 accept; data=[]). Both re-run green today (verified — they run automatically before novel cases and pass). Committing this file is proptest's own recommendation, so this is acceptable; noting only that the seeds are historical artifacts of the re-open, not live failures.

Positives worth calling out: honest disclosure that the oracle proptest is a mutation-catcher (shares BC lineage) with genuine spec-independent grounding via the test_rfc1006_s6_* holdout vectors; explicit 6-vs-7 accept-floor boundary pair; reserved-byte-ignored and max-length (65535) coverage; version-before-length short-circuit test.

@Zious11

Zious11 commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Review Cycle 1 Triage

Finding Severity Routed To Status
NIT-1: RFC attribution precision ("§6 stated" vs. derived 4+3=7) NIT No action — non-blocking, cosmetic wording only Accepted as-is
NIT-2: stale proptest-regression seeds are historical artifacts of the length-floor 4→7 re-open NIT No action — committed regression file re-verified green, per proptest convention Accepted as-is

Verdict: APPROVE (0 BLOCKING, 0 MAJOR, 2 NIT — both accepted without code change).
Security review: CLEAN (0 findings, all severities).
Convergence: reached in cycle 1.

Proceeding to CI verification and merge.

@Zious11
Zious11 merged commit 7ce0db5 into develop Sep 7, 2026
26 checks passed
@Zious11
Zious11 deleted the feature/STORY-184-tpkt-header-parser branch September 7, 2026 02:00
Zious11 added a commit that referenced this pull request Sep 7, 2026
…GATION-ADR014-CLAUDEMD RESOLVED

PR #466 squash-merged to develop as 7ce0db5 (TPKT header parser, RFC-min-7
rework). Per-story adversarial CONVERGED 3/3; pr-reviewer APPROVE; security
CLEAN; CI 13/13. ADR-014 + CLAUDE.md port-102 edit landed on develop via this
PR, resolving the F4-OBLIGATION-ADR014-CLAUDEMD carry-forward (ADR-014 stays
proposed until F7).

- STORY-184.md: status ready -> delivered
- STORY-INDEX.md: v4.25 -> v4.26 (status column + wave-87 delivery-progress
  row; totals unchanged 147/97/863)
- STATE.md: v2.8 -> v2.9; develop_head 97361cd -> 7ce0db5; stories_delivered
  120 -> 121; phase/current_step/EXACT RESUME POINT updated to F4 IN PROGRESS;
  D-562 decision recorded; F4-OBLIGATION-ADR014-CLAUDEMD marked RESOLVED; two
  new carry-forwards added (DEFERRED-BC-2.20.005-STALE-LEN4,
  BC-2.20.002-LOW-DOUBLE-GUARD)
- cycles/feature-s7comm/lessons.md: new — two process-gap observations
  (PG-CHECK-GREEN-DOC-TENSE-BLINDSPOT, PG-CANONICAL-HOLDOUT-NOT-AC-ENFORCED)
- cycles/feature-s7comm/session-checkpoints.md: archived D-561 checkpoint
- code-delivery/STORY-184/: PR description + review evidence

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
Zious11 added a commit that referenced this pull request Sep 7, 2026
…-min-7 (STORY-185 pre-impl) + rehash

BC-2.20.005's postcondition-4 parenthetical, EC-001, EC-002, and the
empty-vector canonical row claimed a TPKT length==4 header-only frame
legitimately produces an empty tpkt_payload. That is stale under
BC-2.20.004's RFC-min-7 accept floor (parse_tpkt_header now rejects
length < 7, STORY-184) — the smallest valid frame has length==7, a
3-byte payload, never empty. Replaced with truncated-delivery framing:
an empty/short tpkt_payload here can only arise from data.len() < length
at the TPKT layer, not a legitimately-parsed header-only frame. The
COTP-parse behavior contract itself is unchanged.

Resolves DEFERRED-BC-2.20.005-STALE-LEN4 (D-562 carry-forward), marked
RESOLVED in STATE.md Active Carry-Forwards. Not a phase transition;
phase/current_step otherwise left as-is (STORY-185 delivery is just
starting).

Rehash (canonical bin/compute-input-hash --write only):
- BC-2.20.005's own input-hash unchanged (cf116b5 — inputs are ADR-014
  + ARCH-INDEX.md, neither touched by this body-only edit); confirmed
  via direct tool invocation.
- STORY-185 cites BC-2.20.005 as an input, so its hash cascades:
  275ae46 -> 7f6bb1e. Repo-root --scan confirms MATCH; ADR-014 now
  resolves natively (landed on develop via PR #466 7ce0db5, no
  workaround needed); STORY-194's re-verification anchors do not
  include BC-2.20.005 and it remained MATCH, unaffected.
- Full --scan: MATCH=125, STALE=22 — the pre-existing 22-story
  background-stale set is unchanged (STORY-185 was the sole
  newly-affected story, now back to MATCH).

Burst-log note appended to cycles/feature-s7comm/burst-log.md.

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
Zious11 added a commit that referenced this pull request Sep 7, 2026
…es delivered

Session-wrap-pause checkpoint (D-564). Pipeline IN-PROGRESS→PAUSED;
version 3.0→3.1. F4 delta-implementation (feature-s7comm, epic E-23)
mid-cycle: STORY-184 (#466, wave 87) + STORY-185 (#467, wave 88)
DELIVERED to develop e0ea30c; STORY-186 worktree created as a clean
baseline (no code yet) on feature/STORY-186-iso-on-tcp-reassembly.

D-563 Session Resume Checkpoint archived to
cycles/feature-s7comm/session-checkpoints.md; new checkpoint written
with all six required fields. Current Phase Steps: new row added,
D-559 row evicted (preserved verbatim in Decisions Log D-559).
regression-state.json + sidecar-learning.md hook-churn folded in.

No story/spec/code content changed — bookkeeping-only wrap-pause
burst, the only STATE.md mutation performed during wrap
(BC-6.28.001 INV-1), single-commit burst (TD-VSDD-053).

Claude-Session: https://claude.ai/code/session_01EQAaPvh9fwaG31jmkPicKW
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.

1 participant