CHANGELOG.md

# Changelog

All notable changes to this project are documented here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.0] - TBD

Initial public release. **Alpha** — public API and wire format may
change before `v1.0` based on real-deployment feedback.

### Added

- **Wire format v1** (`PhiAccrualUdp.Packet`) — 12-byte fixed format
  with magic `0xCEA6`, version `0x01`, reserved flags byte (must be
  zero in v1), and 64-bit unsigned millisecond timestamp.
- **UDP listener** (`PhiAccrualUdp.Listener`) — opens a UDP socket on
  a configurable port, decodes incoming packets, calls
  `PhiAccrual.observe/2` with local monotonic receipt time. Decode
  failures emit `[:phi_accrual_udp, :decode, :error]` telemetry with
  reason classification.
- **Periodic UDP sender** (`PhiAccrualUdp.Sender`) — sends heartbeat
  packets to a list of `{host, port}` targets at a configurable
  interval. Configurable timestamp source.
- **Custom node resolution** — listener accepts a `:node_resolver`
  function mapping `(ip, port)` to user-defined node identifiers.
  Default: `{ip, port}` tuple.
- **Telemetry schema** — `[:listener, :started]`, `[:sample, :received]`,
  `[:decode, :error]`, `[:sender, :started]`, `[:sender, :tick]`.

### Notes

- Wire format and telemetry schema are **not yet committed**. Both
  may change before `v1.0`. Magic/version/flags structure is
  deliberately chosen to permit format evolution without breaking
  on-the-wire compatibility for v1 senders.
- Receiver-driven clock discipline: the EWMA uses local monotonic
  receipt time, never the packet timestamp. This preserves
  `phi_accrual`'s contract that cross-node timestamps are
  meaningless.