Clarify that the MULTIPART ACK chain terminates with a plain
PAYLOAD_TYPE_ACK (not a MULTIPART with remaining=0). For K extra
copies, the wire sequence is:
MULTIPART(remaining=K), MULTIPART(remaining=K-1), ...,
MULTIPART(remaining=1), then PAYLOAD_TYPE_ACK (plain).
All copies share the same ack_crc. Receivers MUST dedup at the
application layer by ack_crc, since each packet's hash differs.
Validated against upstream meshcore-dev/MeshCore @ 1a7b361
(Mesh::routeDirectRecvAcks, src/Mesh.cpp:362 — while-loop emits
MULTIPART(remaining=extra...1), then createAck(crc) plain ACK).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a "Multipart ACK Usage Constraints" subsection making explicit
what upstream enforces in code but the spec previously left implicit:
- MULTIPART-wrapped ACKs MUST only be emitted on direct-routed return
paths. When no direct path is known, the sender MUST fall back to a
plain PAYLOAD_TYPE_ACK via flood. Matches sendAckTo() and
routeDirectRecvAcks() in upstream.
- All MULTIPART and plain copies of the same ACK carry an identical
4-byte ack_crc; receivers MUST dedup by CRC at the application
layer (Section 16 packet-hash dedup does not catch them because the
wrapper byte makes each copy hash-distinct).
- Typical inter-copy delay ~300 ms plus the direct retransmit delay;
exact count via getExtraAckTransmitCount() (reference default 0).
Validated against upstream meshcore-dev/MeshCore @ 1a7b361
(Mesh::routeDirectRecvAcks, BaseChatMesh::sendAckTo).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>