Skip to content

fix(safety-docs): correct ASIL declaration + RxSequenceGuard/Zone-model staleness - #178

Merged
SoundMatt merged 2 commits into
mainfrom
audit-fix/batch5-asil-and-docs
Aug 23, 2026
Merged

fix(safety-docs): correct ASIL declaration + RxSequenceGuard/Zone-model staleness#178
SoundMatt merged 2 commits into
mainfrom
audit-fix/batch5-asil-and-docs

Conversation

@SoundMatt

Copy link
Copy Markdown
Owner

Fixes three confirmed HIGH findings from the cpp-RCP v3.0.0 deep audit, verified individually against source before editing.

Finding A — .fusa.json project ASIL contradicted .fusa-hara.json

.fusa.json's project.asil was ASIL-B, but .fusa-hara.json's H-001 (and its safety goals SG-001/SG-003/SG-007) are ASIL-C, and HARA.md's H-001 rationale explicitly states no decomposition is claimed — S3/E4/C2 maps to ASIL-C directly.

Fix: .fusa.json's asil/project.asil corrected to ASIL-C.

Tooling bug discovered en route: the pinned cpp-FuSa v0.18.0 binary's config loader (src/config/config.cpp) only reads a top-level asil/standard key — never the nested project.asil/project.standard this repo's .fusa.json schema used. So HARA005 was silently falling back to the tool's own hardcoded "ASIL-B" default regardless of what project.asil said; the previous ASIL-B value only ever "matched" that default by coincidence. Confirmed by editing only the nested field first — cpfusa check still printed HARA005: Hazard ASIL ASIL-C exceeds project ASIL ASIL-B. Fixed by adding top-level asil/standard fields (matching cpp-FuSa's own dogfooded .fusa.json) alongside the existing nested ones.

Verified: cpfusa check now reports [iso26262/ASIL-C] and HARA005 no longer fires (0 errors, 1 pre-existing unrelated warning — FUSA004, missing evidence file, expected without a test run).

Docs updated (project-wide "target ASIL" statements only, each with a one-line justification citing HARA.md/H-001): README.md, AUDIT_PACK.md (doc metadata + intro sentence only — see follow-ups), TARA-ANALYSIS.md, SAFETY_PLAN.md.

Left alone (narrowly-scoped, correctly ASIL-B per .fusa-hara.json): SG-002/004/005/006/008/010/011.

Flagged as ambiguous, deliberately not changed: README.md's per-header "(ASIL-B)" design-pattern labels (watchdog.hpp/redundancy.hpp rows), INCIDENT-RESPONSE.md's "ASIL-B requirement violation" severity example — neither clearly asserts the project's own target ASIL.

Not touched: safety-case.json/md, sas.json/md, sci.json, fmea.*, tooling/tara.* — these are regenerated by cpfusa safety-case/etc. on every tagged release (release.yml) via a bot PR and will pick up the corrected ASIL automatically; hand-editing would just be overwritten.

Finding B — HARA.md/FORMAL_VERIFICATION.md said RxSequenceGuard was unwired; it's been wired since Phase 4 batch C

Verified directly against include/rcp/mock.hpp: Server holds a seq_trackers_ member (std::array<e2e::RxSequenceGuard, ...>), and both dispatch_e2e() and dispatch_frame_e2e() call it via a shared seq_gate_admits() helper on every dispatched request/frame before CRC unwrap — integration-tested in tests/test_mock.cpp, not just RxSequenceGuard's own standalone tests/test_e2e.cpp unit test. include/rcp/e2e.hpp's own "UPDATE (Phase 4/Phase 17 batch C...)" file-header comment corroborates this as the authoritative account.

Fix: updated HARA.md's SG-004 row, full H-004 section, and its Residual Risks row from "not wired"/"effectively Open" to the current "Mitigated (opt-in)" state (matching c-RCP's own disposition for its equivalent mechanism) — while being explicit this covers mock::Server's own reference dispatch; a caller driving a transport Server with fully custom dispatch logic instead of wiring mock::Server::dispatch_frame()/dispatch_frame_e2e() as the FrameHandler isn't automatically covered (per udp.hpp/l2.hpp's own header notes). Updated FORMAL_VERIFICATION.md's SP1/SP2 point 1 to match; left point 2 (the TLA+ spec modeling the pre-RFC-1982 algorithm — a separate, still-true, still-open issue) untouched.

Did not change H-004's S2/E3/C2/ASIL-B classification or claim anything about coverage/testing beyond what mock.hpp/test_mock.cpp actually show.

Finding C — SAFETY_PLAN.md still documented the retired Zone/Command/Controller model

Verified all five dead requirement ids (REQ-CTRL-025/026/027/004, REQ-ERR-011) are absent from .fusa-reqs.json, .fusa-reqs-pending.json, and include/.

  • "Zone mismatch detection" → repointed to REQ-MOCK-010 ("dispatch to an unmapped byte_bus_id reports invalid_parameter"), the real current endpoint-addressing-validation mechanism, matching HARA.md's SG-002 "Addressed By" entry.
  • "Deadline monitor" row's description reworded to drop stale "zone"/"Config.Deadline" wording (REQ-DL-002 id was already correct).
  • "Payload copy-on-send", "Payload copy-on-publish", "Context / deadline propagation" (also citing dead ids, named in the finding but not walked through in its concrete fix guidance): verified no current analog exists for any of the three in the stream/endpoint/register-map wire-protocol architecture. Marked "No direct analog" per instructions rather than inventing a false mapping.
  • Also fixed the Safety Goals table in the same document (SG-001/003/007 ASIL-B → ASIL-C per .fusa-hara.json, part of Finding A; "Commands"/"CmdWatchdog"/"Dead zone" terminology retired, part of Finding C) since it sits directly above the mechanisms table and shares the identical root causes.

Verification

  • python3 -c "import json; json.load(open('.fusa.json'))" — passes.
  • cpfusa check — HARA005 gone; 0 errors, 1 pre-existing unrelated warning.
  • cpfusa lint — 146 warnings both before and after (git stash diff) — unchanged, confirming no new findings from these doc/JSON edits.
  • cpfusa trace — pre-existing dangling-test-tag/no-LLR-children warnings only, all in tests/*.cpp, unrelated to and untouched by this batch.

Follow-ups flagged, not resolved by this pass

  • AUDIT_PACK.md §2's ASIL-D gap-analysis derogation table and its ASIL-B(D) = ASIL-A + ASIL-B decomposition math predate the ASIL-C correction and need dedicated safety-case re-derivation (flagged in-document with an explicit note).
  • AUDIT_PACK.md §3's "ASIL-C upgrade path"/"80% ASIL-C target" MC/DC wording now reads oddly against a project that already targets ASIL-C; left untouched since the underlying coverage-gap facts are unaffected and touching MC/DC threshold language felt too far outside a documentation-consistency fix.
  • .fusa-hara.json itself has a latent, separate inconsistency discovered while reading it for this batch: H-003 and H-005 are rated ASIL-B as hazards, but their own safety goals SG-003/SG-007 are rated ASIL-C. Out of scope for these three findings; noted for a future pass.
  • No new requirement ids were invented.

This closes 3 findings from the cpp-RCP v3.0.0 deep audit.

SoundMatt and others added 2 commits August 22, 2026 16:36
…el staleness

Fixes three confirmed findings from the cpp-RCP v3.0.0 deep audit.

Finding A (HIGH) — .fusa.json project ASIL contradicted .fusa-hara.json:
- .fusa.json's project.asil was ASIL-B; H-001 (and SG-001/SG-003/SG-007)
  in .fusa-hara.json are ASIL-C, with HARA.md's H-001 rationale
  explicitly stating no decomposition is claimed. Corrected to ASIL-C.
- Discovered en route: the pinned cpp-FuSa v0.18.0 binary's config
  loader (src/config/config.cpp) only reads a top-level "asil"/
  "standard" key, never the nested "project.asil"/"project.standard"
  this repo's .fusa.json schema used — so the HARA005 rule was silently
  falling back to its own hardcoded "ASIL-B" default regardless of what
  project.asil said (the previous ASIL-B value only ever "matched" by
  coincidence). Added top-level "asil"/"standard" fields (matching
  cpp-FuSa's own dogfooded .fusa.json) alongside the existing nested
  ones so the pinned tool actually reads the corrected value. Verified:
  `cpfusa check` before this fix reproduces "HARA005: Hazard ASIL
  ASIL-C exceeds project ASIL ASIL-B" even after editing only the
  nested field; after adding the top-level fields, `cpfusa check`
  reports [iso26262/ASIL-C] and HARA005 no longer fires (0 errors,
  1 pre-existing unrelated warning: FUSA004, missing evidence file).
- Propagated the same correction, with a one-line justification citing
  HARA.md/H-001, to the other project-wide "target ASIL" declarations
  found by grepping the repo: README.md, AUDIT_PACK.md (doc metadata +
  intro sentence only — its ASIL-D-derogation/decomposition table and
  math predate the correction and are explicitly flagged in-document as
  a follow-up needing dedicated safety-case re-derivation, not resolved
  here), TARA-ANALYSIS.md, and SAFETY_PLAN.md (also part of Finding C's
  fix).
- Left narrowly-scoped ASIL-B claims alone (SG-002/004/005/006/008/010/
  011 stay ASIL-B, matching .fusa-hara.json). Flagged as ambiguous and
  deliberately NOT changed: README.md's per-header ASIL-B design-pattern
  labels (watchdog.hpp/redundancy.hpp table rows) and
  INCIDENT-RESPONSE.md's "ASIL-B requirement violation" severity
  example — none clearly assert the project's own target ASIL.
- Not touched: safety-case.json/md, sas.json/md, sci.json, fmea.*,
  tooling/tara.* — these are regenerated by `cpfusa safety-case`/etc. on
  every tagged release (release.yml) and will pick up the corrected
  ASIL automatically once regenerated; hand-editing them would just be
  overwritten.

Finding B (HIGH) — HARA.md/FORMAL_VERIFICATION.md said RxSequenceGuard
was unwired; it has been wired since Phase 4 batch C:
- Verified directly against include/rcp/mock.hpp: Server holds a
  seq_trackers_ member (std::array<e2e::RxSequenceGuard, ...>), and both
  dispatch_e2e() and dispatch_frame_e2e() call it via a shared
  seq_gate_admits() helper on every dispatched request/frame before CRC
  unwrap. Integration-tested in tests/test_mock.cpp, not just
  RxSequenceGuard's own standalone tests/test_e2e.cpp unit test.
  include/rcp/e2e.hpp's own "UPDATE (Phase 4/Phase 17 batch C...)" file
  header comment corroborates this as the authoritative account.
- Updated HARA.md's SG-004 row, full H-004 section, and its Residual
  Risks table row from "not wired"/"effectively Open" to the current
  "Mitigated (opt-in)" state (matching c-RCP's own disposition for its
  equivalent mechanism), while being explicit that this covers
  mock::Server's own reference dispatch — a caller driving a transport
  Server with fully custom dispatch logic instead of wiring
  mock::Server::dispatch_frame()/dispatch_frame_e2e() as the
  FrameHandler is not automatically covered (per udp.hpp/l2.hpp's own
  header notes on the correct integration path).
- Updated FORMAL_VERIFICATION.md's SP1/SP2 point 1 (the wiring-gap
  claim) to match. Left point 2 (the TLA+ spec modeling the
  pre-RFC-1982 algorithm) untouched — a separate, still-true, and
  still-open issue unrelated to this finding.
- Did not touch H-004's S2/E3/C2/ASIL-B classification or any
  coverage/testing claim beyond what mock.hpp/test_mock.cpp actually
  show.

Finding C (HIGH) — SAFETY_PLAN.md still documented the retired
Zone/Command/Controller model:
- Verified all five dead requirement ids (REQ-CTRL-025/026/027/004,
  REQ-ERR-011) are absent from .fusa-reqs.json, .fusa-reqs-pending.json,
  and include/.
- "Zone mismatch detection" repointed to REQ-MOCK-010 ("dispatch to an
  unmapped byte_bus_id reports invalid_parameter"), the real current
  endpoint-addressing-validation mechanism in mock::Server::dispatch,
  matching HARA.md's SG-002 "Addressed By" entry.
- "Deadline monitor" row's description reworded to drop "zone"/
  "Config.Deadline" wording (REQ-DL-002 id was already correct).
- "Payload copy-on-send", "Payload copy-on-publish", and "Context /
  deadline propagation" rows (also citing dead REQ-CTRL-026/027/004,
  which the audit finding's own text named but its concrete fix
  guidance didn't walk through): verified no current analog exists for
  any of the three in the stream/endpoint/register-map wire-protocol
  architecture (no in-process handler-invocation/pub-sub/context-
  cancellation model remains). Marked "No direct analog" per instructions,
  rather than inventing a false mapping.
- Also fixed the Safety Goals table in the same document (SG-001/003/007
  ASIL-B -> ASIL-C per .fusa-hara.json, part of Finding A; "Commands"/
  "CmdWatchdog"/"Dead zone" terminology retired in favor of the current
  request/watchdog/RC-Server wording, part of Finding C) since it sits
  directly above the mechanisms table and shares the identical root
  causes (stale terminology + stale ASIL).

Verification: `python3 -c "import json; json.load(open('.fusa.json'))"`
passes. `cpfusa check`: HARA005 gone (0 errors, 1 pre-existing unrelated
warning). `cpfusa lint`: 146 warnings both before and after (git stash
diff) — unchanged, confirming no new findings from these doc/JSON edits.
`cpfusa trace`: pre-existing dangling-test-tag/no-LLR-children warnings
only, all in tests/*.cpp already unrelated to and untouched by this
batch.

Follow-ups noted, not resolved by this pass:
- AUDIT_PACK.md §2's ASIL-D gap-analysis derogation table and its
  ASIL-B(D) = ASIL-A + ASIL-B decomposition math predate the ASIL-C
  correction and need dedicated safety-case re-derivation (flagged
  in-document).
- AUDIT_PACK.md §3's "ASIL-C upgrade path"/"80% ASIL-C target" MC/DC
  wording now reads oddly against a project that already targets
  ASIL-C; left untouched since the underlying coverage-gap facts are
  unaffected and touching MC/DC threshold language felt too far outside
  a documentation-consistency fix to do safely here.
- .fusa-hara.json itself has a latent, separate inconsistency (H-003
  and H-005 are rated ASIL-B as hazards, but their own safety goals
  SG-003/SG-007 are rated ASIL-C) discovered while reading the file for
  this batch; out of scope for these three findings, noted for a future
  pass.
- No new requirement ids were invented.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Matt <47545907+SoundMatt@users.noreply.github.com>
…talog entries

The prior commit on this branch correctly raised .fusa.json's declared
ASIL from B to C. That bump surfaced four `// fusa:test REQ-ID`
tags in tests/test_shmem.cpp, tests/test_regmap.cpp, and
tests/test_server.cpp whose ids had no corresponding entry in
.fusa-reqs.json:

  - REQ-SHMEM-009 (tests/test_shmem.cpp:9, TEST_CASE ~line 482):
    a Channel's underlying state is freed exactly once regardless of
    std::shared_ptr<Channel> release order.
  - REQ-SHMEM-010 (tests/test_shmem.cpp:10, TEST_CASE ~line 181):
    Channel::request() returns ErrClosed after close(), playing both
    the "send" and "recv" role of c-RCP's split shmem_side_send()/
    shmem_side_recv() API.
  - REQ-RMAP-081 (tests/test_regmap.cpp:37, TEST_CASE ~line 828):
    already staged in .fusa-reqs-pending.json as a spec-defect gap
    (TC18's own EP_RESP_ON_ERROR prose reference is never given a
    wire field by Table 31), but never migrated into the confirmed
    catalog despite carrying a real, tested defensive guarantee
    (ep_generic_cfg::render() invents no bit for it). Migrated here
    with status "partial", matching the REQ-RMAP-067 precedent for a
    confirmed-gap-but-tested entry, and removed from the pending file
    per its own migration rule (real impl+test tags now exist).
  - REQ-TIMED-012 (tests/test_server.cpp:45, TEST_CASE ~line 1143):
    also staged in .fusa-reqs-pending.json, marked "confirmed absent"
    -- but that analysis is stale. rcp::server::Endpoint::admit()/
    admit_with_ack()/admit_under_tscf_gate()/select_due() (server.hpp)
    already implement and test full TSCF presentation-time admission
    end-to-end (the pending entry's own grep for a
    dispatch-tscf/admit-gate equivalent evidently missed this code's
    actual naming). Migrated to the confirmed catalog as implemented
    and removed from pending.

Verification (all via the pinned /tmp/cpfusa/build/cpfusa, matching
the exact CI invocations in .github/workflows/ci.yml):

  - `cpfusa trace` (bare, the blocking "Traceability check" step):
    the four dangling //fusa:test references are gone -- confirmed
    zero remaining, and all four new ids resolve impl:[✓] test:[✓].
    `cpfusa check`, `cpfusa lint`, and `cpfusa trace --req-coverage
    100` are unchanged before/after (byte-identical output modulo
    timestamps for check/lint; trace --req-coverage 100 mirrors
    trace's own diff exactly).

IMPORTANT CAVEAT discovered during verification: `cpfusa trace`'s
exit code is still 1 after this fix, and this fix cannot make it 0.
Reading cpfusa's own source (src/trace/trace.cpp, src/cmd/cli.cpp)
shows the bare `trace` subcommand's only exit(1) path is
`result.hlr_gate_failed`, which is driven entirely by a separate
HLR/LLR two-tier decomposition check ("WARN: HLR ... has no LLR
children"), promoted from warn-only to a hard gate whenever the
project's declared ASIL is C or D. Dangling //fusa:test references
are WARNING-level only and never gate the exit code, at any ASIL --
confirmed by grepping cli.cpp's trace callback and by reproducing
before/after: reverting only .fusa.json's asil to B (not committed,
diagnostic only) yields exit 0 with this fix in place, while ASIL-C
+ this fix still yields exit 1 with zero dangling refs. This
project's .fusa-reqs.json has never populated parent_id, so every
requirement is its own childless "HLR" (HLR-covered: 0/1137 both
before and after this commit) -- already flagged as a known,
unaddressed gap in ARCHITECTURE.md's schema-compat table ("the tool
also expects an HLR/LLR two-tier hierarchy this repo's schema
doesn't populate ... needs its own investigation"). Closing that gate
requires a much larger, separate effort (retrofitting parent_id
across the whole requirements catalog, or an explicit tooling
decision) and is out of scope for this additive four-entry backfill.
PR #178's CI will still fail the "Traceability check" step after
this commit for that unrelated, pre-existing reason.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Matt <47545907+SoundMatt@users.noreply.github.com>
@SoundMatt
SoundMatt merged commit be612ff into main Aug 23, 2026
24 of 25 checks passed
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