Skip to main content

Requirements Traceability

Status: verification-engineering work product. Traces the 40 safety requirements (docs/safety/safety_requirements_specification.md) bidirectionally to the code that implements them and the tests that exercise them, then computes requirements-driven coverage. Spine = the SR table; function IDs = docs/safety/system_definition.md §4.

Standing caveat (unchanged from the SRS). Target integrity SIL 3 / PL e is allocated, not achieved. The numeric claim gates on FMEDA, Option-B fault-injection (SR-R-03), and SC 3 process evidence (HARA §6). Nothing in this matrix advances that claim — it measures verification completeness against the requirement set, not the systematic-capability argument. Every integrity-decomposition claim in the SRS remains conditional on independence being demonstrated (β unquantified).


1. Method​

Trace is bidirectional: each SR → its allocated function(s) F-xx → implementation file:line (taken from the SRS "Satisfied" citations, not re-derived) → the specific test(s) that exercise it, and back (function → SR reverse map, §5). A requirement is scored Verified only when a real test in this tree actually exercises the behaviour it claims — implementation existing is not verification. Requirements coverage is reported two ways: (a) the fraction of SRs with ≥1 passing verifying test (test-existence), and (b) the fraction of safety-relevant functions F-xx traced to ≥1 SR (requirement-completeness); §5 exposes any function with no requirement as a coverage hole. Structural coverage (statement/branch/MC-DC) is credited only where a host harness measures it — 100% line/branch/MC-DC on firmware/main/estop_verdict.c (the extracted decision core) and 70.4% line / 56.8% branch on host/machine_app_runner.c (docs/safety/coverage/host-summary.json); the ROS 2 node has no coverage run yet. The pre-qualified pstop_c library is referenced at its interface only (its own req_*_test.c suite is cited as evidence for the SR-I integration obligations; internals are not re-verified).

Status vocabulary

StatusMeaning
Verified≥1 real test exercises the full claimed behaviour (passing).
Partially-verifiedThe safety-argument leg is implemented and some test touches it, but a required test, quantification, or decomposed leg is outstanding.
Unverified-gapNo test exercises the requirement in-tree — whether unimplemented (SRS "Gap") or implemented-but-untested. Both are verification holes.
Residual-acceptedDeliberately not implemented / non-safety; rationale recorded in the SRS.

2. Traceability matrix​

Test-file shorthand: EV = firmware/test/test_estop_verdict.c; MR = tools/pstop_multi_remote_test.py (group letter in brackets); HIL10/20/30 = tools/hil/test_10_button.py / test_20_discordance.py / test_30_power_cycle.py; JL = ros2/protective_stop_machine/test/test_json_lite.cpp; REQ n_nn = pstop_c/pstop/test/src/pstop/requirements/req_n_nn_test.c.

2.1 System level (SR-SYS)​

SRAlloc F-xxCode (file:line)Verifying test(s)MethodStatus
SR-SYS-01F-R-01..05, F-H-03, F-M-03/04, final elemtiming budget (HARA A-05)NO TEST measures end-to-end latency; HIL20/30 wait within a heartbeat budget but assert STOP-occurred, not the ≤2.1 s internal budget (was ≤1.3 s before max_missed 3 → 5 on 2026-08-04)Analysis + Test (latency)Unverified-gap
SR-SYS-02F-R-02/06, F-H-03, F-P-03/04endpoint freshness pathConstituents: EV (fresh-sample OK), MR[F] (bad-CRC→STOP), REQ 2_02/2_03 (replay/lost). End-to-end staleness/forgery NO TESTTest + Fault-injPartially-verified
SR-SYS-03F-R-03, F-H-02, F-M-03arming pathHIL10 (fresh needs gesture; short-blip defers), HIL30 (boot no re-arm), MR[A] I3/I4TestPartially-verified (host floor SR-H-03 unverified)
SR-SYS-04F-H-05, F-P-04many-to-one ORMR[B/D/E/F] 32/32 (any-STOP OR, ownership)Test (E2E)Partially-verified (ghost-bond masking unquantified)
SR-SYS-05F-M-03/04, machn relaymachn series relay + fbSR-M-03/04 tested (JL/backend default). machn hardware final element NO TEST in this treeAnalysis + TestPartially-verified
SR-SYS-06F-R-03main.c reclose-only debounceEV #1 ("open → immediate STOP" same tick; reclose re-runs debounce)TestVerified
SR-SYS-07F-P-02, F-H/M dispatchmachine.c default→STOPMR[A] I3, HIL10 (fresh needs gesture), HIL30 (boot), REQ 3_04Inspection + TestVerified
SR-SYS-08F-R-06/07, F-P-01/04endpoint CRC/echo/clampREQ 2_01 (corruption), MR[F] (bad-CRC→STOP). Residual bit-error/FMEDA NO TESTAnalysisPartially-verified
SR-SYS-09F-P-02, F-H-05 (+F-M/machn cfg)machine.c:135-142 (STOP→OK gated on is_stop_only==false), :131,155-156 (stop-only STOP still stops), :16,31 (remote_details_cb→is_stop_only), :266-320 (heartbeat-monitored)machine_test.c:677 (test_bond_stop_ok_stop_only_operator), machine_test.c:1165 (test_2_clients_stop_only_stop); MR[E] (allowlist / stop-only). Shell operator-list config plumbing (machn /api/operators) NO TESTTest + InspPartially-verified

2.2 Remote firmware (SR-R)​

SRAlloc F-xxCode (file:line)Verifying test(s)MethodStatus
SR-R-01F-R-01main.c:265-290 (estop_channel_closed)EV #1/#2 (both-phase truth table; every fault→STOP), 100% MC-DC on estop_verdict.cTest + InspVerified
SR-R-02F-R-02main.c:304-306,340-348EV #2 (arith-image select; no fault manufactures OK), MC-DCTest + MC/DCVerified
SR-R-03F-R-02main.c:305/307[Reconciled 2026-08-07] Object-code diff gate now shipped + CI-wired (scripts/check_estop_diversity.sh, firmware-build.yml; d96129c, DU-3 CLOSED). EV #3 proves equivalence; the object-code non-identity leg is now tested. Residual: identical-logic-fault-injection divergence still owedFault-inj + obj-diffPartially-verified
SR-R-04F-R-02main.c:340-348 (STOP-only override)EV #2 (health/debounce never lowers to OK)Test + MC/DCVerified
SR-R-05F-R-03main.c:323-347EV #1 (OPEN→STOP same tick; only reclose debounced)TestVerified
SR-R-06F-R-03main.c:359-363,903 (estop_primed)EV #4/#4b (primed only when both cores settle), HIL30 (cold boot no re-arm; held-open→STOP flows)TestVerified
SR-R-07F-R-04main.c:887-903 (transmit-on-agreement)HIL20 (single-channel divergence → comparator silent → machine liveness STOP; no STOP on wire)Test + Fault-injVerified
SR-R-08F-R-04(relies on libc memcmp)NO TEST — per-offset positive-diff self-test absent (DU-7)Fault-injUnverified-gap
SR-R-09F-R-01periodic pad-config read-back + drive-low self-test → controlled reset (gpio_cfg_fault); 1cfbd9a[Reconciled 2026-08-07] Implemented + logic host-reasoned; DU-1 CLOSED (clock_guard_and_gpio_re_verify.md). Residual: on-bench corruption of a live pad config is bench-blockedFault-injPartially-verified
SR-R-10F-R-01not implementedNO TEST — common-cause pull-down detection absent (DU-6)Fault-inj + AnalysisUnverified-gap
SR-R-11F-R-01ESTOP_SETTLE_US bench constantNO TEST — settle-vs-deployed-cable characterization absent (DU-8)Analysis + TestUnverified-gap
SR-R-12F-R-05, F-P-01single shared encode imageNO TEST — golden-vector encode test absent (DU-5)Test + Fault-injUnverified-gap
SR-R-13F-R-06main.c:741/746/767/779 (echo relayed, not synthesized)Library anti-replay REQ 2_02/2_03; MR[F] bad-CRC→STOP. Remote-side spoofed-reply→no-OK integration NO TESTTest + InspPartially-verified
SR-R-14F-R-06/07main.c:568,572-579 (sess_ensure_socket dark on VPN=0)NO TEST — VPN-drop regression absent (implemented, untested)TestUnverified-gap
SR-R-15F-R-09not implemented (peer slots not CRC/version checked)NO TEST (FMEA R09-1)TestUnverified-gap

2.3 Host machine (SR-H)​

SRAlloc F-xxCode (file:line)Verifying test(s)MethodStatus
SR-H-01F-H-03, F-P-03machine.c:290-298; host machine_app_runner.c:721MR[C] I2 (silence→STOP), MR[F] (timeout→STOP), HIL20/30 livenessTestVerified
SR-H-02F-H-02machine_app_runner.c:375-379,834-844,666MR[A] A2a/A2b/A4 I4 (short cycle refused), HIL10 (short blip defers)TestVerified
SR-H-03F-H-02host cfg_validate refuses unsafe timing at startup (machine_app_runner.c:656+)[Reconciled 2026-08-07] tools/test_config_floor.py 7/7 (rejects min_stop_ms=0, out-of-envelope heartbeat/max_missed); DU-4 CLOSEDTestVerified
SR-H-04F-H-03, F-P-03machine-side clock-freeze guard (clock_guard, host + machn; SR-H-04b machine_app_runner.c:332)[Reconciled 2026-08-07] Pure unit-tested clock_guard (host) + machn HIL (machine_clock_guard_hil_validation.md); DU-2 CLOSED. Residual: genuine esp_timer-freeze can't be injected on unmodified silicon (proven on remote analog)Fault-injVerified
SR-H-05F-H-05pstop_c OR propertyMR[B/D/E] (OR, ownership, ghost bond). Random-fault ghost-bond masking unquantifiedTestPartially-verified
SR-H-06F-H-01machine_app_runner.c:752-757 (pre-dispatch filter)MR[F] F3 (out-of-range type: no crash, no spurious arm), MR[E] allowlistInsp + TestVerified

2.4 ROS 2 machine (SR-M)​

SRAlloc F-xxCode (file:line)Verifying test(s)MethodStatus
SR-M-01F-M-07machine_bridge_node.cpp:72-97,323,354,28-35test_timing_floors (8/8, every accept/reject boundary) + test_node_runtime runtime rejection: SetParametersRejectsUnsafeMinStop/RejectsOversizeHeartbeat/AcceptsTighter (set-param) and ConfigureServiceApplies/RejectsUnsafe (service) — reject-loosening exercised at unit, param, and service levels (closes M07-1/DU-4)TestVerified (2026-08-02)
SR-M-02F-M-04/05hardware_backend.cpp:119 (bool_at("relay_stop", true))JL exercises bool_at default mechanism (present-key), not the missing-key→STOP safety default directlyTestPartially-verified
SR-M-03F-M-04, F-M-08hardware_backend.cpp / machine_bridge_node.cpp:286test_hardware_parse (malformed/empty→blind) + test_hardware_backend ClosedPortUnreachable/Non2xxUnreachable (backend→unreachable) + test_node_runtime HardwareUnreachableDiagnosesError (state→UNSTABLE and diagnostics ERROR) — closes M04-1TestVerified (2026-08-02)
SR-M-04F-M-03software_backend.cpp:199 (relay.applicable=false)Inspection (safety-case scope statement)InspectionResidual-accepted
SR-M-05F-M-05json_lite.hpp:87,97 (depth cap 32)JL RejectsPathologicalNesting (500-deep → clean reject, no crash), RejectsMalformedTestVerified
SR-M-06F-M-05json_lite.hpp:185-198JL NumbersBoolsNull (tolerant numeric parse) — confirms no safety field is numericInspection + TestResidual-accepted

2.5 pstop_c interface / integration (SR-I)​

SRAlloc F-xxCode (file:line)Verifying test(s)MethodStatus
SR-I-01F-P-01, F-R-05encode main.c:422, decode :745 (library size/span)REQ 2_01 (CRC corruption detect); MR drives real 40-byte + CRC16 wire. Golden-vector pin NO TEST (= SR-R-12)TestPartially-verified
SR-I-02F-P-02, F-H-01/F-M-02machine.c:196-201; host :752-757; ROS build_backendREQ 2_08 (only OK/STOP/BOND/UNBOND), REQ 2_07 (wrong machine-ID reject), REQ 3_04 (stay STOP until bond+OK); MR[F] malformed→STOP; MR[A] I3Test + InspVerified
SR-I-03F-P-03, F-H-03host binds CLOCK_MONOTONICREQ 3_05/3_01 (heartbeat-rate validity). Frozen-clock diagnostic NO TEST (= SR-H-04)Fault-injPartially-verified
SR-I-04F-P-04, F-R-06remote relays echo, no synth main.c:767,779REQ 2_02 (out-of-order), REQ 2_03 (lost), REQ 2_01 (corruption). Shell-level replay integration NO TESTTest + InspPartially-verified

3. Requirements coverage summary​

3.1 Headline numbers​

  • SRs with at least one cited verifying test: 32 / 40 = 80.0 %‡
  • Strict, fully-verified only: 17 / 40 = 42.5 %
  • Functions traced to at least one SR: 22 / 27 = 81.5 %
  • Functions traced excluding declared non-safety functions: 22 / 25 = 88.0 %

‡ Citation resolution, not test execution or passing state, is checked by the linter.

  • (a) Requirements coverage: See the generated citation and fully-verified figures above. [Reconciled 2026-08-07: +4 as DU-1/2/3/4 closures gained tests — SR-H-03/SR-H-04 now Verified, SR-R-03/SR-R-09 now Partially-verified]. The Partially-verified requirements each leave a named leg untested — end-to-end, quantification, golden-vector/replay, fault-injection divergence, or the operator-list config plumbing. SR-M-04 is Residual and inspection-only; the Unverified-gap requirements have no test at all.

  • (b) Function traceability: See the generated figures above. Section 5 records functions with no requirement and distinguishes declared non-safety functions from genuine requirements-coverage holes.

3.2 Breakdown by area​

AreaCountVerifiedPartially-verifiedUnverified-gapResidual-accepted≥1 cited test %Fully-verified %
SR-SYS9261088.9 %22.2 %
SR-R15636060.0 %40.0 %
SR-H65100100 %83.3 %
SR-M6310283.3 %†50.0 %
SR-I41300100 %25.0 %
Total4017147280.0 %42.5 %

† The SR-M cited-test percentage includes SR-M-06 as Residual-with-test; see the generated SR-M row above. Citation resolution does not establish test execution or passing state.

Reading: SR-I is fully touched by the pre-qualified pstop_c suite but never completed (golden-vector + replay integration missing). SR-R is the weak area — 8 of 15 have no test, and it holds five of the six highest-priority DU gaps. Structural coverage confirms the split: the decision core (estop_verdict.c) is at 100 % MC-DC, the host runner sits at 56.8 % branch, and the ROS 2 node — now structurally covered (2026-08-02) — is at ~89 % line (machine_bridge_node.cpp 98 %, hardware_backend.cpp 99 %; see coverage_report.md §1).


4. Test-gap register (input to "drive coverage up")​

Ranked. P0 = highest-priority DU / cheapest-high-value first (OPEN_ITEMS §3b); P1 = implemented-but-untested (test only, no code work); P2 = Partial→Verified closers; P3 = requirements-completeness holes (§5).

RankSRGapDU / cross-refWork needed
P0-1SR-H-03Host accepts unsafe timing config DONE 2026-08-02 (cfg_validate + test_config_floor.py 7/7)DU-4 CLOSED✅ compiled envelope in host cfg_load; rejects unsafe values
P0-2SR-R-09GPIO pad config never re-verified DONE (1cfbd9a; gpio_cfg_fault)DU-1 CLOSED✅ periodic read-back + drive-low self-test → reset. Residual: live-corruption fault-injection bench-blocked
P0-3SR-H-04Frozen/backward clock disables heartbeat DONE (clock_guard host + machn HIL)DU-2 CLOSED✅ clock-freeze guard → STOP; HIL-validated
P0-4SR-R-03Compiler may erase Option-B diversity DONE (check_estop_diversity.sh CI gate, d96129c)DU-3 CLOSED✅ object-code diff gate. Residual: identical-fault-injection divergence test
P0-5SR-R-08memcmp mis-returns equal undetectedDU-7Per-offset positive-diff self-test (40 offsets)
P0-6SR-R-12Common-mode encode faultDU-5, SR-I-01Golden-vector encode test + second encoder image
P1-1SR-R-14VPN-drop → dark path (implemented, untested)FMEA R06-3VPN-drop regression test
P1-2SR-M-01validate_timing reject-loosening DONE 2026-08-02 (test_node_runtime set-param + service rejection, test_timing_floors)DU-4/M07-1✅ ROS 2 set-param rejection test added
P1-3SR-M-03Unreachable /state.json→UNSTABLE DONE 2026-08-02 (test_hardware_backend + test_node_runtime HardwareUnreachableDiagnosesError)M04-1✅ Drop-/state.json backend + node test added
P1-4SR-M-02Missing-relay_stop→STOP default (partial)M04-2Field-level missing-key→true test
P2-1SR-I-01Golden-vector encode/decode both ends(= SR-R-12)Author golden-vector both-end pin
P2-2SR-I-04Shell-level replay integrationHARA §7 item 6Replay reply → no-OK integration test
P2-3SR-R-13Remote-side spoofed-reply→no-OKR06-1Replay-reply integration on remote
P2-4SR-SYS-01End-to-end latency ≤2.1 s unmeasured (budget lengthened from ≤1.3 s by max_missed 3 → 5, 2026-08-04)HARA A-05Injected-demand→STOP latency measurement
P2-5SR-SYS-02End-to-end staleness/forgeryH-05/H-11Stale/replayed-OK end-to-end test
P2-6SR-SYS-04 / SR-H-05Ghost-bond random-fault masking unquantified—FMEDA / random-fault campaign
P2-7SR-SYS-05machn hardware final element untested hereH-10/A-07machn series-relay + feedback bench test
P2-8SR-SYS-08Residual bit-error/masquerade rateA-08FMEDA feed (analysis)
P3SR-R-10, SR-R-11, SR-R-15Unimplemented DU/config itemsDU-6/DU-8/R09-1Implement, then test

Not in the register (deliberate): SR-M-04 and SR-M-06 are Residual-accepted (non-safety software backend / no numeric safety field) — no test owed.


5. Function → SR reverse map​

Every F-xx from system_definition.md §4 and the SR(s) whose "Allocated to" touches it. A function with no SR is a requirements-coverage hole.

5.1 Remote firmware (F-R)​

F-xxFunctionSRs touching it
F-R-01Dual-channel loop sensingSR-SYS-01, SR-R-01/09/10/11
F-R-02Per-core verdict (Option A/B)SR-SYS-02, SR-R-02/03/04
F-R-03Release debounce + boot holdSR-SYS-03/06, SR-R-05/06
F-R-04Lockstep comparatorSR-R-07/08
F-R-05Message encode + transmitSR-SYS-01, SR-R-12, SR-I-01
F-R-06Black-channel round-tripSR-SYS-02/08, SR-R-13/14, SR-I-04
F-R-07Network transport mgmtSR-SYS-08, SR-R-14
F-R-08Status LED ring— none (declared non-safety)
F-R-09Boot / NVS / identitySR-R-15
F-R-10Admin/web interface— none (declared non-safety; SYSTEM_DEFINITION requires a non-interference argument, currently unrequirement'd)

5.2 Host machine (F-H)​

F-xxFunctionSRs touching it
F-H-01Host pstop_c machine roleSR-H-06, SR-I-02
F-H-02Arming policySR-SYS-03, SR-H-02/03
F-H-03Heartbeat/livenessSR-SYS-01/02, SR-H-01/04, SR-I-03
F-H-04Robot status output + logging— none (HOLE: undeclared, no SR)
F-H-05Many-to-one aggregationSR-SYS-04/09, SR-H-05

5.3 ROS 2 machine (F-M)​

F-xxFunctionSRs touching it
F-M-01Lifecycle node mgmt— none (HOLE: undeclared, no SR)
F-M-02Backend abstractionSR-I-02
F-M-03Software backendSR-SYS-01/03/05, SR-M-04
F-M-04Hardware backendSR-SYS-01/05, SR-M-02/03
F-M-05JSON parsingSR-M-02/05/06
F-M-06Publish status/relay/bonded on ROS topics— none (HOLE: undeclared, no SR)
F-M-07Parameter validation + reconfigSR-M-01
F-M-08DiagnosticsSR-M-03

5.4 Pre-qualified pstop_c interface (F-P)​

F-xxFunctionSRs touching it
F-P-01Encode/decode + CRC-16SR-SYS-08, SR-R-12, SR-I-01
F-P-02Machine dispatch (default→STOP)SR-SYS-07/09, SR-I-02
F-P-03Heartbeat on monotonic clockSR-SYS-02, SR-H-01/04, SR-I-03
F-P-04Counter/stamp echo + LOST/OOOSR-SYS-02/04/08, SR-R-14(via 06), SR-I-04

5.5 Requirements-coverage holes​

F-xxNatureAction
F-R-08LED status — declared non-safetyAcceptable; note in safety-case scope.
F-R-10Admin/web — declared non-safetyNeeds a non-interference requirement — SYSTEM_DEFINITION §6 demands F-R-10 be shown non-interfering with the verdict path; no SR currently captures that obligation.
F-H-04Robot status output / operator loggingAdd an SR or explicitly scope non-safety. It carries the operator-visible STOP/OK signal — arguably needs a "must reflect committed verdict" requirement.
F-M-01ROS 2 lifecycle mgmtAdd an SR or scope non-safety. Lifecycle transitions gate activation of the safety path (configure/activate/error).
F-M-06ROS 2 topic publicationAdd an SR or scope non-safety. Publishes status/relay/bonded-remotes; if any downstream consumer treats these as safety, an obligation is missing.

End of traceability matrix. Coverage figures measure verification completeness against the requirement set only; they do not substantiate the SIL 3 / PL e claim, which remains allocated-not-achieved pending FMEDA, Option-B fault-injection (SR-R-03), and SC 3 process evidence.