The event envelope is a breaking-change surface: any change to envelope fields, naming rules, or lifecycle rules is a major version bump (see Core Principles versioning policy).

Status: Canonical Date: October 2026 Layer: Event Layer (Layer 8) Authority for: the common event envelope, event naming rules, event lifecycle rules, and the core OS event catalog. Domain packs extend this standard; they do not modify it. Supersedes: v0.4


Overview

Synedrix OS is event-driven and contract-first (Core Principle 8). Events are the first-class triggers that move work through the system: signals, approvals, state changes, failures, anomalies, and callbacks all enter and propagate as events. This standard defines the single common envelope every event must use, the naming rules that keep event types stable and greppable, the lifecycle rules that preserve end-to-end lineage, and the core OS event catalog emitted by the platform itself.

Every domain pack contributes its own event catalog (e.g. Art Event Catalog) under its own domain value, conforming to this envelope. No domain pack redefines the envelope, the naming rules, or the core OS events.


1. Event design principles

  1. Events are immutable facts. An event records that something happened. It is never mutated, re-emitted with the same event_id, or deleted after emission.
  2. One envelope, many categories. Every event — OS, intelligence, or domain — uses the same 15-field envelope. Categories differ in ownership and naming prefix, not in structure.
  3. Lineage is mandatory. Every event carries correlation_id (end-to-end trace) and causation_id (the prior event or step that caused it). An event without lineage is non-compliant.
  4. Names are stable, descriptive, and fact-based. Event types use dotted, lowercase, past-tense names (edition.created, not create.edition).
  5. No governance bypass. Policy-relevant events carry policy_context; events that represent or precede side effects pass through governance and remain auditable (Core Principle 4).
  6. Observable by default. Events feed the observability layer — Langfuse (LLM traces), QuestDB (temporal), Neo4j (lineage graph), PostHog (behavioral). Emission is not optional for the lifecycle events listed in §6.

2. Common event envelope (15 fields)

Every event — regardless of category or domain — uses these fields. This table is the authoritative definition; the per-domain catalogs reproduce it for convenience only.

FieldTypeRequiredPurpose
event_idstring (UUID)YesGlobally unique event identifier.
tenant_idstringYesThe tenant that owns the event. Stamped by the event layer on receipt, from the authenticated credential of the publisher or, for an inbound external event, from the tenant the ingress source belongs to. A producer does not set it. If a producer supplies one, it must equal the stamped value or the event is rejected. It is required on every event the event layer stores or delivers. The reserved value platform is valid only for an event about shared reference data emitted by a platform role.
event_typestringYesStable dotted name, e.g. order.created, os.outcome.succeeded (§4).
domainstringYesDomain pack code (art, trd, …) or core for OS events.
entity_typestringYesThe primary business entity class (e.g. edition, position, workflow).
entity_idstringYesIdentifier of the primary business entity.
source_typestringYesagent | workflow | tool_adapter | ui | external_system.
source_idstringYesIdentifier of the emitting agent, workflow, tool, or system.
timestampISO 8601 (UTC)YesEvent emission time.
correlation_idstringYesEnd-to-end trace ID; preserved unchanged across async steps and runtime-engine boundaries.
causation_idstringYesThe prior event or step that directly caused this one.
payloadobjectYesEvent-specific data; shape defined per event in the relevant catalog.
policy_contextobjectNoGovernance metadata (policy decision, approval reference) when applicable.
severitystringNolow | medium | high — classification for alerting and triage.
versionstring (SemVer)YesEvent schema version.

Invariants.

  • correlation_id is never regenerated mid-flow. When work crosses the n8n↔LangGraph boundary (Orchestration & Runtime Strategy), both correlation_id and causation_id pass through unchanged.
  • An event that omits any required field is non-compliant and must be rejected by the event layer.
  • tenant_id is stamped by the event layer and is never taken from the payload or from a header the caller sets. The required-field check above applies after stamping, so a producer’s event is not rejected for lacking it. An event is rejected if a supplied tenant_id differs from the one derived from the publisher’s credential, or if no tenant can be derived. This is the check the Task Graph Spec relies on for approval, resumption and cancellation events.
  • Every event in a causation_id chain has the same tenant_id, with one exception. A change to shared reference data emits a platform event, and each tenant effect it causes starts a new flow: a new correlation_id, a tenant_id of that tenant, and a causation_id that names the platform event. A platform event never shares a correlation_id with a tenant event, and a tenant event’s causation_id never names another tenant’s event.
  • Every inbound external source belongs to exactly one tenant. An inbound event whose source cannot be resolved to one tenant is rejected at the ingress and never enters the event layer.
  • Subscriptions, replay and the lineage projection are tenant-scoped: a consumer receives only its own tenant’s events, plus platform events where it subscribes to them. The Neo4j Implementation Guide already carries tenant_id on projected nodes.
  • Business-instance scope is not an envelope field in this version. Where an event needs it, its catalog carries it in the payload. The goal.* and task.* catalogs do not do so today, and whether they should is open.
  • domain values and identifier formats follow the Naming Convention Spec.

3. Event categories

Every event in Synedrix OS falls into one of three categories, each with its own ownership and prefixes.

CategoryOwnershipdomain valuePrefixesDescription
OS (core)Synedrix OScoreos., workflow., policy., approval., agent., memory., goal., task.Goal lifecycle, runtime task graph and task execution, workflow lifecycle, outcomes, approvals, policy decisions, agent invocation and tool authorization, and the memory control plane. Emitted by the platform on every run.
IntelligenceSynedrix OScoreos., intelligence., agent.evaluation.Analytics aggregations, forecasts, anomaly signals, evaluation and quality signals emitted by the OS intelligence-family agents (os-int-*) and the observability layer.
DomainDomain packpack code (art, trd, …)the domain code (art., trading., …)Domain-specific business facts. Defined in each pack’s event catalog; must conform to this envelope and may not collide with OS prefixes.

4. Naming rules

Event types are {scope}.{subject}.{fact} — lowercase, dotted, hyphen-allowed within a segment, no spaces or underscores in the dotted path.

  • Scope is the category prefix: os / workflow / policy / approval / agent / memory / goal / task for OS events, or the domain code (art, trd, …) for domain events.
  • Fact is past-tense and descriptive of what happened: created, completed, failed, approved, escalated, detected. Prefer facts over commands (edition.created, not create.edition).
  • Names are stable once published — renaming an event type is a breaking change to subscribers.
  • A trailing * (e.g. os.outcome.*, trading.signal.*) denotes a subscription or policy pattern, never an actual emitted event name.
  • Domain packs must not emit events under the reserved OS prefixes (os., workflow., policy., approval., agent., memory., goal., task.).
  • Runtime task graph events nest one level deeper as task.graph.{fact}, keeping graph-level and task-level facts under a single reserved prefix (§6.8).

5. Event lifecycle rules

  1. Emit-once, immutable. Each occurrence produces exactly one event with a unique event_id. Corrections are new events, not edits.
  2. Workflow bracketing. Every workflow emits workflow.started and exactly one terminal lifecycle event — workflow.completed or workflow.failed.
  3. Outcome emission. Every agent invocation and every workflow execution emits a corresponding os.outcome.* event derived from its Outcome Envelope (Outcome Spec).
  4. Approval bracketing. Approval-gated paths emit approval.requested and then exactly one of approval.granted / approval.denied.
  5. Causation chains form lineage. causation_id links each event to its direct cause; correlation_id ties the whole flow together. Together they reconstruct end-to-end lineage in Neo4j and are the basis for the Audit & Lineage Agent’s read-only reconstruction.
  6. Ordering. Consumers order by causation_id chain first and timestamp second; they must not assume wall-clock ordering across async boundaries.
  7. Append-only. The event log is append-only; no consumer or producer deletes or rewrites historical events.
  8. Memory bracketing. Every memory recall emits memory.recall.requested and then exactly one of memory.recall.served / memory.recall.denied. Every tier transition emits memory.record.promoted or memory.record.demoted.
  9. Erasure is an event, not an absence. Destroying a memory record’s content emits memory.record.forgotten; the event and the record’s lineage survive the erasure of the content itself (Memory Fabric Spec §8.4).
  10. Goal bracketing. Every Goal Instance that emits goal.received must later emit exactly one terminal goal event — goal.completed, goal.partially-completed, goal.failed, goal.blocked, or goal.cancelled. The obligation attaches at goal.received, not at goal.accepted: a Goal whose intent payload fails schema validation never reaches goal.accepted, and scoping the rule to accepted Goals would exempt from termination exactly the Goals most likely to be malformed. See the open item in §6.7 for how a pre-acceptance failure terminates while the Goal Model Spec has no explicit rejection transition.
  11. Completion carries its proof. goal.completed carries the evaluated success conditions in payload.success_conditions. A completion event without them is non-compliant: the Goal Model Spec forbids marking a Goal complete on a successful execution path alone, and the event is what makes that check auditable after the fact.
  12. Every graph version has a reason. Each graph_version increment under an existing task_graph_id emits task.graph.replanned carrying replan_reason. A graph version that appears in canonical state without a corresponding event is non-compliant — this is the audit-visible replan metadata the Task Graph Spec requires.
  13. Task bracketing. Every Task that is dispatched emits at least one task.dispatched and then exactly one terminal task event — task.succeeded, task.failed, task.blocked, or task.cancelled. (A Task whose conditional edge evaluates false is never dispatched and terminates at task.skipped.) Bracketing is at Task level, not Execution level: a retried Task emits one task.dispatched per attempt, and an attempt that fails while a further attempt is still permitted produces no terminal task event at all. Per-attempt failure and timeout are carried instead by each Execution’s own os.outcome.* event under rule 3. The two levels are complementary and neither substitutes for the other.
  14. One tenant per flow. tenant_id is fixed when the first event of a correlation_id is emitted and does not change across the flow. Work that must cross tenants is two flows linked through a governed, policy-approved path, not one flow. The one built-in case is a platform event, which starts a separate flow for each tenant effect (see the causation invariant in §2).

6. Core OS event catalog

These events are owned by Synedrix OS (domain: core) and are emitted by the platform regardless of which domain pack is active. Domain packs consume them but do not redefine them.

6.1 Workflow lifecycle

Event typeEmitted whenTypical severity
workflow.startedA workflow begins execution.low
workflow.completedA workflow finishes successfully.low
workflow.failedA workflow terminates with an unrecoverable error.high

6.2 Outcomes

One event per Outcome Envelope status value (see Outcome Spec).

Event typeOutcome status
os.outcome.succeededsuccess
os.outcome.partialpartial
os.outcome.failedfailure
os.outcome.blockedblocked (policy veto)
os.outcome.escalatedescalated (HITL)
os.outcome.timeouttimeout
os.outcome.skippedskipped

6.3 Approvals and policy

Event typeEmitted when
approval.requestedAn approval-gated path requests a decision.
approval.grantedA reviewer approves a request.
approval.deniedA reviewer rejects a request.
policy.decisionThe Policy Guard Agent emits an allow / block / escalate decision.
policy.block.raisedA proposed action is blocked by policy.
policy.confirmation.requiredA confirmation gate is triggered before a side effect.
policy.confirmation.grantedA required confirmation is granted.
policy.confirmation.deniedA required confirmation is denied.

6.4 Agent invocation, tool authorization, and runtime

Defined in the Agent Runtime Policy Spec; reproduced here as the canonical event names.

Event typeEmitted when
agent.lifecycle.changedAn agent registry lifecycle state transitions (e.g. draft → active).
agent.tool.authorizedA tool call passes authorization and is forwarded to execution.
agent.tool.unauthorizedA tool call is rejected (tool not in the resolved active set).
agent.tool.suspendedA tool call is rejected because the tool is suspended.
agent.tool.mcp_approval_requestedAn MCP tool call is submitted to Policy Guard before execution.
agent.tool.a2a_approval_requestedAn A2A delegation is submitted to Policy Guard before execution.
agent.tool.mcp_server_blockedAn MCP server call is blocked by denylist.
agent.runtime.model-fallbackA model fallback occurs.
agent.runtime.degraded-modeAn agent enters rules-only degraded mode.
agent.runtime.escalatedAn agent halts and routes to its escalation path.

6.5 Intelligence and observability

Emitted by the OS intelligence-family agents (os-int-*) and the observability layer (see Observability Metrics Spec).

Event typeEmitted when
os.response.sampled_for_reviewA response is sampled for quality review.
os.tool.reliability_aggregatedPer-tool reliability is aggregated over a window.
agent.evaluation.compliance-breachAn evaluation suite reports a compliance breach.
intelligence.anomaly.detectedThe Anomaly Detection Agent emits an anomaly for triage.
intelligence.forecast.generatedThe Forecasting Agent produces a forward projection.

6.6 Memory control plane

Emitted by the memory control-plane agents (os-agt-010 Memory Curator, os-agt-011 Consolidation & Reflection, os-agt-012 Memory Access Broker). The full semantics of each lifecycle stage are defined in Memory Fabric Spec §5.

Event typeEmitted whenEmitterSeverity
memory.record.formedContent becomes a committed memory record with a full envelope.os-agt-010low
memory.record.consolidatedSeveral records are merged into one derived record.os-agt-010low
memory.reflection.completedA higher-order assertion is inferred from consolidated records.os-agt-010low
memory.skill.distilledA reusable procedure is derived from repeated successful trajectories.os-agt-010low
memory.record.promotedA record crosses a tier boundary, or its visibility is widened.os-agt-010medium
memory.record.demotedA record’s tier or visibility is reduced.os-agt-010low
memory.recall.requestedAn agent invokes tool:memory-recall.os-agt-012low
memory.recall.servedRecall is allowed (in full or in part) and records are returned.os-agt-012low
memory.recall.deniedRecall is refused because the requested scope or tier exceeds the agent’s memory_profile.os-agt-012medium
memory.record.expiredA validity_interval closes or a retention window ends.os-agt-010low
memory.record.forgottenA record’s content is erased and a tombstone is written.os-agt-010high
memory.conflict.detectedTwo records overlap in validity interval and disagree.os-agt-011medium

Three emission rules are load-bearing:

  • memory.record.promoted is the tier boundary. No record may cross tiers without it, and it carries the record’s correlation_id and causation_id unchanged — memory lineage composes with event lineage rather than running beside it.
  • memory.recall.denied carries policy_context. A denial is a governance decision and must be auditable; a recall that is silently narrowed rather than denied is non-compliant.
  • memory.record.forgotten is severity: high and never suppressed. Erasure is the one memory operation that destroys data, so its event is the durable proof that the destruction was approved.

6.7 Goal lifecycle

Emitted by the Orchestrator Agent (os-agt-001). The states each event brackets are defined in Synedrix OS — Goal Model Spec; this section names the events, not the state machine. Every event in this family sets entity_type: goal and entity_id to the Goal Instance’s goal_id, and carries that Goal’s correlation_id for its entire life.

Event typeEmitted whenEmitterSeverity
goal.receivedA Goal Instance is created from a Goal Type and enters Synedrix OS.os-agt-001low
goal.acceptedThe Goal Instance validates against its Goal Type’s intent schema and is admitted for planning.os-agt-001low
goal.planning.startedPlanning begins and the Orchestrator starts assembling planning context.os-agt-001low
goal.execution.startedThe first Task of the Goal’s validated graph is dispatched.os-agt-001low
goal.suspendedThe Goal enters waiting_approval and no Task is currently runnable.os-agt-001low
goal.resumedA suspended Goal returns to executing after its blocking condition clears.os-agt-001low
goal.completedEvery declared success condition is satisfied and proven from task outcomes and canonical state.os-agt-001low
goal.partially-completedThe Goal terminates with some but not all success conditions satisfied.os-agt-001medium
goal.failedThe Goal terminates without satisfying its success conditions and cannot be recovered.os-agt-001high
goal.blockedThe Goal cannot proceed because required capability coverage, authority, or input is unavailable.os-agt-001high
goal.cancelledThe Goal is withdrawn by its requester or by a governed intervention.os-agt-001medium

Three emission rules are load-bearing:

  • goal.suspended does not replace approval.requested. A Goal waiting on a human emits both: the approval event brackets the decision (rule 4), the goal event records that the Goal itself parked. Collapsing them loses the distinction between a Goal waiting on one approval and a Goal with no runnable work at all.
  • goal.completed carries payload.success_conditions with each declared condition and its evaluated result (rule 11). This is the event-layer enforcement of the Goal Model Spec rule that a successful execution path is not by itself sufficient for completion.
  • goal.blocked carries policy_context when authority is the cause. A Goal blocked by missing capability coverage and a Goal blocked by policy are different facts to an operator, and policy_context is what separates them without reading the payload.

Open item — no rejection transition. The Goal Model Spec’s lifecycle runs received → accepted with no negative path, so a Goal Instance whose intent_payload fails validation against its Goal Type schema has no state to land in. Rule 10 still requires it to terminate. Until that spec defines an explicit rejection transition, such a Goal terminates as goal.blocked with the validation errors in payload.validation_errors and no policy_context — the absence of policy_context is what distinguishes a malformed intent from an authority refusal. A dedicated goal.rejected event should replace this once the lifecycle gains the state, and that belongs to the Goal Model Spec, not to this standard.

6.8 Runtime task graph and task execution

Emitted by the Orchestrator Agent (os-agt-001). Semantics are defined in Synedrix OS — Task Graph Spec, and the planning statuses that task.graph.rejected and task.graph.incomplete report come from Synedrix OS — Goal Planning Strategy. Graph-level facts nest as task.graph.{fact}; task-level facts sit directly under task..

Event typeEmitted whenEmitterSeverity
task.graph.proposedA candidate Runtime Task Graph Instance is materialized for a Goal. Payload carries planning_mode and graph_version.os-agt-001low
task.graph.validatedThe candidate graph passes deterministic validation and becomes eligible for activation.os-agt-001low
task.graph.rejectedValidation fails (planning_status: invalid). Payload carries validation_errors.os-agt-001high
task.graph.incompleteThe Goal is understood but required capabilities, inputs, policy paths, or executors are unavailable (planning_status: incomplete). Payload carries missing_capabilities and unresolved_inputs.os-agt-001high
task.graph.activatedA validated graph becomes active and its runnable Tasks may be dispatched.os-agt-001low
task.graph.suspendedNo Task is runnable; the graph parks in canonical state and the orchestration execution may terminate cleanly.os-agt-001low
task.graph.resumedGraph state is reloaded, runnable Tasks are recalculated, and execution continues.os-agt-001low
task.graph.replannedA new graph_version is created under the same task_graph_id. Payload carries replan_reason and the prior and new versions.os-agt-001medium
task.dispatchedA runnable Task is dispatched to its bound executor. One per Execution attempt; payload carries execution_id, attempt, and the resolved executor and version.os-agt-001low
task.succeededAn Execution completes and the Task reaches succeeded.os-agt-001low
task.failedAn Execution terminates unrecoverably and no further attempt is permitted.os-agt-001high
task.blockedPolicy, authority, or Business Instance scope prevents the Task from executing.os-agt-001high
task.skippedA conditional edge evaluated false, so the Task is never dispatched.os-agt-001low
task.cancelledThe Task is cancelled by graph-level cancellation or governed intervention.os-agt-001medium

Four emission rules are load-bearing:

  • task.graph.suspended is what makes long waits survivable. It is the durable record that orchestration handed coordination back to canonical state rather than holding an execution open. A graph that stops producing events without emitting it is indistinguishable from a stalled one.
  • task.graph.incomplete is severity: high by design. An incomplete plan is the intended alternative to a fabricated one, so it must surface to an operator rather than reading as a quiet non-result.
  • Runnability is deliberately not an event. There is no task.runnable. Runnability is a deterministic function of canonical state, recomputed on every scheduling pass; emitting it would produce high-volume events carrying no fact canonical state does not already hold. The first observable moment in a Task’s life is task.dispatched.
  • Task events and outcome events are complementary. task.succeeded records the graph-level state transition; the os.outcome.* event records the Execution’s Outcome Envelope (rule 3). An operator reconstructing a Goal reads the task events for structure and the outcome events for result.

Open item — emitter authority. This section names os-agt-001 as the emitter of every goal.* and task.* event, consistent with the Orchestrator’s mission in the Agent Registry. The registry’s current os-agt-001 entry grants Supabase read only, while the Task Graph Spec requires authoritative graph state to be written to Supabase PG17. Emitting these events is therefore specified ahead of the authority to produce the state they describe. Resolving this belongs to the next Agent Registry bump, not to this standard.


7. Domain extension and conformance

A domain pack catalog:

  • Declares its events under its own domain value and prefix (art.*, trading.*, …).
  • Uses the §2 envelope verbatim, including tenant_id; it may constrain optional fields (e.g. always set severity) but may not add envelope fields. A domain catalog never sets tenant_id itself; the event layer assigns it.
  • Must still emit the §6.1 workflow lifecycle events and, where gated, the §6.3 approval events — these are not redefined in the domain catalog.
  • May not use a reserved OS prefix or collide with an existing event type.

Open: examples in other documents. Several documents reproduce full event envelopes in their examples and still show 14 fields: the Observability Metrics Spec, the n8n Implementation Guide, the Domain Pack Template, the MRD workflow contracts, the Trading domain pack and Academy chapter 07.2. They are non-compliant examples until each is updated with its own change.


8. Example events

8.1 Core OS outcome event

{
  "event_id": "evt-9f3c1a20-7b4e-4d2a-9c11-2a6f0e8b1d44",
  "tenant_id": "acme",
  "event_type": "os.outcome.succeeded",
  "domain": "core",
  "entity_type": "workflow",
  "entity_id": "WF-ART-003-EDITION-ALLOCATION",
  "source_type": "workflow",
  "source_id": "WF-ART-003-EDITION-ALLOCATION",
  "timestamp": "2026-06-28T16:42:11Z",
  "correlation_id": "corr-7c2e9b18-1f6a-4c33-8e0d-9b2f1a4c7d10",
  "causation_id": "evt-4b1d77e0-2c9a-4f81-bb20-1e3c5a9f0b22",
  "payload": {
    "outcome_id": "out-3a5e...",
    "status": "success",
    "duration_ms": 1840
  },
  "policy_context": null,
  "severity": "low",
  "version": "1.0.0"
}
Illustrative example, not a production implementation. Review and test before use.

8.2 Domain event (Art)

{
  "event_id": "evt-2d8a44c1-6f0b-4e9d-9a13-77c2e5b0a991",
  "tenant_id": "acme",
  "event_type": "art.edition.created",
  "domain": "art",
  "entity_type": "edition",
  "entity_id": "edition-00871",
  "source_type": "agent",
  "source_id": "art-exe-edition-allocator-001",
  "timestamp": "2026-06-28T16:41:58Z",
  "correlation_id": "corr-7c2e9b18-1f6a-4c33-8e0d-9b2f1a4c7d10",
  "causation_id": "evt-1aa9...",
  "payload": {
    "asset_id": "asset-5521",
    "edition_size": 50,
    "medium": "giclee"
  },
  "policy_context": { "policy_decision": "allow" },
  "severity": "medium",
  "version": "1.0.0"
}
Illustrative example, not a production implementation. Review and test before use.

8.3 Goal terminal event

{
  "event_id": "evt-6c40b9d2-18ae-4f77-a3e1-0d5b9c7f2a83",
  "tenant_id": "acme",
  "event_type": "goal.completed",
  "domain": "core",
  "entity_type": "goal",
  "entity_id": "goal-7f91",
  "source_type": "agent",
  "source_id": "os-agt-001",
  "timestamp": "2026-09-12T09:14:02Z",
  "correlation_id": "corr-3d81f4a7-5b20-4e6c-9f13-8c7a2e0b5d19",
  "causation_id": "evt-0b7e21c4-9a33-4d18-8e52-6f1c3b9a7e05",
  "payload": {
    "goal_type": "procurement.purchase",
    "goal_type_version": "1.0.0",
    "task_graph_id": "tg-4a19",
    "graph_version": 2,
    "planning_mode": "adapted",
    "success_conditions": [
      { "condition": "purchase_exists", "satisfied": true },
      { "condition": "requested_quantity_satisfied", "satisfied": true },
      { "condition": "total_cost_lte_intent_budget", "satisfied": true },
      { "condition": "required_governance_completed", "satisfied": true }
    ]
  },
  "policy_context": { "policy_decision": "allow", "approval_ref": "apr-55c2" },
  "severity": "low",
  "version": "1.0.0"
}
Illustrative example, not a production implementation. Review and test before use.

The graph_version: 2 and planning_mode: adapted in this payload are the link back to a task.graph.replanned event earlier in the same correlation_id chain — completion events carry enough graph identity that an auditor can find the replan without a separate lookup.


Versioning

  • Version: 1.0
  • Supersedes: v0.4

Canonical Reference

Architecture and Standards

Domain Event Catalogs

  • Synedrix Art Event Catalog — art.* events conforming to this envelope.


Synedrix OS · Event Taxonomy Standard v0.4 · September 2026