Delivery

Harmovela Delivery Semantics

> Status: draft. Part of the Harmovela 0.2 core specification. > Category: core

Purpose

Define the event delivery contract between producer and consumer across all transports, including acknowledgement, sequence tracking, and replay.

Delivery Modes

ModeDeliveryDuplicatesReplayUse Case
best_effortAt most onceNoNoTransient UI updates, heartbeat
at_least_onceAlways deliveredPossibleNoTask results, memory facts
replayableAlways deliveredPossibleYes (cursor)Late-joining consumers, audit

Delivery mode is declared in the subscription filter as delivery_mode and carried on each event under delivery.mode.

Event Identity

Every event has a globally unique id. Consumers use the id for idempotent processing:

Acknowledgement Protocol

Harmovela-level Acknowledgement

When a consumer has successfully processed an event, it sends:

{
  "type": "event.acknowledged",
  "causation_id": "evt_01JZ0000000000000000000000",
  "payload": {
    "acknowledged_event_id": "evt_01JZ0000000000000000000000",
    "cursor": "stream_01:42"
  }
}

Rejection

When a consumer cannot process an event:

{
  "type": "event.rejected",
  "causation_id": "evt_01JZ0000000000000000000000",
  "payload": {
    "acknowledged_event_id": "evt_01JZ0000000000000000000000",
    "error": {
      "code": "protocol_error",
      "message": "unexpected event type for current state"
    }
  }
}

Ack Expectations

Sequence Tracking

Each event in a delivery stream carries a sequence number:

{
  "delivery": {
    "mode": "at_least_once",
    "sequence": 42,
    "cursor": "stream_01:42"
  }
}

The cursor format is transport-specific and opaque to consumers. Common formats:

TransportCursor Format
stdioNot applicable (no replay)
WebSocket<stream_id>:<sequence>
HTTP SSEEvent id from SSE protocol
NATS/KafkaNative offset or timestamp

Replay

A consumer replays events by subscribing with from_cursor:

{
  "types": ["task.*"],
  "from_cursor": "stream_01:42",
  "delivery_mode": "replayable"
}

The producer: 1. Finds the stream position matching the cursor. 2. Delivers all events from that position forward (including the event at the cursor position if from_cursor is inclusive). 3. Transitions to live delivery once caught up.

When replay is complete and the stream is caught up, the producer may send:

{
  "type": "event.replayed",
  "payload": {
    "from_cursor": "stream_01:42",
    "to_cursor": "stream_01:99",
    "events_replayed": 57
  }
}

Redelivery

When a consumer disconnects before acknowledging events, the producer may redeliver unacknowledged events on reconnect. Redelivered events carry:

{
  "type": "event.redelivered",
  "payload": {
    "original_event_id": "evt_01JZ0000000000000000000000",
    "attempt": 2
  }
}

The attempt field counts 1-indexed delivery attempts for the same event.

Implementation Notes