Skip to content

Commit 7facb88

Browse files
authored
fix: v2.22.0 — ACF_GBB message_timestamp is spliced between the header quadlets (#100)
encode_acf_gbb/decode_acf_gbb placed the 64-bit message_timestamp after the complete 8-byte shared Message Info header (wire octet 8). TC18 splices it between that header's two 4-byte quadlets (wire octet 4), which pushes evt/rsv/hs/cs/transaction_num/op/rsp/err/ms/read_size_or_segment_num from octet 8 to octet 12. Total message length was unaffected, and this codec round-tripped against itself perfectly, which is why neither the v2.19.0 bit-packing pass nor the v2.20.0 acf_msg_length pass caught it — but every ACF_GBB message this library emitted was unreadable by a conformant peer, and every ACF_GBB E2E CRC was computed over the wrong byte sequence. ACF_ABB is unaffected: it has no timestamp field. Verified against the specification PDF's own figures (rendered at high DPI and read directly, not inferred from this codec) four independent ways: the single-ACF_GBB CRC-coverage figure's three-row "Byte Message Info" group (quadlet 0, 64-bit message_time_stamp, quadlet 1); that figure's own stated acf_msg_length 0x07 = 28 octets, which only closes as 4 + 8 + 4 + 8 + 4 (its ACF_ABB counterpart states 0x05 = 20 = 4 + 4 + 8 + 4); the compound-request figure, where the mtv=0 repurposing fields occupy octets 4..11 between the same two quadlets; and the response-field table, which lists message_timestamp between byte_bus_id and evt. - acf.hpp: new kAcfHeaderQuadletLen/kAcfGbbTimestampOffset/ kAcfGbbSecondQuadletOffset/kAcfGbbMessageInfoLen and encode_acf_gbb_message_info/decode_acf_gbb_message_info, which own the spliced byte geometry and delegate all bit packing to the existing encode/decode_acf_message_info. encode_acf_gbb, decode_acf_gbb and decode_acf_messages route through them. - e2e.hpp: coverage_buffer hand-serialized the same wrong order; it now calls acf::encode_acf_gbb_message_info, so the CRC-covered bytes equal the transmitted bytes by construction. - udp.hpp/l2.hpp/record.hpp needed no change (all route through the codec, no hardcoded offsets). request.hpp needed none either (it deals in the slot's value, not its position) and gained only a comment. - tests: test_acf.cpp and test_e2e.cpp had pinned the bug as correct by reading back the encoder's own output. Rewritten around literal spec-derived octet positions with the derivation spelled out, plus a new full-message conformance vector reproducing the CRC-coverage figure's message octet for octet and asserted in both directions. Mutation-checked: restoring the old offsets fails 4 test cases. - .fusa-reqs.json: REQ-WIRE-005/REQ-E2E-002 now state exact octet positions (REQ-E2E-002 also still said "10-byte ACF shared header", stale since v2.19.0 made it 8); REQ-SEQ-001 records where the repurposed slot sits. Wire-format-breaking for ACF_GBB only; no public API signature changes. kVersion/CMakeLists.txt 2.21.0 -> 2.22.0. Build clean, ctest 53/53. Signed-off-by: Matt <47545907+SoundMatt@users.noreply.github.com>
1 parent d50e51d commit 7facb88

10 files changed

Lines changed: 530 additions & 80 deletions

File tree

‎.fusa-reqs.json‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@
267267
{
268268
"id": "REQ-E2E-002",
269269
"title": "CRC coverage buffer layout",
270-
"text": "rcp::e2e::coverage_buffer shall assemble, in order, the 8-byte big-endian stream_id, the 4-byte big-endian avtp_timestamp (zero-filled when std::nullopt is passed, the NTSCF stand-in), the 10-byte ACF shared header, and the payload; rcp::e2e::compute_crc shall compute the CRC over exactly that buffer.",
270+
"text": "rcp::e2e::coverage_buffer shall assemble, in order, the 8-byte big-endian stream_id, the 4-byte big-endian avtp_timestamp (zero-filled when std::nullopt is passed, the NTSCF stand-in), the ACF Message Info block in its exact on-the-wire byte order, and the payload; rcp::e2e::compute_crc shall compute the CRC over exactly that buffer. The Message Info block is the contiguous 8-byte shared header for ACF_ABB, and for ACF_GBB is the 16-byte form in which the 64-bit big-endian message_timestamp is spliced between the shared header's two quadlets (block octets 0-3 = quadlet 0, octets 4-11 = message_timestamp, octets 12-15 = quadlet 1), so that the CRC covers byte-for-byte the octets rcp::acf::encode_acf_gbb transmits.",
271271
"standard": "iso26262",
272272
"level": "ASIL-B",
273273
"asil": "ASIL-B"
@@ -1923,7 +1923,7 @@
19231923
{
19241924
"id": "REQ-WIRE-005",
19251925
"title": "ACF_GBB carries a 64-bit message_timestamp",
1926-
"text": "encode_acf_gbb/decode_acf_gbb shall round-trip the shared ACF header, a 64-bit message_timestamp value, and the payload unchanged for an ACF_GBB (0x0D) message.",
1926+
"text": "encode_acf_gbb/decode_acf_gbb shall round-trip the shared ACF header, a 64-bit message_timestamp value, and the payload unchanged for an ACF_GBB (0x0D) message, and shall place them on the wire in the order octets 0-3 = shared-header quadlet 0 (acf_msg_type/acf_msg_length/pad/mtv/rsv/byte_bus_id), octets 4-11 = message_timestamp big-endian, octets 12-15 = shared-header quadlet 1 (evt/rsv/hs/cs/transaction_num/op/rsp/err/ms/read_size_or_segment_num), octets 16 onwards = byte_msg_payload.",
19271927
"standard": "iso26262",
19281928
"level": "ASIL-B",
19291929
"asil": "ASIL-B"
@@ -2002,8 +2002,8 @@
20022002
},
20032003
{
20042004
"id": "REQ-WIRE-015",
2005-
"title": "RELAY spec §15.5 canonical ControlFlags/Message conversion",
2006-
"text": "rcp::acf::ControlFlags shall define FlagAck/FlagRead/FlagWrite/FlagResponse/FlagError/FlagMoreSegments at bit values 0x80/0x40/0x20/0x10/0x08/0x04 respectively, matching RELAY spec §15.5's Go definition exactly; rcp::acf::to_message/from_message shall convert between AcfMessageInfo and the RELAY spec §15.5 canonical Message shape, preserving byte_bus_id, transaction_num, read_size_or_segment, and body, and carrying message_timestamp only for the ACF_GBB encoding.",
2005+
"title": "RELAY spec \u00a715.5 canonical ControlFlags/Message conversion",
2006+
"text": "rcp::acf::ControlFlags shall define FlagAck/FlagRead/FlagWrite/FlagResponse/FlagError/FlagMoreSegments at bit values 0x80/0x40/0x20/0x10/0x08/0x04 respectively, matching RELAY spec \u00a715.5's Go definition exactly; rcp::acf::to_message/from_message shall convert between AcfMessageInfo and the RELAY spec \u00a715.5 canonical Message shape, preserving byte_bus_id, transaction_num, read_size_or_segment, and body, and carrying message_timestamp only for the ACF_GBB encoding.",
20072007
"standard": "relay",
20082008
"level": "QM",
20092009
"asil": "QM"
@@ -2611,7 +2611,7 @@
26112611
{
26122612
"id": "REQ-SEQ-001",
26132613
"title": "message_timestamp-repurposing request_type decode/encode (mtv=0)",
2614-
"text": "rcp::request::decode_request_type shall return RequestErrc::timestamp_not_repurposed when mtv is true, shall return RequestErrc::unknown_request_type when the top byte of message_timestamp is not one of the 8 defined opcodes, and shall otherwise recover the opcode and the remaining 7 parameter bytes exactly as produced by encode_request_type for the same inputs; rcp::request::make_conditional_request shall always build an ACF_GBB AcfMessageInfo with mtv clear.",
2614+
"text": "rcp::request::decode_request_type shall return RequestErrc::timestamp_not_repurposed when mtv is true, shall return RequestErrc::unknown_request_type when the top byte of message_timestamp is not one of the 8 defined opcodes, and shall otherwise recover the opcode and the remaining 7 parameter bytes exactly as produced by encode_request_type for the same inputs; rcp::request::make_conditional_request shall always build an ACF_GBB AcfMessageInfo with mtv clear. The repurposed slot is the ACF_GBB message_timestamp slot at wire octets 4-11 (see REQ-WIRE-005), whose first octet carries the opcode.",
26152615
"standard": "iso26262",
26162616
"level": "ASIL-B",
26172617
"asil": "ASIL-B"
@@ -2987,7 +2987,7 @@
29872987
{
29882988
"id": "REQ-L2-007",
29892989
"title": "MultiFrame round-trips two ACF_ABB messages packed into one L2 frame",
2990-
"text": "rcp::l2::encode_l2_multi_frame/decode_l2_multi_frame shall round-trip two or more ACF_ABB/ACF_GBB messages packed into a single Ethernet frame's AVTPDU, per extraction §12.9.1.1's multiple-requests-in-one-frame requirement, the same as rcp::udp::MultiFrame's equivalent guarantee.",
2990+
"text": "rcp::l2::encode_l2_multi_frame/decode_l2_multi_frame shall round-trip two or more ACF_ABB/ACF_GBB messages packed into a single Ethernet frame's AVTPDU, per extraction \u00a712.9.1.1's multiple-requests-in-one-frame requirement, the same as rcp::udp::MultiFrame's equivalent guarantee.",
29912991
"standard": "iso26262",
29922992
"level": "ASIL-B",
29932993
"asil": "ASIL-B"

‎CMakeLists.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.21)
22

33
project(cpp-rcp
4-
VERSION 2.21.0
4+
VERSION 2.22.0
55
DESCRIPTION "Remote Control Protocol for automotive zonal architecture (C++)"
66
HOMEPAGE_URL "https://github.com/SoundMatt/cpp-RCP"
77
LANGUAGES CXX

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ reference.
2222
| Header | Description |
2323
|---|---|
2424
| `<rcp/avtp.hpp>` | TC18 wire codec, framing half — IEEE 1722 AVTPDU (NTSCF/TSCF) header framing, `StreamId`, `ByteBusId` |
25-
| `<rcp/acf.hpp>` | TC18 wire codec, message half — ACF_ABB/ACF_GBB message format, `AcfMessageInfo` |
25+
| `<rcp/acf.hpp>` | TC18 wire codec, message half — ACF_ABB/ACF_GBB message format, `AcfMessageInfo`. ACF_ABB Message Info is 8 contiguous octets; ACF_GBB is 16, with the 64-bit `message_timestamp` spliced between the header's two quadlets (octets 0–3 quadlet 0, 4–11 timestamp, 12–15 quadlet 1) — corrected in v2.22.0, see ROADMAP.md milestone 66 |
2626
| `<rcp/regmap.hpp>` | RC Server register-map model (generic + functional config, EP0) |
2727
| `<rcp/lifecycle.hpp>` | RC Server lifecycle state machine |
2828
| `<rcp/request.hpp>` | Conditional-request taxonomy (compound, triggered, timed, chained, ...) and sequencers |

‎ROADMAP.md‎

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ work, since every endpoint type's functional config block depends on it.
134134
| **Phase 16** | v2.18.0 | Certification refresh | HARA/TARA/FMEA/formal verification/audit pack regenerated against the new requirement set |
135135
| **Phase 16** | v2.19.0 | Wire format conformance fix pass | `avtp.hpp`/`acf.hpp` NTSCF/TSCF/ACF Message Info bit-packing re-derived from the specification's own header diagrams; `e2e.hpp` CRC coverage and numeric error-code mapping corrected to match |
136136
| **Phase 16** | v2.20.0 | TC18 conformance fix pass II | `acf_msg_length` actually computed; multiple-requests-in-one-frame (§12.9.1.1) supported; error responses carry a Table 27 error code; GPIO exact-length payload check; ISELED gains a real wire codec; PWM_IN rising/falling-edge triggers; `control_data_length` validated |
137+
| **Phase 16** | v2.21.0 | L2 (native Ethernet) transport + UDP Annex J conformance fix | New `l2.hpp` raw-Ethernet transport (EtherType 0x22F0); `udp.hpp` gains Annex J's 4-byte encapsulation sequence number and port 17221 default |
138+
| **Phase 16** | v2.22.0 | ACF_GBB `message_timestamp` wire-position fix | The 64-bit `message_timestamp` is spliced *between* the ACF Message Info header's two quadlets (octet 4), not appended after both (octet 8); `e2e.hpp` CRC coverage follows; wire-breaking for ACF_GBB only |
137139
| **Phase 16** | v3.0.0 | **TC18 RCP — General Availability** | First release where cpp-RCP *is* the OPEN Alliance TC18 Remote Control Protocol |
138140

139141
---
@@ -175,7 +177,9 @@ incrementally through v2.6.0 per the Phase 13 introduction above.
175177
(time-synchronous, client-only) AVTPDU headers (extraction §2.2)
176178
- Implement the two ACF message types RCP defines on top of IEEE 1722:
177179
ACF_ABB (`0x0E`, no timestamp field) and ACF_GBB (`0x0D`, 64-bit
178-
`message_timestamp` field) (extraction §2.3)
180+
`message_timestamp` field, spliced between the shared header's two
181+
quadlets at octets 4..11 — corrected at milestone 66/v2.22.0)
182+
(extraction §2.3)
179183
- Implement the shared `byte_message_info` header fields common to both
180184
message types: `acf_msg_type`, `acf_msg_length`, `pad`, `mtv`, `rsv`,
181185
`byte_bus_id`, `evt` (ack flag + 3-bit sub-opcode), `hs`, `cs`,
@@ -1567,11 +1571,12 @@ already predates milestones 44-61 (still documenting `rcp/firmware.hpp`
15671571
and other since-deleted packages) and bringing it in line with the current
15681572
architecture is a substantial, separately-scoped task this milestone's own
15691573
text does not name. Milestone #63 (Wire Format Conformance Fix Pass,
1570-
v2.19.0) is next, ahead of #66 (TC18 RCP General Availability, v3.0.0),
1574+
v2.19.0) is next, ahead of #67 (TC18 RCP General Availability, v3.0.0),
15711575
since it corrects a defect in the exact wire surface GA's own scope
15721576
requires to be correct. (Milestone #64, TC18 Conformance Fix Pass II,
1573-
v2.20.0, and milestone #65, L2 Transport + UDP Annex J Conformance Fix,
1574-
v2.21.0, were added later in the same vein — see their own entries.)
1577+
v2.20.0; milestone #65, L2 Transport + UDP Annex J Conformance Fix,
1578+
v2.21.0; and milestone #66, ACF_GBB `message_timestamp` Wire-Position Fix,
1579+
v2.22.0, were added later in the same vein — see their own entries.)
15751580

15761581
### Retired-model residue cleanup (2026-07-30, v2.19.0 — audit findings cpp-RCP-FS-01..05, cpp-RCP-05, cpp-RCP-07)
15771582

@@ -1744,9 +1749,11 @@ check/lint/trace and RELAY `conform --strict` were not run locally (the
17441749
cross-checked against a second, independent presentation of the same
17451750
header shapes elsewhere in the specification
17461751
- `rcp/e2e.hpp`'s `coverage_buffer`/`compute_crc`/`verify_crc` gain a
1747-
`message_timestamp` parameter and include it, at the correct byte offset,
1748-
in an ACF_GBB message's CRC coverage — previously omitted entirely,
1749-
understating what the CRC actually protects for every GBB message
1752+
`message_timestamp` parameter and include it in an ACF_GBB message's CRC
1753+
coverage — previously omitted entirely, understating what the CRC
1754+
actually protects for every GBB message. (This milestone put it at the
1755+
offset the codec used at the time, which milestone 66/v2.22.0 later
1756+
found was itself wrong — see that milestone.)
17501757
- `rcp/e2e.hpp`'s `E2eErrc::crc_error` gains a numeric-TC18-wire-error-code
17511758
accessor (`POCI_FAILURE` = 12) for use when building an error response's
17521759
`byte_msg_payload`, distinct from the enum's own internal
@@ -1768,8 +1775,11 @@ environment to interoperate-test against directly, so "full bit-for-bit
17681775
wire conformance" is not claimed as an absolute fact here either — this
17691776
milestone's own honest position is "high confidence, derivation-and-cross-
17701777
check verified, not independently interop-tested," which is why GA
1771-
(milestone #66) remains a distinct, later milestone rather than being
1772-
folded into this one.
1778+
(milestone #67) remains a distinct, later milestone rather than being
1779+
folded into this one. (Milestone 66/v2.22.0 vindicated that caution: this
1780+
milestone re-derived the shared header's *bit* packing correctly but never
1781+
questioned the ACF_GBB message's *byte* geometry around it, which was
1782+
wrong the whole time.)
17731783

17741784
Both `AcfMessageInfo`'s and the NTSCF/TSCF headers' public field names are
17751785
kept stable through this fix — only the bit positions/widths the codec
@@ -1977,7 +1987,116 @@ development (see this milestone's own pull request description for the
19771987
byte-for-byte evidence), ahead of the dedicated CI job doing the same on
19781988
every future change.
19791989

1980-
### 66. TC18 RCP — General Availability (v3.0.0)
1990+
### 66. ACF_GBB `message_timestamp` Wire-Position Fix (v2.22.0)
1991+
1992+
A single-field byte-geometry defect, found by an independent audit of this
1993+
repository, that made every ACF_GBB message this library has ever emitted
1994+
unreadable by a conformant peer — and every ACF_GBB E2E CRC wrong.
1995+
1996+
**The bug.** `encode_acf_gbb`/`decode_acf_gbb` placed the 64-bit
1997+
`message_timestamp` *after* the complete 8-byte shared Message Info header,
1998+
at wire octet 8. The specification splices it *between* that header's two
1999+
4-byte quadlets, at wire octet 4 — which pushes the entire second quadlet
2000+
(`evt`/`rsv`/`hs`/`cs`/`transaction_num`/`op`/`rsp`/`err`/`ms`/
2001+
`read_size_or_segment_num`) from octet 8 to octet 12, and the payload from
2002+
octet 16 to… also octet 16, which is exactly why nothing caught it: the
2003+
message length was right, the round trip through this codec's own encoder
2004+
and decoder was right, and only the four bytes in the middle were in the
2005+
wrong place. Milestones 63 (v2.19.0) and 64 (v2.20.0) both audited this
2006+
header and both missed it, because both were looking at *bit* packing
2007+
within the header and at `acf_msg_length`, not at where the header's halves
2008+
sit relative to the timestamp. ACF_ABB is entirely unaffected — it has no
2009+
timestamp field, so its two quadlets have always been correctly contiguous
2010+
at octets 0..7.
2011+
2012+
**How the correct layout was verified**, independently and before any code
2013+
changed, against the specification PDF itself (figures rendered at high DPI
2014+
and read directly, since these layouts exist only as figures, not as
2015+
extractable text) rather than against this codec's prior behavior:
2016+
2017+
1. The **single-ACF_GBB CRC-coverage figure** draws one "Byte Message Info"
2018+
group of three rows in this order: the
2019+
`acf_msg_type`/`acf_msg_length`/`pad`/`mtv`/`rsv`/`byte_bus_id` quadlet;
2020+
then `message_time_stamp` as a double-height 64-bit block (rendered the
2021+
same way that figure's own 64-bit `stream_id` is); then the
2022+
`evt`/`rsv`/`hs`/`cs`/`transaction_num`/`op`/`rsp`/`err`/`ms`/
2023+
`read_size` quadlet.
2024+
2. **Arithmetic cross-check from that same figure**, which states
2025+
`acf_msg_length` = 0x07 for its example: 7 quadlets = 28 octets =
2026+
quadlet0(4) + timestamp(8) + quadlet1(4) + payload(7 real + 1 pad = 8) +
2027+
CRC32(4). No other placement of a 64-bit timestamp closes that sum. Its
2028+
ACF_ABB counterpart states 0x05 = 20 octets = 4 + 4 + (6 real + 2 pad =
2029+
8) + 4, confirming the same two quadlets with no timestamp between them.
2030+
3. The **compound-request figure** (an ACF_GBB with `mtv=0`) shows the
2031+
fields that repurpose the timestamp slot — `request_type`/
2032+
`cmp_start_state`/`cmp_next_state`/`cmp_sequencer`, then
2033+
`cmp_exec_delay`/`cmp_repetitions` — occupying exactly the two quadlets
2034+
*between* the same two header quadlets, i.e. octets 4..11. That figure
2035+
also independently confirms `rcp/request.hpp`'s existing byte assignment
2036+
*within* the slot (opcode in the slot's first octet, seven parameter
2037+
octets after it), which needed no change.
2038+
4. The **response-field table** lists the Message Info fields in wire order
2039+
and places `message_timestamp` ("Present in ACF_GBB, omitted in
2040+
ACF_ABB") between `byte_bus_id` — quadlet 0's last field — and `evt` —
2041+
quadlet 1's first field.
2042+
2043+
**What changed.**
2044+
2045+
- `rcp/acf.hpp` gains `kAcfHeaderQuadletLen`/`kAcfGbbTimestampOffset`/
2046+
`kAcfGbbSecondQuadletOffset`/`kAcfGbbMessageInfoLen` and a pair of
2047+
ACF_GBB-specific Message Info codecs,
2048+
`encode_acf_gbb_message_info`/`decode_acf_gbb_message_info`, which own the
2049+
spliced byte geometry while delegating all bit-level packing to the
2050+
existing `encode_acf_message_info`/`decode_acf_message_info` — so the bit
2051+
layout keeps exactly one definition in the file.
2052+
`encode_acf_gbb`/`decode_acf_gbb` and the multi-message walker
2053+
`decode_acf_messages` all route through them.
2054+
- `rcp/e2e.hpp`'s `coverage_buffer` serialized the ACF_GBB Message Info
2055+
block by hand (header, then timestamp) and so inherited the same wrong
2056+
order; it now calls `acf::encode_acf_gbb_message_info`, making the
2057+
CRC-covered bytes byte-identical to the transmitted bytes *by
2058+
construction* rather than by two code paths agreeing. A new assertion in
2059+
`tests/test_e2e.cpp` pins that identity directly.
2060+
- `rcp/udp.hpp`, `rcp/l2.hpp` and `rcp/record.hpp` needed no change: all
2061+
three already route every ACF_GBB message through
2062+
`encode_acf_gbb`/`decode_acf_gbb` and hardcode no offsets of their own.
2063+
`rcp/request.hpp` likewise needed no change — it deals only in the
2064+
*value* of the 64-bit slot, never its wire position — and gained only a
2065+
comment recording that, plus the figure-3 confirmation above.
2066+
- `.fusa-reqs.json`: `REQ-WIRE-005` and `REQ-E2E-002` now state the exact
2067+
octet positions rather than leaving the geometry implicit (`REQ-E2E-002`
2068+
also still said "the 10-byte ACF shared header", stale since milestone 63
2069+
made it 8), and `REQ-SEQ-001` records where the repurposed slot actually
2070+
sits.
2071+
2072+
**Tests.** `tests/test_acf.cpp`'s and `tests/test_e2e.cpp`'s ACF_GBB
2073+
byte-sequence expectations had *pinned the bug as correct* — the classic
2074+
tautology failure of testing an encoder against its own output. They are
2075+
rewritten around literal octet positions derived from the figures above,
2076+
with the derivation written out in the test file itself, plus a new
2077+
full-message conformance vector reproducing the CRC-coverage figure's own
2078+
message (`acf_msg_type` 0x0D, `acf_msg_length` 0x07, `pad` 1, 7+1-octet
2079+
payload, CRC32 trailer, 28 octets total) octet for octet, asserted in both
2080+
directions (encode to literal, and decode *from* the literal back to
2081+
fields, so the decoder is proven against spec-shaped bytes rather than its
2082+
own encoder's bytes). The new expectations were mutation-checked: reverting
2083+
the two offset constants to their pre-fix values fails 4 test cases across
2084+
the two suites, confirming the tests actually discriminate between the two
2085+
layouts. Full local build and `ctest` (53/53 suites) pass.
2086+
2087+
**Breaking change.** This changes bytes on the wire for every ACF_GBB
2088+
message, so a v2.22.0 peer cannot interoperate with a pre-v2.22.0 peer for
2089+
GBB traffic (ACF_ABB traffic is unaffected). No public API signature,
2090+
struct field, or function name changes — `AcfMessageInfo` and the
2091+
`encode_acf_gbb`/`decode_acf_gbb` signatures are identical — so this is a
2092+
source-compatible, wire-incompatible release. Given the pre-v2.22.0 bytes
2093+
were never conformant with anything but themselves, correcting them is
2094+
strictly a move toward the interoperability GA requires; `kVersion`/
2095+
`CMakeLists.txt` bump 2.21.0 → 2.22.0 accordingly, following the same minor
2096+
-bump convention milestones 63/64/65 used for their own wire-format
2097+
corrections during this pre-GA v2.x line.
2098+
2099+
### 67. TC18 RCP — General Availability (v3.0.0)
19812100

19822101
- First release where cpp-RCP's `RCP` conforms to the OPEN Alliance TC18
19832102
Remote Control Protocol Specification at the wire level: an RC Client

0 commit comments

Comments
 (0)