events: metadata rides as headers, not in the body (ADR 0047)

emit stamps the ADR 0047 headers — x-event-id, x-source, x-node, x-time,
content-type, and optional x-causation-id / x-schema — and publishes the
body as only the domain payload. on() reconstructs the Event from those
headers. Event gains id (the x-event-id a consumer dedups on) plus the
optional causation/schema. EventHeaders joins the contracts spine.

Supersedes the first cut that carried source/node/time in the body.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
2026-09-04 00:25:55 +02:00
parent f335bfb9e7
commit 20f7bd2a7b
4 changed files with 95 additions and 22 deletions
+29 -1
View File
@@ -44,9 +44,37 @@ export interface ToolDefinition {
readonly run: (args: Readonly<Record<string, unknown>>) => Promise<unknown>;
}
/** A message crossing the broker: a routing key and a JSON body, per-node addressed. */
/**
* The metadata that rides an event as AMQP headers (novox/hq ADR 0047). An event's identity and
* provenance live here, not in the body, so a consumer — or the broker, or an audit tool — reads
* who/when/what without parsing the payload. An unknown `x-` header is ignored, not refused: an
* event is observed by parties that need not all understand every header.
*/
export interface EventHeaders {
/** A unique id — for dedup and audit (delivery is at-least-once). */
readonly "x-event-id": string;
/** The emitter: the module, context or node name. */
readonly "x-source": string;
/** The node it was emitted from. */
readonly "x-node": string;
/** Emit time, RFC-3339. */
readonly "x-time": string;
/** Always `application/json`. */
readonly "content-type": string;
/** The event or command that caused this one — tracing. */
readonly "x-causation-id"?: string;
/** A version of the body's shape, so a body evolves without silent misreads. */
readonly "x-schema"?: string;
readonly [header: string]: string | undefined;
}
/**
* A message crossing the broker: a routing key and a JSON body, per-node addressed. For an event,
* `headers` carries the ADR 0047 metadata; plain request/reply transport leaves it absent.
*/
export interface Envelope<T = unknown> {
readonly key: string;
readonly node: string;
readonly body: T;
readonly headers?: EventHeaders;
}