Repository navigation
[cpp-RCP v3.0.0] Full ground-up rewrite ported from c-RCP #129
Description
Why
A 2026-08-21 parity audit against c-RCP (the project's reference implementation) found cpp-RCP behind on nearly every axis — no MC/DC, no requirement-atomicity audit, no fixed-capacity architecture, a replay-detection mechanism implemented but never wired into dispatch, a 26-commit tag/main drift — tracked in #122-127 (master #128).
Then a bigger problem surfaced: cpp-RCP was never rebaselined to TC18 v0.5.1_RC5 at all. README.md/ROADMAP.md cite v0.5.1_RC, the exact pre-RC5 baseline c-RCP itself started from before spending a real multi-issue program (c-RCP #96, #339, #341, #434) reconciling to RC5. Confirmed concretely, not just by citation: c-RCP's RC5 rebaseline found spi_nr_cs narrowed to a 4-bit (count-1) field plus a new deassert_cs_pause bit (RC5 ticket NXP_100) — cpp-RCP's spi.hpp has neither. That's a real wire-format defect, not a stale comment.
Given the scale of what's wrong at once — spec version, requirement catalog, safety evidence, and architecture — the decision is a full ground-up rewrite rather than another patch effort: clear cpp-RCP's implementation and rebuild it from scratch, using c-RCP's current (RC5-conformant, atomicity-audited, ASIL-D-hardened) implementation as the content source of truth, re-expressed in idiomatic modern C++.
This is cpp-RCP's own v3.0.0 — its ROADMAP.md already names "Phase 16, v3.0.0: TC18 RCP — General Availability" as the destination. This rewrite fulfills that directly.
Where the work happens
Branch rewrite/v3-from-c-rcp, not main — so main stays green/deployable for however many sessions this takes, and existing tags remain a rollback point. Each module is rebuilt in its own PR against this branch (delete-old + add-new in the same PR, so the branch stays buildable and CI-green throughout rather than broken for the whole multi-session effort). Final cutover merges this branch into main and tags v3.0.0.
Full plan: see the session's plan file (module-by-module mapping, phase sequencing, verification discipline) — summarized in the checklist below.
Supersedes
Closing, with a pointer here: #122 (release integrity), #123 (CI reliability), #124 (requirement schema/atomicity), #125 (ASIL-D SEOOC), #126 (allocation architecture), #127 (CAN XL fragmentation decision), #128 (parity master tracker). All of that work is subsumed by porting c-RCP's already-hardened equivalents directly, rather than incrementally patching the code being replaced.
Not superseded, still real, independent bugs: #58 (cs-bit polarity — will simply not exist in the ported code, verify during the port), #23 (go-RCP tooling-package parity — orthogonal, revisit post-rewrite).
Phases (see plan file for full detail)
- Phase 0 — branch setup, issue cleanup (this tracker),
ROADMAP.mdv3 section - Phase 1 — core wire/protocol:
acf,avtp,request(+sequencer+scheduler folded),fragment(new),respqueue(new),loan - Phase 2 — safety layer:
e2e,lifecycle,watchdog, allocation seam (DI-based, fixed-capacity, fault-injection tests) - Phase 3 — per-endpoint modules (batched):
can,lin,iseled,adc,pwm,gpio,spi,uart,mdio,wakeup - Phase 4 — server/dispatch:
regmap,mock(wire in classifier/Table-30/E2E-guard/fragmentation),server/endpoint.hpp,discovery,adapt - Phase 5 — transport:
udp,l2,shmem,admin; re-verifytls/capi - Phase 6 — requirement catalog: re-derive
.fusa-reqs.jsonfrom c-RCP's 1282 entries - Phase 7 — formal verification + MC/DC + release-pipeline hardening
- Phase 8 — remaining modules + re-integrate
dyndata/redundancy/sim - Phase 9 — cutover: full verification, merge to
main, tagv3.0.0, update RELAY'sdocs/RCP-ARCHITECTURE.md
Scale, honestly
Multi-session — comparable to building c-RCP's current maturity from scratch, since it includes standing up infrastructure c-RCP itself took many dedicated efforts to build (fragmentation, respqueue, MC/DC, fixed-capacity architecture, formal-verification CI) and more than tripling cpp-RCP's test suite (730 → ~2350+ test cases). Progress tracked here, one comment per phase batch, same as c-RCP's own tracker-issue pattern.
Activity
docs(roadmap): v3.0.0 becomes a full rewrite ported from c-RCP, not P…
ci: run CI/DCO on the rewrite/v3-from-c-rcp branch too
rewrite(phase1): port acf.hpp/avtp.hpp from c-RCP's RC5-conformant re…
SoundMatt commentedon Aug 21, 2026
Phase 1, batch 1 complete:
acf/avtpported from c-RCP (#130, merged1edc870). Found and fixed a real regression along the way — ACF_GBB'smessage_timestamphad been spliced into the wrong position since v2.22.0, meaning every ACF_GBB frame this library emitted was unreadable by a conformant peer. Full CI green (all 21 checks incl. cpfusa check/lint/cyber/trace/verify/qualify/vuln/report, cross-platform build matrix, ASan/UBSan, clang-tidy, coverage). Also had to widenci.yml/dco.yml's branch triggers to include this branch — they were main-only, so PRs against here got zero CI signal until now.Starting batch 2:
request(+sequencer+scheduler) andfragment+respqueue+loan, as two parallel PRs since they don't depend on each other at the C level.rewrite(phase1): add fragment/respqueue, convert loan to fixed-capaci…
rewrite(phase1): add fragment/respqueue, convert loan to fixed-capaci…
SoundMatt commentedon Aug 21, 2026
Phase 1, batch 2 complete:
fragment/respqueue(NEW) +loan(fixed-capacity conversion) ported from c-RCP (#131, merged79261c4).fragment.hppcloses cpp-RCP's largest functional gap (multi-AVTPDU CAN XL fragmentation, previously an explicit no-go at ROADMAP.md milestone 52) with a genuinely stronger design than c-RCP's own — fully fixed-capacity, zero heap allocation, vs. c-RCP'srealloc()-grown buffer.loan.hpp's buffer pool was found unbounded (a real gap an earlier parity audit flagged) and converted to match c-RCP's fixed bound exactly. Full CI green (21 checks), independently re-verified from a fresh build (221/221 targets, 55/55 ctest suites).request/sequencer/scheduler (the remaining Phase 1 module) is still in progress — hit a mid-task API error partway through, resumed from the partial state (production code was already complete, test port was what remained).rewrite(phase1): port request/sequencer/scheduler from c-RCP's RC5-co…
rewrite(phase1): port request/sequencer/scheduler from c-RCP's RC5-co…
SoundMatt commentedon Aug 21, 2026
Phase 1 complete. All three batches merged:
acf/avtp(found + fixed a real ACF_GBB layout regression from v2.22.0)fragment/respqueue(new) +loan(fixed-capacity conversion)request/sequencer/scheduler (found + fixed issue cs-bit polarity inverted in compound_wait_check_of() and should_execute_chained() #58's cs-bit polarity bug, a fabricatedcompound_wait_check_of()concept with no basis in c-RCP, a collapsed cancel-outcome distinction, and a broken sequencer advance model)Final state on
rewrite/v3-from-c-rcp, independently re-verified from a fresh full-tree build: 221/221 build targets, 55/55 ctest suites, 100% pass. Every wire-protocol-core module is now RC5-conformant, fixed-capacity where c-RCP itself is (and stronger infragment/loan, which have zero heap allocation vs. c-RCP's own C version), and test-covered well beyond where it started (acf24→63,avtp15→34,request12→135,fragment/respqueue0→30/27 new,loan6→9).Starting Phase 2: safety layer (
e2e,lifecycle,watchdog, allocation seam).rewrite(phase2): port watchdog + build allocation fault-injection sea…
rewrite(phase2): port watchdog + build allocation fault-injection sea…
SoundMatt commentedon Aug 21, 2026
Phase 2, batch 1 complete:
watchdog(content audit, fixed-capacity conversion added) +alloc.hpp(NEW — allocation fault-injection seam, deliberately DI-based rather than a global hook table) ported/built from c-RCP (#133, merged2d976af). Full CI green (21 checks), independently re-verified from a fresh build (223/223 targets, 56/56 ctest suites).e2e/lifecycle(the larger, safety-critical piece of Phase 2 — including the correction toHARA.md's overstated H-004 replay-detection mitigation claim) is still in progress.51 remaining items
rewrite(phase6): batch 10 — SPI/UART/WAKEUP catalog re-derivation (#163)
rewrite(phase6): batch 11 — REQ-REGMAP-*→REQ-RMAP-* rename + RMAP/SRV…
rewrite(phase6): batch 12 — DISC/RELAY catalog re-derivation (#165)
rewrite(phase6): batch 13 — MOCK/PWRMODE catalog re-derivation (Phase…
SoundMatt commentedon Aug 22, 2026
Phase 6 complete: requirement catalog re-derivation (13/13 batches merged)
Phase 6 — re-deriving cpp-RCP's
.fusa-reqs.jsonfrom c-RCP's current RC5-conformant catalog — is done. All 13 batches merged intorewrite/v3-from-c-rcp, each independently re-verified before merge per this rewrite's standing discipline (semantic id-keyed diff, sec-tested count check, direct spot-checks against source, fresh from-scratch rebuild+test, and — from batch 9 onward — a whole-tree tag-coverage re-derivation against the real pinnedcpfusabinary itself, not an approximation of its rules).Final numbers
.fusa-reqs.json: 412 → 1133 entries (+721 across the whole phase).fusa-reqs-pending.json: 3 → 174 entries (every genuine gap or id-collision found along the way, honestly documented withgaptext rather than force-fit into the main catalog)cpfusa v0.18.0binary against the fully merged branch, not a proxy check.REQ-TLS-*/REQ-AUTH-*/REQ-E2E-*/REQ-RL-*/REQ-DISC-*/REQ-RMAP-*+ 8 Table-30-evt-classification ids) never grew or shrank; no batch added a new cybersecurity-marked entry.Batch list
Recurring patterns worth recording for future reference
REQ-*ids (via//fusa:req///fusa:test/Catch2 bracket tags) than existed in the catalog — an earlier, untracked porting pass had already ported the behavior and left provenance comments; the batch's job was walking those citations into honest catalog entries, not re-deriving requirements independently..fusa-reqs-pending.jsonwith both the stale and real content documented, for a future dedicated renumbering-cleanup batch — 174 pending entries now carry this history, a real backlog item, not phase debt to write off.cpfusa trace's real gate requirestestedRequirements == totalRequirementsexactly, and — discovered again the hard way in batch 11 — it only recognizes literal// fusa:test REQ-IDmanifest comments, not Catch2[REQ-ID]bracket tags. Every batch from 9 onward mandated grepping the whole tree for both tag forms before adding an id, and building/running the real pinnedcpfusabinary directly rather than an approximating script. Batch 11's fixup commit is the concrete example of what happens when that discipline slips.power.c+powerstate.c(folded into cpp's singlepowerstate.hppper an earlier deliberate design choice). It surfaced one real safety-relevant finding: REQ-PWRMODE-026, mode committing before the sleep/standby response is confirmed transmitted — filed to pending, not force-fixed, since the right fix touches ordering guarantees this batch's scope didn't cover.Next
Proceeding to Phase 7 (formal verification + MC/DC + release-pipeline hardening) per the original rewrite plan's sequencing.
rewrite(phase7): batch 1 — port LifecycleStateMachine/E2ESafePoint TL…
rewrite(phase7): batch 2 — add real MC/DC coverage ratchet gate (LLVM) (
rewrite(phase7): batch 3 — release-pipeline hardening, dispositions, …
SoundMatt commentedon Aug 22, 2026
Phase 7 complete: formal verification + MC/DC + release-pipeline hardening (3/3 batches merged)
Phase 7 — the remaining safety-evidence infrastructure from the original rewrite plan — is done. All 3 batches merged into
rewrite/v3-from-c-rcp, each independently re-verified before merge (reproducing every measurement/claim directly rather than trusting the porting agent's own report — TLC re-run on all 5 specs, the full MC/DC instrumented pipeline reproduced end-to-end, the version-sources-agree/cpfusa cyber/analyze claims all reproduced with matching output).Batch list
LifecycleStateMachine.tla/E2ESafePoint.tlafrom c-RCP, add TLC CI automation for all 5 specs (the pre-existing 3 had never been run in CI at all)version-sources-agreeCI gate, realcpfusa analyze/cpfusa cybergating (both were previously softened — `What each batch actually found (not just what it built)
WatchdogSafeState,RxSequenceGuard,CrcSafeStateLatch) had never been wired into CI at all — a real, silent gap closed alongside the 2 new spec ports. Both new ports are faithful direct ports (confirmed field-for-field against cpp-RCP's reallifecycle.hpp/e2e.hpp), with one known abstraction gap (already present in c-RCP's own spec, not introduced here) documented rather than silently carried forward.cpfusa coverage --mcdc-fileis broken in the same way c-RCP's owncfusaequivalent is (its parser expects JSON keys realllvm-cov exportoutput never produces — confirmed directly against both the tool's source and real export output). The CI job readsllvm-cov's own totals directly instead, matching c-RCP's precedent. Floor set from a real local measurement (67.17%), not copied from c-RCP's own unrelated 93%..fusa.jsonhad no realproject.versionfield at all —cpfusahad been silently defaulting every generated artifact to"0.1.0"for this repo's whole history, fixed as part of building the version-sources-agree job.cpfusa cyber --strict's job name never matched itsrun:line (plaincpfusa cyber, no flag) — investigated rather than blindly adding the flag: cpp-RCP surfaces 838 real WARNING findings under--strict(mostly the expected byte-packing idiom of a wire-protocol library), correctly left un-added rather than mass-remediated or blanket-waived in one batch, with the job renamed to match its honest behavior.cpfusa analyze's|| truewas masking a real job-configuration bug, not a code defect: Catch2's own vendored FetchContent source was inside the scanned tree (cppcheck was misreporting Catch2's own test macros as first-party errors), andcompile_commands.jsonwas never actually being found by clang-tidy — both fixed, now a real 0-error hard gate..fusa-dispositions.jsonended up legitimately empty (verified against both tools' real current output, not fabricated to look populated).Final state
version-sources-agree,cpfusa analyze, and (honestly-named)cpfusa cyberare all real CI gates now, no softened escape hatches..fusa-dispositions.jsonandCHANGELOG.mdnow exist, both honestly reflecting the codebase's real current state rather than being stood up as empty formalities.Next
Proceeding to Phase 8 (remaining modules:
record,observe,config,cli,tsn,powerstate,faultinject,ratelimit,mdns,authz, live bridges,relay/relay.hpp; re-integratedyndata/redundancy/sim) per the original rewrite plan's sequencing — scoping now, since several of these already appear as passing CI jobs from earlier untracked porting passes (mirroring the Phase 6 pattern where most work turned out to be catalog/wiring, not net-new implementation).rewrite(phase8): batch B — content-parity audit (tsn/powerstate/fault…
rewrite(phase8): batch A — content-parity audit (record/observe/confi…
SoundMatt commentedon Aug 22, 2026
Phase 8 complete: remaining modules (2/2 batches merged)
Phase 8 — the original rewrite plan's final content phase — is done. Both batches merged into
rewrite/v3-from-c-rcp, each independently re-verified before merge.Batch list
record,observe,config,cli+ re-verifydyndata/redundancy/sim(no c-RCP equivalent)tsn,powerstate,faultinject,ratelimit,mdns,authz,relay/relay.hpp,restbridge/grpcbridgeWhat each batch found
Same pattern as Phases 6-7: every module already existed with substantial implementation and passing tests from an earlier untracked pass, so both batches were content-parity audits against c-RCP, not rewrites.
config.hpp: the manifest parser only routed an object to endpoint-entry parsing if bothstream_keyandbyte_bus_idkeys were present in an initial scan — an entry with only one was silently dropped instead of rejected (the same bug class c-RCP's ownconfig.cindependently found and fixed). Fixed by routing on either key, with the entry parser itself now the one place validating both are genuinely present, throwing a real parse error if not. Also ported genuine capability gaps intoobserve.hpp(a standaloneobserve::record()free function, per-endpoint attribution on spans/counters) andcli.hpp(advertising 5 real capabilities — time_sync/enhanced_cancel/trigger/chained/compound_bundles — that were implemented but never surfaced incapabilities_json(), plus a missingtsntransport entry).dyndata/redundancy/simconfirmed clean against the now-much-more-mature Phase 5-7 core, no changes needed.authz.hpp's policy-entry keying is coarser than c-RCP's (can't distinguish read vs write within one request kind) — a pre-existing v2.11.0 design choice, already justified at length in the header, correctly left as a documented divergence rather than force-changed (it would break the already-testedPolicyEntry/permit()contract). Everything else (tsn,powerstate,faultinject,ratelimit,mdns,relay.hpp, both live bridges) confirmed already capability-equivalent to c-RCP, several with their own already-documented architectural divergences (e.g.tsn.hpp's primitiveapply_priority()vs c-RCP's full transport-wrapper). Notablyfaultinject.hpp'spick_rule()was found to already fix a real use-after-free/data-race c-RCP's own mechanism doesn't guard against — arguably ahead of the reference implementation there.Both batches correctly declined to re-litigate closed Phase 6 catalog decisions (in particular
powerstate.hpp's 8 already-filedREQ-PWRMODE-*architectural gaps) and flagged (without fixing) a handful of catalog-only staleness items for a future targeted pass:REQ-RED-001/002/003's titles still say the retiredRedundantControllerclass name thoughtext/code/tests correctly sayRedundantRequestFn; several prefixes (REQ-REC-*,REQ-OBS-*,REQ-CFG-*,REQ-CLI-*) have ids that tag different content across languages or have no cpp-RCP counterpart at all, by design.Full test suite: 58/58 passing on the merged branch, 0 build errors/warnings.
Next
This closes out every phase of the original rewrite plan except final cutover: full-repo verification, merge
rewrite/v3-from-c-rcpintomain, tagv3.0.0, update RELAY'sdocs/RCP-ARCHITECTURE.md. Given the scale and finality of that step (replacingmain's content and cutting a major version tag), I'm pausing here for an explicit go-ahead before proceeding, rather than treating it as another routine batch-merge.release: bump version to 3.0.0
SoundMatt commentedon Aug 22, 2026
v3.0.0 shipped 🎉 — the full ground-up rewrite is complete
Tag
v3.0.0is pushed,mainis updated, andrelease.ymlran for real for the first time — regenerated safety artifacts (SBOM, provenance, dFMEA, safety case, all four gap reports including the newly-added IEC 62443 one, compliance report) merged via #173, all correctly declaring version 3.0.0 throughout (confirming the.fusa.jsonversion-default bug found and fixed in Phase 7 batch 3 is genuinely resolved — the pre-fix artifacts had been silently defaulting to"0.1.0").Final cutover steps
rewrite/v3-from-c-rcpbranch intomain(89 files, ~54k insertions), after independent full-repo verification: fresh build (0 errors/warnings), 58/58 tests,cpfusa check/traceclean (1133/1133 traced+tested, 0 gaps), all 5 TLA+ specs verify, and the branch's own CI fully green including the MC/DC gate.CMakeLists.txt/version.hpp/.fusa.jsonstill said2.26.0after the merge; bumped all three to3.0.0together (verified they agree, matching theversion-sources-agreegate's own check) and promotedCHANGELOG.md's[Unreleased]to a real[3.0.0]entry.v3.0.0— pushed oncemain's CI confirmed green on the version-bump commit.release.yml's first-ever real run — succeeded, regenerated every safety artifact for the real 3.0.0 state, opened chore: regenerate safety artifacts for v3.0.0 #173 automatically, merged after spot-checking the version fields.docs/RCP-ARCHITECTURE.mdpreviously tracked three cpp-RCP conformance gaps as open (Table 30 Row-2 evt[2:0] wiring across all 8 endpoint types, the §13.5.1 compound-wait comparison primitive, and specificrequest.hppfield-level gaps). All three verified directly against the current codebase and corrected in place — genuinely closed by this rewrite, not assumed from its existence. A smaller, different set of real gaps remains and is cited honestly from cpp-RCP's own current.fusa-reqs-pending.json.What this rewrite covered, start to finish
Per the original plan (
docs/wiggly-shimmying-wombat.md), phases 1-5 landed in earlier sessions (core wire/protocol, safety layer, per-endpoint modules, server/dispatch, transport, plus admin/shmem hardening). This session covered:.fusa-reqs.json412 → 1133 entries, 174 genuine gaps/id-collisions honestly documented rather than force-fit, zero tag-coverage gaps.version-sources-agree, two previously-softened safety gates turned real,.fusa-dispositions.json,CHANGELOG.md).config.hpp's manifest parser.Every batch this session was independently re-verified before merge — reproducing measurements directly rather than trusting a porting agent's own report, spot-checking specific claims against real source, fresh from-scratch rebuilds, and (from batch 9 onward) whole-tree tag-coverage re-derivation against the real pinned
cpfusabinary.cpp-RCP is now, per its own ROADMAP.md Phase 17 goal, the OPEN Alliance TC18 Remote Control Protocol.