JSON Schemas¶
reflexr publishes two JSON Schemas, generated from its Pydantic models, which are the source of truth, and checked in under schemas/:
| Schema | What it describes | Use it to |
|---|---|---|
reflexr.rules.v1.json |
A rule: its condition, scope, action and policies | Validate rules written as JSON, by people or by agents |
reflexr.v1.json |
Every frame of the stream protocol: client is any frame a client sends, and server any frame a server sends; $defs holds every frame, command, outcome and envelope they refer to |
Generate a client's types |
A test fails if either checked-in file drifts from the models. Regenerate both with make schema, which runs:
uv run python -m reflexr.core.schema rules > schemas/reflexr.rules.v1.json
uv run python -m reflexr.core.schema protocol > schemas/reflexr.v1.json
Validating rules¶
A rule serializes to plain JSON, with durations as ISO 8601 strings. Rule.model_validate reads it back, and any JSON Schema validator can check it against reflexr.rules.v1.json first, which is how an application can accept rules from an agent or a form:
from reflexr import Rule
from reflexr.core import DEFAULT_REGISTRY
rule = Rule.model_validate_json(text) # raises pydantic.ValidationError if it does not fit
rule.check(events=DEFAULT_REGISTRY) # raises InvalidRule for unknown types or fields
Names are qualified: an event type or a rule name without its namespace fails validation, and an event type's error names the qualified one it may mean.
Generating client types¶
Frontends generate their types from the protocol schema rather than writing them by hand. For TypeScript, for example, with json-schema-to-typescript:
Within reflexr.v1, changes are additive only: new optional fields and new event types. Clients should ignore fields they do not know, and keep an event of an unknown type as an opaque envelope, since it still carries a seq. Application events appear in the schema as objects with a type and any other fields, because the application defines them.
The schemas¶
schemas/reflexr.rules.v1.json
{
"$defs": {
"AbsencePattern": {
"additionalProperties": false,
"description": "Fire once when nothing has passed the filter for ``within`` since the last envelope did.\n\nOnly scopes that have been seen can go quiet. Time passes with every envelope in the log,\nwhether or not it passes the filter.",
"properties": {
"kind": {
"const": "absence",
"default": "absence",
"title": "Kind",
"type": "string"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"within"
],
"title": "AbsencePattern",
"type": "object"
},
"ActionRef": {
"additionalProperties": false,
"description": "The action a rule runs, by name, and the params the rule passes it.",
"properties": {
"action": {
"minLength": 1,
"title": "Action",
"type": "string"
},
"params": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"title": "Params",
"type": "object"
}
},
"required": [
"action"
],
"title": "ActionRef",
"type": "object"
},
"AllFilter": {
"additionalProperties": false,
"description": "Envelopes that match every filter in ``of``.",
"properties": {
"kind": {
"const": "all",
"default": "all",
"title": "Kind",
"type": "string"
},
"of": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 1,
"title": "Of",
"type": "array"
}
},
"required": [
"of"
],
"title": "AllFilter",
"type": "object"
},
"AnyFilter": {
"additionalProperties": false,
"description": "Envelopes that match at least one filter in ``of``.",
"properties": {
"kind": {
"const": "any",
"default": "any",
"title": "Kind",
"type": "string"
},
"of": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 1,
"title": "Of",
"type": "array"
}
},
"required": [
"of"
],
"title": "AnyFilter",
"type": "object"
},
"Condition": {
"additionalProperties": false,
"description": "A rule's ``when``: a filter, and optionally dedupe, a pattern and a throttle.",
"properties": {
"dedupe": {
"anyOf": [
{
"$ref": "#/$defs/Dedupe"
},
{
"type": "null"
}
],
"default": null
},
"filter": {
"$ref": "#/$defs/Filter"
},
"pattern": {
"$ref": "#/$defs/Pattern",
"default": {
"kind": "each"
}
},
"throttle": {
"anyOf": [
{
"$ref": "#/$defs/Throttle"
},
{
"type": "null"
}
],
"default": null
}
},
"required": [
"filter"
],
"title": "Condition",
"type": "object"
},
"CountPattern": {
"additionalProperties": false,
"description": "Fire when ``at_least`` envelopes pass within ``within``. A firing consumes them.",
"properties": {
"at_least": {
"minimum": 1,
"title": "At Least",
"type": "integer"
},
"kind": {
"const": "count",
"default": "count",
"title": "Kind",
"type": "string"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"at_least",
"within"
],
"title": "CountPattern",
"type": "object"
},
"Dedupe": {
"additionalProperties": false,
"description": "Drop an envelope whose ``key`` fields were already seen within ``within``.",
"properties": {
"key": {
"items": {
"type": "string"
},
"minItems": 1,
"title": "Key",
"type": "array"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"key",
"within"
],
"title": "Dedupe",
"type": "object"
},
"EachPattern": {
"additionalProperties": false,
"description": "Fire for every envelope that passes the filter.",
"properties": {
"kind": {
"const": "each",
"default": "each",
"title": "Kind",
"type": "string"
}
},
"title": "EachPattern",
"type": "object"
},
"Filter": {
"discriminator": {
"mapping": {
"all": "#/$defs/AllFilter",
"any": "#/$defs/AnyFilter",
"not": "#/$defs/NotFilter",
"on": "#/$defs/OnFilter",
"predicate": "#/$defs/PredicateFilter",
"where": "#/$defs/WhereFilter"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/OnFilter"
},
{
"$ref": "#/$defs/WhereFilter"
},
{
"$ref": "#/$defs/AllFilter"
},
{
"$ref": "#/$defs/AnyFilter"
},
{
"$ref": "#/$defs/NotFilter"
},
{
"$ref": "#/$defs/PredicateFilter"
}
]
},
"JsonValue": {},
"NotFilter": {
"additionalProperties": false,
"description": "Envelopes that do not match ``filter``.",
"properties": {
"filter": {
"$ref": "#/$defs/Filter"
},
"kind": {
"const": "not",
"default": "not",
"title": "Kind",
"type": "string"
}
},
"required": [
"filter"
],
"title": "NotFilter",
"type": "object"
},
"OnFilter": {
"additionalProperties": false,
"description": "Envelopes whose event is one of ``types``.",
"properties": {
"kind": {
"const": "on",
"default": "on",
"title": "Kind",
"type": "string"
},
"types": {
"items": {
"pattern": "^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$",
"type": "string"
},
"minItems": 1,
"title": "Types",
"type": "array"
}
},
"required": [
"types"
],
"title": "OnFilter",
"type": "object"
},
"Pattern": {
"discriminator": {
"mapping": {
"absence": "#/$defs/AbsencePattern",
"count": "#/$defs/CountPattern",
"each": "#/$defs/EachPattern",
"sequence": "#/$defs/SequencePattern"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/EachPattern"
},
{
"$ref": "#/$defs/CountPattern"
},
{
"$ref": "#/$defs/SequencePattern"
},
{
"$ref": "#/$defs/AbsencePattern"
}
]
},
"PredicateFilter": {
"additionalProperties": false,
"description": "Envelopes whose event the registered Python predicate ``name`` accepts.\n\nAn escape hatch for what the other filters cannot express. Predicates must be pure.",
"properties": {
"kind": {
"const": "predicate",
"default": "predicate",
"title": "Kind",
"type": "string"
},
"name": {
"minLength": 1,
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "PredicateFilter",
"type": "object"
},
"RetryPolicy": {
"additionalProperties": false,
"description": "How a failed run is retried: exponential backoff, up to ``max_attempts`` attempts.",
"properties": {
"backoff": {
"default": "PT1S",
"format": "duration",
"title": "Backoff",
"type": "string"
},
"max_attempts": {
"default": 5,
"minimum": 1,
"title": "Max Attempts",
"type": "integer"
},
"max_backoff": {
"default": "PT5M",
"format": "duration",
"title": "Max Backoff",
"type": "string"
},
"multiplier": {
"default": 2.0,
"minimum": 1.0,
"title": "Multiplier",
"type": "number"
}
},
"title": "RetryPolicy",
"type": "object"
},
"RuleName_a-z__a-z0-9____a-z0-9___-_____": {
"maxLength": 100,
"pattern": "^[a-z][a-z0-9_]*:[a-z0-9][a-z0-9._-]*$",
"type": "string"
},
"Scope": {
"additionalProperties": false,
"description": "The event fields that partition a rule's state and ordering. None: the whole workspace.",
"properties": {
"fields": {
"default": [],
"items": {
"type": "string"
},
"title": "Fields",
"type": "array"
}
},
"title": "Scope",
"type": "object"
},
"SequencePattern": {
"additionalProperties": false,
"description": "Fire when envelopes match ``steps`` in order, all within ``within`` of the first.\n\nAn envelope that matches the first step while a sequence is in progress (but not the step\nit is waiting for) starts the sequence again from it, so the latest start counts.",
"properties": {
"kind": {
"const": "sequence",
"default": "sequence",
"title": "Kind",
"type": "string"
},
"steps": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 2,
"title": "Steps",
"type": "array"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"steps",
"within"
],
"title": "SequencePattern",
"type": "object"
},
"Throttle": {
"additionalProperties": false,
"description": "Allow at most ``at_most`` firings per scope in any period of ``per``.",
"properties": {
"at_most": {
"minimum": 1,
"title": "At Most",
"type": "integer"
},
"per": {
"format": "duration",
"title": "Per",
"type": "string"
}
},
"required": [
"at_most",
"per"
],
"title": "Throttle",
"type": "object"
},
"WhereFilter": {
"additionalProperties": false,
"description": "Envelopes whose event field ``field`` compares with ``value`` by ``op``.\n\nA missing field matches nothing, except ``exists`` with ``value=False``.",
"properties": {
"field": {
"pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$",
"title": "Field",
"type": "string"
},
"kind": {
"const": "where",
"default": "where",
"title": "Kind",
"type": "string"
},
"op": {
"enum": [
"eq",
"ne",
"lt",
"le",
"gt",
"ge",
"in",
"contains",
"matches",
"exists"
],
"title": "Op",
"type": "string"
},
"value": {
"$ref": "#/$defs/JsonValue",
"default": null
}
},
"required": [
"field",
"op"
],
"title": "WhereFilter",
"type": "object"
}
},
"$id": "https://github.com/alexnodeland/reflexr/schemas/reflexr.rules.v1.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "When ``when`` holds for a scope, run ``then``.",
"properties": {
"description": {
"default": "",
"title": "Description",
"type": "string"
},
"enabled": {
"default": true,
"title": "Enabled",
"type": "boolean"
},
"name": {
"$ref": "#/$defs/RuleName_a-z__a-z0-9____a-z0-9___-_____"
},
"on_dead_letter": {
"default": "continue",
"enum": [
"continue",
"block"
],
"title": "On Dead Letter",
"type": "string"
},
"ordering": {
"default": "scope",
"enum": [
"scope",
"none"
],
"title": "Ordering",
"type": "string"
},
"retry": {
"$ref": "#/$defs/RetryPolicy",
"default": {
"backoff": "PT1S",
"max_attempts": 5,
"max_backoff": "PT5M",
"multiplier": 2.0
}
},
"scope": {
"$ref": "#/$defs/Scope",
"default": {
"fields": []
}
},
"start": {
"default": "now",
"enum": [
"now",
"beginning"
],
"title": "Start",
"type": "string"
},
"then": {
"$ref": "#/$defs/ActionRef"
},
"timeout": {
"anyOf": [
{
"format": "duration",
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Timeout"
},
"when": {
"$ref": "#/$defs/Condition"
}
},
"required": [
"name",
"when",
"then"
],
"title": "Rule",
"type": "object"
}
schemas/reflexr.v1.json
{
"$defs": {
"AbsencePattern": {
"additionalProperties": false,
"description": "Fire once when nothing has passed the filter for ``within`` since the last envelope did.\n\nOnly scopes that have been seen can go quiet. Time passes with every envelope in the log,\nwhether or not it passes the filter.",
"properties": {
"kind": {
"const": "absence",
"default": "absence",
"title": "Kind",
"type": "string"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"within"
],
"title": "AbsencePattern",
"type": "object"
},
"ActionRef": {
"additionalProperties": false,
"description": "The action a rule runs, by name, and the params the rule passes it.",
"properties": {
"action": {
"minLength": 1,
"title": "Action",
"type": "string"
},
"params": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"title": "Params",
"type": "object"
}
},
"required": [
"action"
],
"title": "ActionRef",
"type": "object"
},
"AgentActor": {
"description": "An agent or graph running an action for one firing of a rule.",
"properties": {
"kind": {
"const": "agent",
"default": "agent",
"title": "Kind",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"run_id": {
"$ref": "#/$defs/RunId"
}
},
"required": [
"rule",
"run_id",
"name"
],
"title": "AgentActor",
"type": "object"
},
"AllFilter": {
"additionalProperties": false,
"description": "Envelopes that match every filter in ``of``.",
"properties": {
"kind": {
"const": "all",
"default": "all",
"title": "Kind",
"type": "string"
},
"of": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 1,
"title": "Of",
"type": "array"
}
},
"required": [
"of"
],
"title": "AllFilter",
"type": "object"
},
"AnyFilter": {
"additionalProperties": false,
"description": "Envelopes that match at least one filter in ``of``.",
"properties": {
"kind": {
"const": "any",
"default": "any",
"title": "Kind",
"type": "string"
},
"of": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 1,
"title": "Of",
"type": "array"
}
},
"required": [
"of"
],
"title": "AnyFilter",
"type": "object"
},
"ArchiveRule": {
"additionalProperties": false,
"description": "Archive a stored rule, and cancel its unfinished runs.",
"properties": {
"expected_version": {
"anyOf": [
{
"minimum": 1,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Expected Version"
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Reason"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"type": {
"const": "archive_rule",
"default": "archive_rule",
"title": "Type",
"type": "string"
}
},
"required": [
"rule"
],
"title": "ArchiveRule",
"type": "object"
},
"CancelRun": {
"additionalProperties": false,
"description": "Cancel a run that has not finished, stopping its action if it is running.",
"properties": {
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Reason"
},
"run_id": {
"$ref": "#/$defs/RunId"
},
"type": {
"const": "cancel_run",
"default": "cancel_run",
"title": "Type",
"type": "string"
}
},
"required": [
"run_id"
],
"title": "CancelRun",
"type": "object"
},
"Causation": {
"description": "What caused an event that a run emitted, and how deep its causal chain is.",
"properties": {
"depth": {
"minimum": 1,
"title": "Depth",
"type": "integer"
},
"firing_id": {
"$ref": "#/$defs/FiringId"
},
"run_id": {
"$ref": "#/$defs/RunId"
}
},
"required": [
"firing_id",
"run_id",
"depth"
],
"title": "Causation",
"type": "object"
},
"ChainTarget": {
"additionalProperties": false,
"description": "Feedback on a causal chain: everything one triggering event led to.",
"properties": {
"correlation_id": {
"title": "Correlation Id",
"type": "string"
},
"kind": {
"const": "chain",
"default": "chain",
"title": "Kind",
"type": "string"
}
},
"required": [
"correlation_id"
],
"title": "ChainTarget",
"type": "object"
},
"ClientFrame": {
"discriminator": {
"mapping": {
"command": "#/$defs/CommandFrame",
"hello": "#/$defs/Hello"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/Hello"
},
{
"$ref": "#/$defs/CommandFrame"
}
]
},
"Command": {
"discriminator": {
"mapping": {
"archive_rule": "#/$defs/ArchiveRule",
"cancel_run": "#/$defs/CancelRun",
"give_feedback": "#/$defs/GiveFeedback",
"install_rule": "#/$defs/InstallRule",
"publish": "#/$defs/Publish",
"replay_rule": "#/$defs/ReplayRule",
"retry_run": "#/$defs/RetryRun",
"skip_run": "#/$defs/SkipRun",
"update_rule": "#/$defs/UpdateRule"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/Publish"
},
{
"$ref": "#/$defs/GiveFeedback"
},
{
"$ref": "#/$defs/RetryRun"
},
{
"$ref": "#/$defs/SkipRun"
},
{
"$ref": "#/$defs/CancelRun"
},
{
"$ref": "#/$defs/ReplayRule"
},
{
"$ref": "#/$defs/InstallRule"
},
{
"$ref": "#/$defs/UpdateRule"
},
{
"$ref": "#/$defs/ArchiveRule"
}
]
},
"CommandFrame": {
"additionalProperties": false,
"description": "A client's command, with an id that correlates it with its result.\n\n``command_id`` is also an idempotency key: the server deduplicates repeated ids.",
"properties": {
"command": {
"$ref": "#/$defs/Command"
},
"command_id": {
"minLength": 1,
"title": "Command Id",
"type": "string"
},
"type": {
"const": "command",
"default": "command",
"title": "Type",
"type": "string"
}
},
"required": [
"command_id",
"command"
],
"title": "CommandFrame",
"type": "object"
},
"CommandResult": {
"additionalProperties": false,
"description": "The result of one command frame.",
"properties": {
"command_id": {
"title": "Command Id",
"type": "string"
},
"ok": {
"title": "Ok",
"type": "boolean"
},
"outcome": {
"anyOf": [
{
"$ref": "#/$defs/Outcome"
},
{
"type": "null"
}
],
"default": null
},
"rejection": {
"anyOf": [
{
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"title": "Rejection"
},
"type": {
"const": "command_result",
"default": "command_result",
"title": "Type",
"type": "string"
}
},
"required": [
"command_id",
"ok"
],
"title": "CommandResult",
"type": "object"
},
"Condition": {
"additionalProperties": false,
"description": "A rule's ``when``: a filter, and optionally dedupe, a pattern and a throttle.",
"properties": {
"dedupe": {
"anyOf": [
{
"$ref": "#/$defs/Dedupe"
},
{
"type": "null"
}
],
"default": null
},
"filter": {
"$ref": "#/$defs/Filter"
},
"pattern": {
"$ref": "#/$defs/Pattern",
"default": {
"kind": "each"
}
},
"throttle": {
"anyOf": [
{
"$ref": "#/$defs/Throttle"
},
{
"type": "null"
}
],
"default": null
}
},
"required": [
"filter"
],
"title": "Condition",
"type": "object"
},
"CountPattern": {
"additionalProperties": false,
"description": "Fire when ``at_least`` envelopes pass within ``within``. A firing consumes them.",
"properties": {
"at_least": {
"minimum": 1,
"title": "At Least",
"type": "integer"
},
"kind": {
"const": "count",
"default": "count",
"title": "Kind",
"type": "string"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"at_least",
"within"
],
"title": "CountPattern",
"type": "object"
},
"Dedupe": {
"additionalProperties": false,
"description": "Drop an envelope whose ``key`` fields were already seen within ``within``.",
"properties": {
"key": {
"items": {
"type": "string"
},
"minItems": 1,
"title": "Key",
"type": "array"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"key",
"within"
],
"title": "Dedupe",
"type": "object"
},
"EachPattern": {
"additionalProperties": false,
"description": "Fire for every envelope that passes the filter.",
"properties": {
"kind": {
"const": "each",
"default": "each",
"title": "Kind",
"type": "string"
}
},
"title": "EachPattern",
"type": "object"
},
"ErrorFrame": {
"additionalProperties": false,
"description": "A frame the server could not understand.",
"properties": {
"message": {
"title": "Message",
"type": "string"
},
"type": {
"const": "error",
"default": "error",
"title": "Type",
"type": "string"
}
},
"required": [
"message"
],
"title": "ErrorFrame",
"type": "object"
},
"EvaluatorActor": {
"description": "An evaluator: a judge or decision model whose verdicts are recorded as feedback.",
"properties": {
"kind": {
"const": "evaluator",
"default": "evaluator",
"title": "Kind",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"version": {
"title": "Version",
"type": "string"
}
},
"required": [
"name",
"version"
],
"title": "EvaluatorActor",
"type": "object"
},
"EventFrame": {
"description": "An envelope from the log.",
"properties": {
"actor": {
"discriminator": {
"mapping": {
"agent": "#/$defs/AgentActor",
"evaluator": "#/$defs/EvaluatorActor",
"external_agent": "#/$defs/ExternalAgentActor",
"source": "#/$defs/SourceActor",
"system": "#/$defs/SystemActor",
"user": "#/$defs/UserActor"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/UserActor"
},
{
"$ref": "#/$defs/AgentActor"
},
{
"$ref": "#/$defs/ExternalAgentActor"
},
{
"$ref": "#/$defs/SystemActor"
},
{
"$ref": "#/$defs/SourceActor"
},
{
"$ref": "#/$defs/EvaluatorActor"
}
],
"title": "Actor"
},
"causation": {
"anyOf": [
{
"$ref": "#/$defs/Causation"
},
{
"type": "null"
}
],
"default": null
},
"correlation_id": {
"title": "Correlation Id",
"type": "string"
},
"event": {
"additionalProperties": true,
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
],
"title": "Event",
"type": "object"
},
"id": {
"$ref": "#/$defs/EventId"
},
"seq": {
"minimum": 1,
"title": "Seq",
"type": "integer"
},
"traceparent": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Traceparent"
},
"ts": {
"format": "date-time",
"title": "Ts",
"type": "string"
},
"type": {
"const": "event",
"default": "event",
"title": "Type",
"type": "string"
},
"workspace_id": {
"$ref": "#/$defs/WorkspaceId"
}
},
"required": [
"seq",
"id",
"ts",
"workspace_id",
"actor",
"correlation_id",
"event"
],
"title": "EventFrame",
"type": "object"
},
"EventId": {
"type": "string"
},
"ExternalAgentActor": {
"description": "An agent outside reflexr, connected over MCP.",
"properties": {
"client_id": {
"title": "Client Id",
"type": "string"
},
"kind": {
"const": "external_agent",
"default": "external_agent",
"title": "Kind",
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Name"
}
},
"required": [
"client_id"
],
"title": "ExternalAgentActor",
"type": "object"
},
"FeedbackTarget": {
"discriminator": {
"mapping": {
"chain": "#/$defs/ChainTarget",
"firing": "#/$defs/FiringTarget",
"run": "#/$defs/RunTarget"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/RunTarget"
},
{
"$ref": "#/$defs/FiringTarget"
},
{
"$ref": "#/$defs/ChainTarget"
}
]
},
"Filter": {
"discriminator": {
"mapping": {
"all": "#/$defs/AllFilter",
"any": "#/$defs/AnyFilter",
"not": "#/$defs/NotFilter",
"on": "#/$defs/OnFilter",
"predicate": "#/$defs/PredicateFilter",
"where": "#/$defs/WhereFilter"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/OnFilter"
},
{
"$ref": "#/$defs/WhereFilter"
},
{
"$ref": "#/$defs/AllFilter"
},
{
"$ref": "#/$defs/AnyFilter"
},
{
"$ref": "#/$defs/NotFilter"
},
{
"$ref": "#/$defs/PredicateFilter"
}
]
},
"FiringId": {
"type": "string"
},
"FiringTarget": {
"additionalProperties": false,
"description": "Feedback on one firing: whether the rule should have fired.",
"properties": {
"firing_id": {
"$ref": "#/$defs/FiringId"
},
"kind": {
"const": "firing",
"default": "firing",
"title": "Kind",
"type": "string"
}
},
"required": [
"firing_id"
],
"title": "FiringTarget",
"type": "object"
},
"GiveFeedback": {
"additionalProperties": false,
"description": "Give typed feedback on a run, a firing or a causal chain.",
"properties": {
"feedback_type": {
"title": "Feedback Type",
"type": "string"
},
"target": {
"$ref": "#/$defs/FeedbackTarget"
},
"type": {
"const": "give_feedback",
"default": "give_feedback",
"title": "Type",
"type": "string"
},
"value": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"title": "Value",
"type": "object"
}
},
"required": [
"feedback_type",
"target",
"value"
],
"title": "GiveFeedback",
"type": "object"
},
"Hello": {
"additionalProperties": false,
"description": "The first frame a client sends on a connection.",
"properties": {
"from_head": {
"default": false,
"title": "From Head",
"type": "boolean"
},
"protocol": {
"title": "Protocol",
"type": "string"
},
"resume_after_seq": {
"default": 0,
"minimum": 0,
"title": "Resume After Seq",
"type": "integer"
},
"type": {
"const": "hello",
"default": "hello",
"title": "Type",
"type": "string"
},
"types": {
"anyOf": [
{
"items": {
"pattern": "^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$",
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Types"
}
},
"required": [
"protocol"
],
"title": "Hello",
"type": "object"
},
"InstallRule": {
"additionalProperties": false,
"description": "Install a stored rule: its first version, or the next version of an archived one.",
"properties": {
"provenance": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"default": {},
"title": "Provenance",
"type": "object"
},
"rule": {
"$ref": "#/$defs/Rule"
},
"type": {
"const": "install_rule",
"default": "install_rule",
"title": "Type",
"type": "string"
}
},
"required": [
"rule"
],
"title": "InstallRule",
"type": "object"
},
"JsonValue": {},
"NotFilter": {
"additionalProperties": false,
"description": "Envelopes that do not match ``filter``.",
"properties": {
"filter": {
"$ref": "#/$defs/Filter"
},
"kind": {
"const": "not",
"default": "not",
"title": "Kind",
"type": "string"
}
},
"required": [
"filter"
],
"title": "NotFilter",
"type": "object"
},
"OnFilter": {
"additionalProperties": false,
"description": "Envelopes whose event is one of ``types``.",
"properties": {
"kind": {
"const": "on",
"default": "on",
"title": "Kind",
"type": "string"
},
"types": {
"items": {
"pattern": "^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*$",
"type": "string"
},
"minItems": 1,
"title": "Types",
"type": "array"
}
},
"required": [
"types"
],
"title": "OnFilter",
"type": "object"
},
"Outcome": {
"discriminator": {
"mapping": {
"published": "#/$defs/PublishedOutcome",
"recorded": "#/$defs/RecordedOutcome",
"rule": "#/$defs/RuleOutcome",
"rule_version": "#/$defs/RuleVersionOutcome",
"run": "#/$defs/RunOutcome"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/PublishedOutcome"
},
{
"$ref": "#/$defs/RecordedOutcome"
},
{
"$ref": "#/$defs/RunOutcome"
},
{
"$ref": "#/$defs/RuleOutcome"
},
{
"$ref": "#/$defs/RuleVersionOutcome"
}
]
},
"Pattern": {
"discriminator": {
"mapping": {
"absence": "#/$defs/AbsencePattern",
"count": "#/$defs/CountPattern",
"each": "#/$defs/EachPattern",
"sequence": "#/$defs/SequencePattern"
},
"propertyName": "kind"
},
"oneOf": [
{
"$ref": "#/$defs/EachPattern"
},
{
"$ref": "#/$defs/CountPattern"
},
{
"$ref": "#/$defs/SequencePattern"
},
{
"$ref": "#/$defs/AbsencePattern"
}
]
},
"PredicateFilter": {
"additionalProperties": false,
"description": "Envelopes whose event the registered Python predicate ``name`` accepts.\n\nAn escape hatch for what the other filters cannot express. Predicates must be pure.",
"properties": {
"kind": {
"const": "predicate",
"default": "predicate",
"title": "Kind",
"type": "string"
},
"name": {
"minLength": 1,
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "PredicateFilter",
"type": "object"
},
"Publish": {
"additionalProperties": false,
"description": "Publish an event. Publishing an ``id`` already in the log appends nothing.",
"properties": {
"correlation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Correlation Id"
},
"event": {
"additionalProperties": true,
"properties": {
"type": {
"type": "string"
}
},
"required": [
"type"
],
"title": "Event",
"type": "object"
},
"id": {
"anyOf": [
{
"$ref": "#/$defs/EventId"
},
{
"type": "null"
}
],
"default": null
},
"type": {
"const": "publish",
"default": "publish",
"title": "Type",
"type": "string"
}
},
"required": [
"event"
],
"title": "Publish",
"type": "object"
},
"PublishedOutcome": {
"additionalProperties": false,
"description": "What publishing did: the event's position, and whether it was already there.",
"properties": {
"duplicate": {
"default": false,
"title": "Duplicate",
"type": "boolean"
},
"id": {
"$ref": "#/$defs/EventId"
},
"seq": {
"title": "Seq",
"type": "integer"
},
"type": {
"const": "published",
"default": "published",
"title": "Type",
"type": "string"
}
},
"required": [
"seq",
"id"
],
"title": "PublishedOutcome",
"type": "object"
},
"RecordedOutcome": {
"additionalProperties": false,
"description": "Where a fact the command recorded, such as feedback, was appended.",
"properties": {
"id": {
"$ref": "#/$defs/EventId"
},
"seq": {
"title": "Seq",
"type": "integer"
},
"type": {
"const": "recorded",
"default": "recorded",
"title": "Type",
"type": "string"
}
},
"required": [
"seq",
"id"
],
"title": "RecordedOutcome",
"type": "object"
},
"ReplayComplete": {
"additionalProperties": false,
"description": "Every event up to ``up_to_seq`` has been replayed; what follows is live.",
"properties": {
"type": {
"const": "replay_complete",
"default": "replay_complete",
"title": "Type",
"type": "string"
},
"up_to_seq": {
"title": "Up To Seq",
"type": "integer"
}
},
"required": [
"up_to_seq"
],
"title": "ReplayComplete",
"type": "object"
},
"ReplayRule": {
"additionalProperties": false,
"description": "Reset a rule to evaluate the log again, rebuilding its state or firing again.",
"properties": {
"from_seq": {
"default": 0,
"minimum": 0,
"title": "From Seq",
"type": "integer"
},
"mode": {
"default": "rebuild",
"enum": [
"rebuild",
"refire"
],
"title": "Mode",
"type": "string"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"type": {
"const": "replay_rule",
"default": "replay_rule",
"title": "Type",
"type": "string"
}
},
"required": [
"rule"
],
"title": "ReplayRule",
"type": "object"
},
"RetryPolicy": {
"additionalProperties": false,
"description": "How a failed run is retried: exponential backoff, up to ``max_attempts`` attempts.",
"properties": {
"backoff": {
"default": "PT1S",
"format": "duration",
"title": "Backoff",
"type": "string"
},
"max_attempts": {
"default": 5,
"minimum": 1,
"title": "Max Attempts",
"type": "integer"
},
"max_backoff": {
"default": "PT5M",
"format": "duration",
"title": "Max Backoff",
"type": "string"
},
"multiplier": {
"default": 2.0,
"minimum": 1.0,
"title": "Multiplier",
"type": "number"
}
},
"title": "RetryPolicy",
"type": "object"
},
"RetryRun": {
"additionalProperties": false,
"description": "Make a run runnable now.",
"properties": {
"run_id": {
"$ref": "#/$defs/RunId"
},
"type": {
"const": "retry_run",
"default": "retry_run",
"title": "Type",
"type": "string"
}
},
"required": [
"run_id"
],
"title": "RetryRun",
"type": "object"
},
"Rule": {
"additionalProperties": false,
"description": "When ``when`` holds for a scope, run ``then``.",
"properties": {
"description": {
"default": "",
"title": "Description",
"type": "string"
},
"enabled": {
"default": true,
"title": "Enabled",
"type": "boolean"
},
"name": {
"$ref": "#/$defs/RuleName_a-z__a-z0-9____a-z0-9___-_____"
},
"on_dead_letter": {
"default": "continue",
"enum": [
"continue",
"block"
],
"title": "On Dead Letter",
"type": "string"
},
"ordering": {
"default": "scope",
"enum": [
"scope",
"none"
],
"title": "Ordering",
"type": "string"
},
"retry": {
"$ref": "#/$defs/RetryPolicy",
"default": {
"backoff": "PT1S",
"max_attempts": 5,
"max_backoff": "PT5M",
"multiplier": 2.0
}
},
"scope": {
"$ref": "#/$defs/Scope",
"default": {
"fields": []
}
},
"start": {
"default": "now",
"enum": [
"now",
"beginning"
],
"title": "Start",
"type": "string"
},
"then": {
"$ref": "#/$defs/ActionRef"
},
"timeout": {
"anyOf": [
{
"format": "duration",
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Timeout"
},
"when": {
"$ref": "#/$defs/Condition"
}
},
"required": [
"name",
"when",
"then"
],
"title": "Rule",
"type": "object"
},
"RuleName": {
"type": "string"
},
"RuleName_a-z__a-z0-9____a-z0-9___-_____": {
"maxLength": 100,
"pattern": "^[a-z][a-z0-9_]*:[a-z0-9][a-z0-9._-]*$",
"type": "string"
},
"RuleOutcome": {
"additionalProperties": false,
"description": "A rule's progress after the command reset it.",
"properties": {
"progress": {
"$ref": "#/$defs/RuleProgress"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"type": {
"const": "rule",
"default": "rule",
"title": "Type",
"type": "string"
}
},
"required": [
"rule",
"progress"
],
"title": "RuleOutcome",
"type": "object"
},
"RuleProgress": {
"additionalProperties": false,
"description": "Where a rule is in a workspace's log, stored with its cursor.",
"properties": {
"cursor": {
"default": 0,
"minimum": 0,
"title": "Cursor",
"type": "integer"
},
"deadlines": {
"additionalProperties": {
"format": "date-time",
"type": "string"
},
"default": {},
"propertyNames": {
"$ref": "#/$defs/ScopeKey"
},
"title": "Deadlines",
"type": "object"
},
"definition": {
"default": "",
"title": "Definition",
"type": "string"
},
"generation": {
"default": 0,
"minimum": 0,
"title": "Generation",
"type": "integer"
},
"silent_through": {
"default": 0,
"minimum": 0,
"title": "Silent Through",
"type": "integer"
}
},
"title": "RuleProgress",
"type": "object"
},
"RuleVersionOutcome": {
"additionalProperties": false,
"description": "A stored rule's version after the command changed it, or found it changed already.",
"properties": {
"duplicate": {
"default": false,
"title": "Duplicate",
"type": "boolean"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"seq": {
"title": "Seq",
"type": "integer"
},
"type": {
"const": "rule_version",
"default": "rule_version",
"title": "Type",
"type": "string"
},
"version": {
"title": "Version",
"type": "integer"
}
},
"required": [
"rule",
"version",
"seq"
],
"title": "RuleVersionOutcome",
"type": "object"
},
"Run": {
"additionalProperties": false,
"description": "One firing's action: its status, attempts, and a graph's latest checkpoint.",
"properties": {
"attempts": {
"default": 0,
"title": "Attempts",
"type": "integer"
},
"checkpoint": {
"$ref": "#/$defs/JsonValue",
"default": null
},
"checkpoints": {
"default": 0,
"title": "Checkpoints",
"type": "integer"
},
"correlation_id": {
"title": "Correlation Id",
"type": "string"
},
"created_at": {
"format": "date-time",
"title": "Created At",
"type": "string"
},
"depth": {
"default": 0,
"title": "Depth",
"type": "integer"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Error"
},
"fired_seq": {
"title": "Fired Seq",
"type": "integer"
},
"id": {
"$ref": "#/$defs/RunId"
},
"matched": {
"items": {
"type": "integer"
},
"title": "Matched",
"type": "array"
},
"next_attempt_at": {
"format": "date-time",
"title": "Next Attempt At",
"type": "string"
},
"output": {
"$ref": "#/$defs/JsonValue",
"default": null
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Reason"
},
"rule": {
"$ref": "#/$defs/RuleName"
},
"scope": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"title": "Scope",
"type": "object"
},
"scope_key": {
"$ref": "#/$defs/ScopeKey"
},
"status": {
"default": "pending",
"enum": [
"pending",
"running",
"retrying",
"succeeded",
"dead",
"cancelled",
"skipped"
],
"title": "Status",
"type": "string"
},
"step": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Step"
},
"trace_ids": {
"default": [],
"items": {
"$ref": "#/$defs/TraceId"
},
"title": "Trace Ids",
"type": "array"
},
"updated_at": {
"format": "date-time",
"title": "Updated At",
"type": "string"
}
},
"required": [
"id",
"rule",
"scope_key",
"scope",
"fired_seq",
"matched",
"correlation_id",
"next_attempt_at",
"created_at",
"updated_at"
],
"title": "Run",
"type": "object"
},
"RunId": {
"type": "string"
},
"RunOutcome": {
"additionalProperties": false,
"description": "A run after the command changed it.",
"properties": {
"run": {
"$ref": "#/$defs/Run"
},
"type": {
"const": "run",
"default": "run",
"title": "Type",
"type": "string"
}
},
"required": [
"run"
],
"title": "RunOutcome",
"type": "object"
},
"RunTarget": {
"additionalProperties": false,
"description": "Feedback on one run: a workflow execution.",
"properties": {
"kind": {
"const": "run",
"default": "run",
"title": "Kind",
"type": "string"
},
"run_id": {
"$ref": "#/$defs/RunId"
}
},
"required": [
"run_id"
],
"title": "RunTarget",
"type": "object"
},
"Scope": {
"additionalProperties": false,
"description": "The event fields that partition a rule's state and ordering. None: the whole workspace.",
"properties": {
"fields": {
"default": [],
"items": {
"type": "string"
},
"title": "Fields",
"type": "array"
}
},
"title": "Scope",
"type": "object"
},
"ScopeKey": {
"type": "string"
},
"SequencePattern": {
"additionalProperties": false,
"description": "Fire when envelopes match ``steps`` in order, all within ``within`` of the first.\n\nAn envelope that matches the first step while a sequence is in progress (but not the step\nit is waiting for) starts the sequence again from it, so the latest start counts.",
"properties": {
"kind": {
"const": "sequence",
"default": "sequence",
"title": "Kind",
"type": "string"
},
"steps": {
"items": {
"$ref": "#/$defs/Filter"
},
"minItems": 2,
"title": "Steps",
"type": "array"
},
"within": {
"format": "duration",
"title": "Within",
"type": "string"
}
},
"required": [
"steps",
"within"
],
"title": "SequencePattern",
"type": "object"
},
"ServerFrame": {
"discriminator": {
"mapping": {
"command_result": "#/$defs/CommandResult",
"error": "#/$defs/ErrorFrame",
"event": "#/$defs/EventFrame",
"replay_complete": "#/$defs/ReplayComplete",
"welcome": "#/$defs/Welcome"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/Welcome"
},
{
"$ref": "#/$defs/EventFrame"
},
{
"$ref": "#/$defs/ReplayComplete"
},
{
"$ref": "#/$defs/CommandResult"
},
{
"$ref": "#/$defs/ErrorFrame"
}
]
},
"SkipRun": {
"additionalProperties": false,
"description": "Give up on a waiting or dead-lettered run.",
"properties": {
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Reason"
},
"run_id": {
"$ref": "#/$defs/RunId"
},
"type": {
"const": "skip_run",
"default": "skip_run",
"title": "Type",
"type": "string"
}
},
"required": [
"run_id"
],
"title": "SkipRun",
"type": "object"
},
"SourceActor": {
"description": "A system that publishes events, such as a monitoring service or a webhook.",
"properties": {
"kind": {
"const": "source",
"default": "source",
"title": "Kind",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "SourceActor",
"type": "object"
},
"SystemActor": {
"description": "reflexr itself (the reactor, schedules), or the application acting on its own behalf.",
"properties": {
"kind": {
"const": "system",
"default": "system",
"title": "Kind",
"type": "string"
},
"name": {
"default": "reflexr",
"title": "Name",
"type": "string"
}
},
"title": "SystemActor",
"type": "object"
},
"Throttle": {
"additionalProperties": false,
"description": "Allow at most ``at_most`` firings per scope in any period of ``per``.",
"properties": {
"at_most": {
"minimum": 1,
"title": "At Most",
"type": "integer"
},
"per": {
"format": "duration",
"title": "Per",
"type": "string"
}
},
"required": [
"at_most",
"per"
],
"title": "Throttle",
"type": "object"
},
"TraceId": {
"type": "string"
},
"UpdateRule": {
"additionalProperties": false,
"description": "Replace an active stored rule with a new version, reset if its condition or scope changed.",
"properties": {
"expected_version": {
"anyOf": [
{
"minimum": 1,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Expected Version"
},
"provenance": {
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"default": {},
"title": "Provenance",
"type": "object"
},
"rule": {
"$ref": "#/$defs/Rule"
},
"type": {
"const": "update_rule",
"default": "update_rule",
"title": "Type",
"type": "string"
}
},
"required": [
"rule"
],
"title": "UpdateRule",
"type": "object"
},
"UserActor": {
"description": "A person, identified by the host application's user id.",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"kind": {
"const": "user",
"default": "user",
"title": "Kind",
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Name"
}
},
"required": [
"id"
],
"title": "UserActor",
"type": "object"
},
"Welcome": {
"additionalProperties": false,
"description": "The server's answer to ``hello``.",
"properties": {
"head_seq": {
"title": "Head Seq",
"type": "integer"
},
"protocol": {
"default": "reflexr.v1",
"title": "Protocol",
"type": "string"
},
"reset": {
"default": false,
"title": "Reset",
"type": "boolean"
},
"type": {
"const": "welcome",
"default": "welcome",
"title": "Type",
"type": "string"
},
"workspace_id": {
"$ref": "#/$defs/WorkspaceId"
}
},
"required": [
"workspace_id",
"head_seq"
],
"title": "Welcome",
"type": "object"
},
"WhereFilter": {
"additionalProperties": false,
"description": "Envelopes whose event field ``field`` compares with ``value`` by ``op``.\n\nA missing field matches nothing, except ``exists`` with ``value=False``.",
"properties": {
"field": {
"pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$",
"title": "Field",
"type": "string"
},
"kind": {
"const": "where",
"default": "where",
"title": "Kind",
"type": "string"
},
"op": {
"enum": [
"eq",
"ne",
"lt",
"le",
"gt",
"ge",
"in",
"contains",
"matches",
"exists"
],
"title": "Op",
"type": "string"
},
"value": {
"$ref": "#/$defs/JsonValue",
"default": null
}
},
"required": [
"field",
"op"
],
"title": "WhereFilter",
"type": "object"
},
"WorkspaceId": {
"type": "string"
}
},
"$id": "https://github.com/alexnodeland/reflexr/schemas/reflexr.v1.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Frames of the reflexr.v1 workspace protocol: `client` is any frame a client sends, `server` any frame a server sends.",
"properties": {
"client": {
"$ref": "#/$defs/ClientFrame"
},
"server": {
"$ref": "#/$defs/ServerFrame"
}
},
"title": "reflexr.v1",
"type": "object"
}