Contract Model
Core Model
BREAD contracts are device-centric and capability-oriented:
- one canonical contract header per device type (
dcmt_ops.h,rlht_ops.h), - one shared capability query contract (
bread_caps.h), - generation-free public include surface.
Public API Boundary
Canonical includes:
<bread/bread_ops.h><bread/dcmt_ops.h><bread/rlht_ops.h><bread/bread_caps.h>
Generation-scoped public headers are intentionally excluded.
Payload Layouts
Each device header states its type, opcodes and payload layouts once, with
CRUMBS’ crumbs_ops.h (CRUMBS 0.14.0 or newer):
CRUMBS_DEFINE_FAMILY(RLHT, 0x01, RLHT_OPS)declaresRLHT_TYPE_IDand theRLHT_OP_*constants; the build fails on a duplicate opcode.BREAD_CHECK_FAMILY_OPS(RLHT, RLHT_OPS)follows it and fails the build if an opcode equals a bread-common one (BREAD_COMMON_OPSinbread_common_ops.h: the fault ops 0x7A-0x7B, the watchdog ops 0x7C-0x7E andBREAD_OP_GET_CAPS0x7F), which the CRUMBS check does not cover.CRUMBS_DEFINE_PAYLOAD(rlht_set_setpoints, 4, RLHT_SET_SETPOINTS_FIELDS)declaresrlht_set_setpoints_t,rlht_set_setpoints_wire_size,rlht_set_setpoints_pack()andrlht_set_setpoints_unpack(); the build fails if the field list does not sum to the stated size.
The controller wrappers (rlht_send_*, dcmt_send_*) pack through these
structs, and the state parsers unpack through rlht_state / dcmt_state.
Slice firmware is meant to unpack each SET payload with the matching
*_unpack() and build its GET_STATE reply with rlht_state_pack() /
dcmt_state_pack(), so both sides read one statement of every layout.
Until the Slice repositories adopt them (feastorg/Slice_RLHT#11,
feastorg/Slice_DCMT#28), the firmware still reads hand-written offsets.
Capability Principle
Behavior differences are represented as additive capabilities, not generation forks. Controllers must gate optional behavior by runtime capability flags.
Generation Labels
Hardware generation labels (Gen1/Gen2/Gen3) are inventory metadata only. They are not protocol identity and must not drive controller command branching.
When to Split a Contract Line
A wire break within a type bumps that type’s module major
(protocol-versioning.md), and controllers refuse a device whose major
differs. A new contract line (new type ID) is the alternative where a
split is unavoidable, and only for true wire breaks, such as:
- incompatible opcode semantics,
- reinterpretation of existing payload bytes,
- non-additive behavior that cannot be safely gated by capabilities.
Until then, evolve the same type line additively.