Bottom of a three-MR stack for v0.5 — contract freeze (#3, then #2, then #7).
read_frame returns wire::Frame — Signal, Skipped or Eof — instead of Option<Signal>. A well-framed body that cannot be decoded is consumed in full and reported as Skipped; Err is reserved for a stream that can no longer be framed.
terminal-garden and the hardware ingest loop continue past skipped frames.
Ordered first deliberately: #2 renumbers discriminants and bumps the schema to v4, which is exactly the situation that makes an older renderer meet records it cannot decode. The skip behaviour has to exist before that lands.
Closes #3