All checks were successful
Run Check Script / check (pull_request) Successful in 2m8s
`AgentStatus.device_id` and `AgentStatus.timestamp` were stringly
typed. Both now carry real types that prevent a whole class of
wire-format typos while keeping the on-wire JSON shape intact.
**device_id: String → harmony_types:🆔:Id**
Agent config + heartbeat payload now share the same `Id` that the
example IoT pipeline already uses for `IotDeviceSetupConfig`. Mixing
a device id with a deployment name or arbitrary `String` is now a
type error. `Id` is re-exported from `iot-contracts` so consumers
don't need a direct `harmony_types` dependency just to name the
field.
To keep the wire format byte-compatible, `harmony_types::Id` gains
`#[serde(transparent)]`. Audit: no consumer in the tree relies on
the previous `{"value": "…"}` shape — `Id` is persisted by sqlite
via `to_string()`, never serialized directly — so this is a
latent-bug fix more than a behavior change.
**timestamp: String → chrono::DateTime<Utc>**
The agent was calling `chrono::Utc::now().to_rfc3339()` and stuffing
the String into the payload. It now holds a real `DateTime<Utc>`
which serde-serializes as RFC 3339 anyway. The smoke script's
reboot-gate lex comparison still works: time-digit prefixes resolve
before the trailing `Z` (chrono default) vs `+00:00` (prior format)
difference matters.
**Plumbing**
- `iot/iot-agent-v0/src/config.rs`: `AgentSection.device_id: Id`.
TOML deserializes the bare string thanks to `#[serde(transparent)]`.
- `iot/iot-agent-v0/src/main.rs`: `watch_desired_state` and
`report_status` take `Id` instead of `String`.
- `iot/iot-contracts/Cargo.toml`: adds `harmony_types` path dep and
`chrono = { workspace, features = ["serde"] }`.
**Verification**
- `cargo test -p iot-contracts`: 8/8 passes. New assertions pin the
wire format: `"device_id":"pi-01"` (not `{"value":"pi-01"}`) and
`"timestamp":"2026-04-21T18:15:42Z"` (RFC 3339).
- x86_64 smoke-a3.sh PASSes end-to-end including the reboot-
reconnect loop — wire format remains compatible with the existing
smoke-script parsing.