Contracts

Enumerations Reference

Status: v1.41 (living document — updated as enums evolve) Owner: datapipelines.co core Purpose: Single source of truth for every enum value used across the system. Prevents spelling drift across specs and across the codebase.


How to use this document

  • Every enum used in datapipelines.co is cataloged here.
  • Specs may inline enum values for readability, but this document is the authoritative reference.
  • If a value appears in code or a spec that doesn't match this document, the spec/code is wrong.
  • When adding a new enum value: add it here first, then propagate to specs and code.
  • Values are additive-only per the stability promises in individual specs.
  • Each enum has exactly ONE authoring spec (see the cross-reference table). Other specs are consumers.
  • Values marked (reserved) are registered for future use — they MUST NOT appear in generated code or be accepted by validators in v1.

Case & serialization convention

The strings cataloged here are the wire values — what appears in JSON payloads, exactly as written (case included). Kotlin enum classes use UPPER_SNAKE_CASE constants with an explicit mapping to the wire value:

enum class WriteMode(@JsonValue val wire: String) {
    REPLACE("replace"),
    APPEND("append");

    companion object {
        @JsonCreator @JvmStatic
        fun fromWire(v: String) = entries.firstOrNull { it.wire == v }
            ?: throw IllegalArgumentException("Unknown WriteMode: $v")
    }
}

Where the cataloged value is already UPPER (DQL, POSTGRES, SUCCESS), wire and constant coincide. Never rely on default Enum.name serialization for lowercase/kebab/snake wire values — the explicit @JsonValue mapping is mandatory so the catalog string stays the single source of truth.


1. LogicalType — canonical data types

Source: Type System §3 Used by: every spec — this is the foundational type vocabulary.

Value Wire Description
NULL null All-null column; type could not be inferred
BOOLEAN boolean Two-valued logic: true / false / null
INTEGER number Exact integer, int32 range (≤ 2^31 − 1)
BIGINTEGER string Exact integer, int64 range (≤ 2^63 − 1). Exceeds IEEE 754 double safe integer range.
DECIMAL number Numeric with precision ≤ 15. Scale present = exact origin; scale omitted = approximate origin (REAL → DECIMAL(7), DOUBLE → DECIMAL(15)) — see Type System §3.4
BIGDECIMAL string Numeric with precision > 15 (or unbounded — precision omitted)
STRING string Variable-length text. Includes source UUIDs, JSON, XML, enums, intervals.
BINARY string (base64) Variable-length bytes
DATE string (ISO 8601 date) Calendar date, no time
TIME string (ISO 8601 time) Time of day, no date, no timezone
TIMESTAMP string (ISO 8601 datetime, UTC) Date and time, normalized to UTC on ingest

Excluded from parameters declarations: NULL (only the other 10 may be parameter types).


2. NodeType — pipeline node SQL category

Source: Pipeline Contract §4.6 Used by: pipeline-contract, dag-executor.

Value Description
DQL Data Query Language — SELECT. Produces a ResultSet. May have an output block (tempdb / caller / datasource).
DML Data Manipulation Language — INSERT, UPDATE, DELETE, MERGE. Produces a row count. No output block.
DDL Data Definition Language — CREATE, ALTER, DROP, TRUNCATE. Produces success/failure. No output block.
PIPELINE Executes another pipeline as a child execution (pipeline composition). Carries a pipeline ref {name, version}, never source/template; may carry an output block only when the pinned child has a caller node (Pipeline Contract §4.9, §8.5).
CALCULATOR Evaluates a catalog calculator and writes ONE typed value — or, on a multi-output kind (121), a named set of them — into the execution Context. Carries kind, inputs and context_key (single) or context_keys (multi — never both, never neither), never source/template/output — it runs no SQL and produces no table (Pipeline Contract §4.10, Calculators).
SCRIPT Evaluates a pinned jsonata/javascript template as a pure function over staged data and the Context. Carries template, inputs, output (row/table modes) or context_key (value mode) and strict, never source — tempdb-only by construction (Pipeline Contract §4.12, DAG Executor §6.3).

Reserved for future: EXPRESSION, HTTP (non-SQL node types — see ROADMAP).


3. OutputTarget — where a DQL node's ResultSet goes

Source: Pipeline Contract §4.7 Used by: pipeline-contract, dag-executor, staging.

Value Required fields Description
tempdb table Stage ResultSet into in-memory tempdb table for downstream nodes to query.
caller (none) Return ResultSet as the pipeline's result. Default if the output block is omitted. At most one node per pipeline may resolve to caller; zero is legal (pure write-back pipelines emit no data_ready).
datasource datasource, table, mode Stream ResultSet to an external datasource's table.

Reserved for future: kafka, s3, email, webhook (see ROADMAP).


4. WriteMode — for output.target: "datasource"

Source: Pipeline Contract §4.7 Used by: pipeline-contract, dag-executor.

Value Description
replace TRUNCATE (or DELETE) + INSERT in one transaction
append INSERT only; existing rows preserved

5. Dialect — supported source database dialects

Source: Type System §5 (single authority) Used by: datasources (driver dispatch), templates (template targets a dialect), pipeline-contract (validation: template dialect must match datasource dialect).

Value JDBC driver
POSTGRES org.postgresql:postgresql (bundled)
ORACLE com.oracle.database.jdbc:ojdbc11 (optional -Poracle profile)
MSSQL com.microsoft.sqlserver:mssql-jdbc (bundled)
MYSQL com.mysql:mysql-connector-j (optional -Pmysql profile)
H2 com.h2database:h2 (bundled; also used for staging)
DUCKDB org.duckdb:duckdb_jdbc (bundled)
SQLITE org.xerial:sqlite-jdbc (bundled)
LAKE org.duckdb:duckdb_jdbc (bundled) — object storage read in place (Parquet/Iceberg); DuckDB is the engine, with a different §5.6 posture from DUCKDB. See Datasources §4.1.

Reserved for future: SNOWFLAKE, BIGQUERY, REDSHIFT (see ROADMAP).


5A. CredentialKind — what a datasource's stored credential IS

Source: Datasources §3.4 (single authority) Used by: datasources (validation, pool build, encryption), rest-api (POST /api/v1/datasources), mcp-server (datasources_get reports the kind; there is no MCP create — MCP Server §6.2.22), metadata-db (datasources.credential_kind).

Wire values are lowercase snake_case, so the @JsonValue mapping is explicit per the case convention above.

Value Wire username secret Description
PASSWORD password required required on create A database login. The default when a payload names no kind, and what the legacy top-level username/password pair means.
TOKEN token optional required on create A bearer or personal access token, placed where the pinned driver wants it by the adapter.
PRIVATE_KEY private_key absent required on create A PEM private key. Catalogued for the reference targets; no shipped dialect accepts it yet.
SERVICE_ACCOUNT_JSON service_account_json absent required on create One service-account JSON document. Catalogued for the reference targets; no shipped dialect accepts it yet.
NONE none absent absent Nothing is stored — an IAM role, OS auth, or an embedded file database with no authentication.

These are not (reserved) values in the enums.md sense: all five are accepted by the validator and stored by the column. What a shipped dialect can USE is narrower and is the adapter's declaration (supportedCredentialKinds, Datasources §3.4) — a kind outside a dialect's set is refused at save with datasource.validation.properties_invalid, never stored and ignored.


6. TemplateEngine — template language

Source: Templates §4 Used by: templates (engine dispatch).

Value Description
freemarker Apache Freemarker template engine — the engine of sql and html templates.
none No rendering engine — the engine of the script types (jsonata, javascript), whose body is evaluated by the scripting engine seam as a pure function of its input (transform-nodes design §2.1, since 7b). The pairing is enforced both ways: engine must match the template's type (template.validation.engine_unsupported).

Reserved for future: pebble, handlebars, thymeleaf-sql (see ROADMAP).


6A. TemplateType — template kind

Source: Template Hierarchy §5; the script types: transform-nodes design §2.1 (7b) Used by: templates (engine-configuration dispatch, type/dialect consistency), pipeline-contract (reference legality).

Value Description
sql The template renders SQL for pipeline nodes. Requires a dialect. Default, and the only kind that existed before 2026-09-02 (046) — every stored template backfilled to it.
html The template renders HTML through a second, auto-escaping engine configuration (design §6). Declares no dialect; no pipeline node may reference it (pipeline.validation.template_type_mismatch).
jsonata A script: the body is one JSONata expression evaluated as a pure function of its input object (7b). engine: none, no dialect, no imports, is_library false; the version carries contract / invariants / tests blocks inside its body_hash.
javascript A script in the GraalJS isolate. The body stores the statements of main(input); save runs its cases on the pool. Absent isolate → script.js.unavailable (D-T16; #8 lane 8b).

Fixed at template create and identical on every version of a template (template.validation.type_immutable). Serialization is the lowercase wire value ("sql" / "html" / "jsonata" / "javascript"), per the case convention above. There are no reserved future values — component sub-typing (kpi, aggrid, svg, form, …) belongs to the future dashboard abstraction and is deliberately absent here (design §2).


7. StagingEngine — tempdb engine

Source: Pipeline Contract §5 Used by: pipeline-contract (settings.tempdb.engine), staging (factory dispatch), dag-executor (instantiation).

Value Description
H2 In-memory H2 database. Default. Only supported engine in v1.

Reserved for future: DUCKDB (in-memory DuckDB — better for analytical workloads; see ROADMAP).

Declaration reality (2026-08-08). The frozen module dependency graph (module-structure §4.2) makes the "pipeline-contract authors, staging consumes" line above impossible: staging depends only on typesystem, so it cannot see a type declared in pipeline-contract. In v1 the enum has a single value (H2), so pipeline-contract declares it (for the settings.tempdb.engine wire value, with @JsonValue) and staging declares an identical local enum class StagingEngine { H2 } for StagingFactory dispatch; dag (which depends on both) maps between them — trivial while there is one value. Consolidation is a P4/dag decision: when a second engine (DUCKDB) lands, move StagingEngine into typesystem (the shared lower layer, exactly as Dialect resolved the same class of problem) so there is one authority. Until then the duplication is bounded to a single constant and dag owns the mapping.


8. UserKind — what a users row is

Source: Auth §4.7; metadata-db §4.1 (users.kind, V34, CHECK chk_users_kind) Used by: auth (UserKind, User.isHuman, UserService, UserRepository), the session filter, every user-administration route, ActorNames (history's "<key name> (API key)").

Value Description
human A person — the only kind that can log in, be linked to an OIDC identity, hold a membership, be invited or be administered on the admin users page
service An endpoint or server key's own identity (#215, PK5): provider key, email <key id>@keys.invalid, no password, never an admin. Created and deactivated with its key; managed only through it
system The System service account (auth §4.5, R7) — the actor for writes no human made; nothing authenticates as it

One predicate — kind = 'human' — guards every login, linking and administration path (auth §4.7). V34 backfilled system on the System row and human on every other; the wire and database token is the lowercase name.

(Until #215 slice (b) this section was Scope, the API-key credential axis; scopes were removed — see §8D.)


8B. Permission — an action a role may perform (was Capability)

Source: Auth §7.6 — the catalog table is the single authority (the value set, the roles each value admits, the surfaces placed on it); the permissions and keys record §2 (#215, ratified 2026-09-23) is its design. Used by: auth (Permission, RolePermissions, ScopeMatrix.allowed, AuthenticatedPrincipal.holds), every REST handler (@RequiredScope(Permission.X)), every MCP tool (its catalog entry, McpToolCatalog.Entry.permission), and the service checks that ask for one (execution.read_all, server_key.create, …).

Since #215 slice (a) a permission is a <functionality>.<permission> catalog value — pipeline.read, template.release, workspace.members.manage — one per piece of functionality a person can take separately (PK1); 65 on this version. The wire form is the dotted token; the Kotlin constant is its upper-snake spelling (PIPELINE_READ). The seven coarse values of v1.16 (view, execute, author, promotion_read, promote, ws_admin, super_admin) are retired: every one maps onto the catalog values whose surfaces it covered, with each surface's roles unchanged. The values are not restated here — a second list is a second thing to keep true; read them in auth §7.6.

It is the ROLE axis. It travels with a membership, not with a credential: the same person is a viewer in one workspace and an author in another. Not a hierarchy: the roles are not a chain, so the ONE role table (RolePermissions) lists each role's permissions outright. A super admin (users.is_admin) holds every value in every workspace (D7) except the two fenced promotion-receiving values, which no member role holds — only the promotion_receiver key role (§8D) does. Code asks whether a principal holds a permission; it never compares role names (PK1).

One axis since slice (b): scopes were removed (#215, PK8), so a permission is held by ROLES only — the four member roles and super admin (the §7.6 member columns), and the two key roles (§8D).


8C. WorkspaceRole — the ONE role a membership holds

Source: Auth §11A; metadata-db §4.12 (workspace_members.role, V29) and §4.17 (workspace_invitations.role) Used by: auth (WorkspaceRole, the role table RolePermissions), the REST role field of the workspace surface (rest-api §17), the members dropdown (ui-screens §4.13).

Exactly one per membership (roles design D1, 2026-09-20). Super admin is NOT a value: it is users.is_admin, a property of the user, held in every workspace. The wire and database token is the lowercase name; the UI prints it with a space (workspace admin).

Value Description
viewer Reads the workspace and executes pipelines (D3). Nothing else
author Viewer + creates, edits, releases, switches, publishes, registers lake tables, records facts (D4, D8)
promoter The ops role (D5): reads datasources and (R2) released-and-newer objects; promotes. Executes nothing, reads no executions, authors nothing, releases nothing
workspace_admin Everything in the workspace, members and promotion included (D6)

V23's three additive booleans (author / promoter / admin) were folded back into this value by V29 with the precedence admin → workspace_admin, else promoter → promoter, else author → author, else viewer; the database CHECK (chk_workspace_member_role, chk_workspace_invitation_role) keeps the set closed.


8A. ApiKeyKind — what an API key IS

Source: Auth §7.7 Used by: auth, web, mcp-server, persistence (api_keys.kind).

Value Description
mcp (V37, renamed from user — A19; kind is the transport and the word says which) The agent's key: CREATED on the Keys page (A15 — the login mint is retired) by someone holding mcp_key.create, with the role chosen at creation under the subset rule (A14); acts as its own service identity holding that member role (A13); presented on /mcp and nowhere else (#215 B2)
endpoint A credential for published endpoints only: acts as its own identity with the api_caller role (§8D), workspace-pinned, and serves exactly the endpoints its bindings cover plus the metadata and result cursor of executions it started
server The promotion peer's credential (091): created by a super admin, presented as DP-Promotion-Key by a SENDING deployment, and accepted on the promotion receiver's routes and nowhere else. Acts as its own identity with the promotion_receiver role (§8D), for any workspace (B6)
dashboard (L5, #367, V45) An external application's credential for the dashboard runtime: created on the Keys page by an api_key.create holder, acts as its own identity with the dashboard_viewer role (§8D), presented on the runtime and refreshes routes of the dashboards its dashboard_key_bindings folders cover and NOWHERE ELSE — refused centrally everywhere else with endpoint.key_kind_refused (details.reason dashboard_key_off_surface)

A kind answers "where may this credential be presented?"; its ROLE (§8D, or the chosen member role for the mcp key) answers "what may it do there?". Each kind carries exactly one role — the database CHECK (chk_api_keys_role) makes it a fact. The wire form is the lowercase name.

A server key authenticates nothing outside the promotion routes. Presented as an ordinary DP-API-Key it is refused on every route — REST, htmx partials, /mcp and every UI page — with endpoint.key_kind_refused. Same rule as the endpoint kind, different family.

An endpoint key with no binding on any ancestor of the path it presents at authorises nothing. The absence of a binding is never a fall-through (since #215 B3 an unbound path serves no key at all); if it were, publishing a new endpoint would silently widen every existing endpoint key's reach at the moment of publication.

A dashboard key with no binding on any ancestor of the dashboard's name serves nothing (L5, R-EP2 verbatim): an unbound key, a key bound elsewhere and a key of another workspace all get the dashboard family's ordinary 404, and a deeper binding REPLACES an inherited one.


8D. KeyRole — the role a key carries

Source: Auth §7.5 and the key-role columns of the §7.6 catalog; the permissions and keys record §3.2 Used by: auth (KeyRole, RolePermissions.of(KeyRole), AuthenticatedPrincipal.keyRole), persistence (api_keys.role, V34, CHECK chk_api_keys_role), the REST role field of key creation (rest-api §16.1), the Keys page's Role column.

Value Carried by Permissions
author an mcp key CHOSEN at creation (A13/A14 — the subset rule decides which member roles a creator may offer) the §7.6 mcp:author column — the author member's permissions, exactly
promoter an mcp key CHOSEN at creation the §7.6 mcp:promoter column — the promoter member's permissions, exactly
workspace_admin an mcp key CHOSEN at creation the §7.6 mcp:workspace_admin column — the workspace-admin member's permissions, exactly
api_caller every endpoint key endpoint.serve (the paths bound to the key), execution.read and execution.result.read (the executions it started)
promotion_receiver every server key (and the deprecated config-value peer) promotion.inventory.read, promotion.push — for any workspace (B6)
dashboard_viewer (L5, #367) every dashboard key dashboard.read (the BINDINGS are the lens — dashboard_key_bindings' folders; unbound = unservable) and dashboard.execute, on the runtime routes only

Pre-created and fixed: the three member roles are the ONLY roles an mcp key can carry (A15 — viewer is never a key role; B1 — super_admin is neither offerable nor acceptable), and no key role holds workspace admin or super admin AUTHORITY over people (PK3). snake_case everywhere (A5); the UI prints it with a space (api caller, workspace admin).


9. NodeStatus — per-node execution outcome

Source: DAG Executor §7 Used by: dag-executor (node_stats), rest-api (response envelope).

Value Description
SUCCESS Node completed without error
FAILED Node threw an exception; pipeline aborted
ABORTED Node never started because a dependency failed
RUNNING The node is executing right now. Appears only in the LIVE progress snapshot a RUNNING execution's row carries (108 §D, Metadata DB §8.3); a TERMINAL snapshot never contains it — a node that had started and never reported is ABORTED there, which is DAG Executor §7.2's row and is unchanged. A node that has not started yet is ABSENT from the live snapshot rather than given a status

10. ExecutionStatus — whole-pipeline execution outcome

Source: REST API §6.4, DAG Executor §5 Used by: rest-api, dag-executor, mcp-server, persistence (pipeline_executions.status).

Value Description
RUNNING Execution in progress
SUCCESS All nodes completed; result returned
FAILED A node failed; execution aborted
ABORTED Execution cancelled: client disconnect beyond sse.disconnect-grace-seconds, explicit DELETE /api/v1/executions/{id}, server shutdown, or the crash sweep (Metadata DB §8)

Reserved for future: PARTIAL (partial-result mode where some nodes succeeded but a non-critical path failed — see ROADMAP).

Declaration reality (2026-08-10). Declared in the dag module, for the same layering reason SseEventType is (§11): web implements rest-api at layer 5 and depends on dag at layer 3, never the reverse (module-structure §4.2). The executor is what produces the status and what writes pipeline_executions.status, so the enum lives at the lowest layer that needs it and web consumes it. This document, rest-api and metadata-db remain the wire authorities — the declaration site is an implementation consequence, not a change of ownership.


11. SseEventType — pipeline execution event types

Source: REST API §6.4, DAG Executor §10 Used by: rest-api (SSE endpoint), dag-executor (event emitter), mcp-server (event forwarding).

Value Emitted when Order
execution_started Execution begins First event, exactly once
node_started A node begins executing After its dependencies completed
node_progress A measured sample of the node's operation — state, destination, cumulative counts, per-state wall time (REST API §6.4.9) Zero or more, strictly between the node's node_started and its node_completed/node_failed; never terminal
node_completed A node finishes successfully After matching node_started
node_failed A node fails After matching node_started; pipeline then halts
pipeline_completed All nodes succeeded; final result imminent After all node_completed
pipeline_failed Execution halts due to node failure After the matching node_failed
execution_aborted Execution cancelled (explicit DELETE, disconnect grace elapsed, shutdown) Terminal; replaces pipeline_completed/pipeline_failed
data_ready Result stored; payload carries schema, inline first page, and result_url cursor Last event when a caller node exists; follows pipeline_completed

Order guarantees: see REST API §6.5.

Reserved for future: data_chunk (streaming row chunks for incremental processing — see ROADMAP).


12. ResultDelivery — REMOVED (v1.1)

Removed 2026-08-07 (SPEC-REVIEW-2026-08, decision D9). The inline-vs-claim-check split no longer exists: every caller result is stored in Redis and data_ready always carries schema + inline first page + result_url cursor. See REST API §7. Section number retained so later sections keep their numbering.


13. ResultFormat — wire format for result data

Source: REST API §7 Used by: rest-api, mcp-server.

Value MIME type Description
json application/json JSON array-of-arrays with separate schema. Default.
arrow application/vnd.apache.arrow.ipc Apache Arrow IPC binary stream with embedded schema. Efficient for large analytical clients.
csv text/csv CSV with header row. All values as their wire-encoded strings.

14. SslMode — datasource TLS mode

Source: Datasources §5 Used by: datasources (PG-idiomatic; analogous config for other dialects).

Value Description
disable No TLS
prefer TLS if available, plain if not
require TLS required; certificate not verified
verify-ca TLS required; CA verified
verify-full TLS required; CA + hostname verified (recommended for production)

15. AuthAuditEvent — auth audit log events

Source: Auth §10.1 Used by: auth, observability.

Value Trigger
auth.login.success Login succeeded, JWT issued (OIDC or local — the details' provider names the method)
auth.login.domain_not_allowed User's email domain not in allowlist
auth.login.user_inactive User account is deactivated (OIDC or local — same event)
auth.login.oidc_error OIDC provider returned an error
auth.login.email_verified_assumed OIDC login accepted WITHOUT an email_verified claim under the provider's trust-email-without-verified-claim knob — the acceptance was configuration's decision, not the IdP's vouching (#187, Auth §4.2)
auth.login.identity_mismatch OIDC login refused: the email's stored identity belongs to a different sign-in — nothing was updated; details name the two PROVIDERS, never the subjects (#187, Auth §4.2)
auth.login.bad_credentials Local login failed: unknown email, OIDC-only account, or wrong password — deliberately indistinguishable (Auth §5A.5)
auth.login.locked Local account locked after lockout.max-failures consecutive failures (Auth §5A.3)
auth.password.seeded Config seeded the bootstrap admin's one-time local credential (Auth §5A.2)
auth.password.changed User changed their own password (self-service or forced, Auth §5A.4)
auth.password.reset Admin reset a user's password — new one-time credential (Auth §5A.1)
auth.password.disabled Admin disabled a user's local access — account is OIDC-only (Auth §5A.1)
auth.password.change_failed Self-service password change failed (wrong current password) — counted on the same §5A.3 lockout counter as POST /login (Auth §5A.4)
auth.password.change_locked Self-service change refused because the account's §5A.3 lockout was already engaged — consulted before any Argon2 work (Auth §5A.4)
auth.user.created Admin created a local account (Auth §5A.1; details carry the acting admin)
auth.user.unlocked Admin cleared a local account's lockout (Auth §5A.3)
auth.logout User logged out (cookie cleared)
auth.api_key.created New API key issued
auth.api_key.revoked API key revoked
auth.api_key.revoked_by_admin A workspace admin or super admin revoked a member's login-minted key — with the membership's removal (reason: member_removed) or as an explicit act that keeps the member (reason: admin_revoked; the member's session keeps working, the next sign-in mints fresh) (#200, Auth §7.4)
auth.api_key.used API key validated (sampled 1/100)
auth.api_key.rejected API key validation failed
auth.scope.denied Request refused by the authorization layer — a role that lacks the permission, an undeclared handler, or a key off its kind's surface (the event keeps its historical name)
auth.user.deactivated Admin deactivated a user
auth.user.activated Admin reactivated a user
auth.user.admin_granted Admin granted admin scope to user
auth.user.admin_revoked Admin revoked admin scope from user
auth.user.identity_reset Admin reset a user's linked sign-in identity to the bootstrap placeholder so the next OIDC sign-in with that email claims the row — the explicit answer to auth.login.identity_mismatch (#187, Auth §4.2)
auth.workspace.created Workspace created — by a super admin through the service path, or by the boot seeder for demo (details carry actor: system)
auth.workspace.updated A workspace's display name was changed
auth.workspace.deleted A workspace was soft-deleted (empty only) — distinct from workspace.deactivated, which purges nothing
auth.workspace.stranded_content Content committed into a workspace concurrently with its deletion and is now invisible with its name held — the detector, not a refusal
auth.workspace.header_rejected DP-Workspace presented on an API-key request
auth.super_admin_acting A super admin acted in a workspace they hold no explicit membership in (RBAC design D-R8). Emitted at the scope interceptor's one choke point on every governed handler, READS INCLUDED — the 404 rule's whole promise is that a workspace is invisible from outside, and the one principal exempt from it is the one whose reads most need to be on the record. details carry the operation, the workspace, the path and acting_via: super_admin
workspace.member_added A member was added, with their role (details.role). The first-login demo join carries reason: first_login_demo_viewer
workspace.member_invited An invitation was created — or an existing one's role replaced by a re-invite (the latest admin decision wins, audited every time; Auth §4.6). details carry workspace, email, role
workspace.invitation_revoked A pending invitation was revoked (Auth §4.6). details carry workspace and email
workspace.invitation_materialised A pending invitation became a real membership at login — the inviter's decision being executed by the invitee's first sign-in (Auth §4.6). details carry workspace, email, role and inviter
workspace.member_removed A member was removed
workspace.member_role_changed A member's role was replaced (D1, 2026-09-20; was workspace.member_flags_changed) — details.from and details.to carry both roles, because a membership row keeps no history of its own
workspace.deactivated A workspace was deactivated (RBAC design D-R10). Nothing it owns is purged; the five effects follow from readers consulting its state
workspace.reactivated A deactivated workspace was restored
datasource.granted A datasource was granted to a workspace (D-R7) — the verb that decides who can see a live database credential's data. details.already_granted distinguishes a new grant from an idempotent re-grant
datasource.revoked A datasource's grant to a workspace was removed. The datasource itself is untouched
endpoint.served A published endpoint served a request (074). Details carry the endpoint id, the execution id and the outcome; the key id is the row's own key_id. This row is also what proves an endpoint key may read that execution's result
endpoint.key_bound An API key was bound to a node of the endpoint tree
endpoint.key_unbound An API key's binding to a node was removed
dashboard.key_bound (L5, #367) A dashboard key was bound to a folder of the dashboard name space; details.name_prefix and workspace_id
dashboard.key_unbound (L5, #367) A dashboard key's folder binding was removed; details.name_prefix
endpoint.published An endpoint was published over a released pipeline
endpoint.unpublished An endpoint was deleted

Password and lockout events exist only for the optional local accounts (Auth §5A); an OIDC-only deployment never writes them. No event in this table ever carries credential material.

Datasource audit events (same audit_log table, defined in Datasources §7.4):

Value Trigger
datasource.pool_build Credential decrypted to build a connection pool
datasource.pool_rebuild Update-triggered eviction of a live pool; carries the initiating operator (the decryption itself is the subsequent pool_build) — see Datasources §7.4
datasource.connection_test Explicit connection test (POST .../test)
datasource.key_rotation Master-key rotation re-encryption pass

Promotion audit events (same audit_log table, defined in Versioning §10):

Value Trigger
auth.promotion.rejected A request on /api/v1/promotion/** was refused by the peer-credential gate — wrong key, missing key, or no key configured on this receiver. details carries the path and a reason of key_mismatch or no_key_configured; it never carries the credential (Versioning §10.6)
auth.promotion.accepted A promotion batch was applied by the receiver, in one transaction. The rejected/accepted pair is the promotion CHANNEL's two outcomes, which is why both sit in the auth domain beside the credential that gates them. details carries source_env (the sender's deployment.name), key_fingerprint (a truncated SHA-256 of the key presented, never the key), the target workspace, and the template/pipeline counts. The actor is the system service account every promoted row is stamped with (Auth §4.5)

MCP audit events (same audit_log table, defined in MCP §14):

Value Trigger
mcp.tool.called Every MCP tool call, success or failure — tool, actor (key id + owner), target, outcome, elapsed_ms, correlation id. Emitted by the dispatcher for every outcome including the §7.6 scope refusal (MCP §14)
mcp.tool.write Every call to a tool the MCP catalog declares mutating — a write to stored definitions or to customer data, pipelines_execute_node node runs included — one event after the tool returns, on success and on failure (the failure carries the error code). The trace that an author-scoped key exercised a write path (MCP §14)
mcp.execution.launched One per pipelines_execute launch (107), emitted BEFORE the blocking run begins — key id, correlation id, pipeline/execution id. executions_cancel's same-credential rule joins on it, because the dispatcher's end-of-call mcp.tool.called row cannot exist while the launching call is still blocking (MCP §14)
mcp.resource.read Every resources/read, success or failure (120) — emitted at McpResourceReader.read, the one place every read passes through. details carries uri (never the content), outcome, the failure's code as a name (resource_not_found | forbidden | internal_error), elapsed_ms and the correlation id (MCP §14)

Version lifecycle audit events (same audit_log table, defined in Versioning §3; emitted by the 101 REST verbs, all session-only — an API key cannot produce one):

Value Trigger
pipeline.version.discarded A RELEASED pipeline version was discarded (POST /pipelines/{id}/versions/{v}/discard). details carries the pipeline id, version, and the pointer before/after (Versioning §3.1)
pipeline.version.restored A DISCARDED pipeline version was restored to RELEASED. details carries the pointer before/after — restore moving the pointer is the interesting case (Versioning §3.1)
pipeline.version.purged A DRAFT pipeline version was purged — the row and its executions deleted, irreversible. details carries the execution-row count that went with it (Versioning §3.1)
pipeline.purged A pipeline ENTITY was purged (only-draft, no inbound edges) — the entity row went with its draft. details carries exclusive_draft_templates offered/purged and, since #320, kept_draft_templates — the ids of the draft-only templates the offer skipped because a parameter set or a visualization also pins them (Versioning §3.2, §3.5.3)
pipeline.current_switched The sticky pointer was moved by the manual switch verb (POST /pipelines/{id}/current) — including on a promotion receiver, where this is the rollout/rollback lever. details carries from/to versions (Versioning §3.4)
pipeline.version.released A DRAFT pipeline version was RELEASED — the D4 human step, on every surface that offers it (REST POST /pipelines/{id}/release, the explorer dialog, the editor). details carries pipeline_id, pipeline_name, version, and via (session or api_key; key_id is on the row) — the record of WHO released and THROUGH WHAT (Versioning D4). Added at T187 (2026-09-10): releases were the one lifecycle verb 101 left unaudited. Since 140, a release past failing checks also carries checks_overridden: [check ids] and override_reason — the escape hatch is on the record, never silent. Since 142 it always carries templates_released: [{template_id, version}] — the DRAFT template versions the release cascaded to with the promoter's consent (versioning §5.3), an empty list when none. Since #517 one row per actual flip: a release that flips dependencies writes one *.version.released row per flipped version — dependencies first in release order, the root last — strictly, in the release's own transaction (auth §10.1A). Three detail keys join every such row: scope (the word version: the row records one version's flip), release_id (one id shared by every row of the same release) and, on a cascaded dependency's row, cascade_root: {family, id, version} (the release's root); the root's row adds dependencies_released: [{family, name, version}] (an empty list when nothing cascaded). A child pipeline the release flipped writes its own pipeline.version.released with these keys
template.version.released The template twin of pipeline.version.released: template_id, version, via. Since 142 a release the PIPELINE cascade made (versioning §5.3) carries the same shape plus cascade_from_pipeline_id and cascade_from_version — "who released template X v2 and why" reads off this one event. Since #517 a cascaded row also carries scope, release_id and cascade_root (the pipeline.version.released row above).
visualization.exported / dashboard.exported A visualization or dashboard CURRENT release was exported (`GET /api/v1/visualizations
visualization.imported / dashboard.imported A visualization or dashboard import LANDED (`POST /api/v1/visualizations
visualization.test.screenshot_uploaded The visualization test workflow's ONE screenshot was stored (POST /api/v1/visualizations/{id}/tests/sessions/{sessionId}/screenshot, #353; audited by #164, the one write an agent makes over REST). The route reads no principal — its only credential is the single-use upload capability — so user_id is the run's STARTER (started_by) and key_id is null; the source IP is on the row. details carry workspace_id, visualization_id, run_id, media_type, size_bytes and, when the upload named one, case — never the token, its hash or the image bytes. A refused upload (a wrong, consumed or expired capability, an invalid image) stores nothing and writes no row
template.version.discarded / template.version.restored / template.version.purged / template.purged / template.current_switched The template twins, by name — same triggers, template surfaces (Versioning §3.5's notation rule)
parameter_set.version.discarded / parameter_set.version.restored / parameter_set.version.purged / parameter_set.purged / parameter_set.current_switched The parameter-set twins (#332), by name — same triggers, rest-api §21's surfaces (the draft purge is POST .../draft/discard, the pipelines spelling). details carry the set's id and name and the version — and, since #372, the pipelines mould's full shape on every verb: the pointer pair current_version_before/current_version_after on a discard AND a restore, from/to on a switch, and scope (entity when the sole draft took the set, version otherwise) on every purge — the values come from the verbs' own results, never a re-read after the fact (Versioning §3.5's notation rule)
visualization.version.discarded / visualization.version.restored / visualization.version.purged / visualization.purged / visualization.current_switched The visualization twins (#332), by name — same triggers, rest-api §22's surfaces. details carry the artifact's id, name and the version (ids, names and versions only, like every lifecycle event) — and, since #372, the same additions as the parameter-set row: the pointer pair current_version_before/current_version_after on a discard AND a restore, from/to on a switch, and scope on every purge (the artifact services answer the pair and the scope from their own statements, inside the write's transaction)
dashboard.version.discarded / dashboard.version.restored / dashboard.version.purged / dashboard.purged / dashboard.current_switched The dashboard twins (#332), by name — same triggers, rest-api §23's surfaces. Same details as the visualization twins
parameter_set.version.released / visualization.version.released / dashboard.version.released The family twins of pipeline.version.released (#332): the D4 human step on each family's release route (POST .../{id}/release, session-style in practice — no MCP tool releases anything). details carry the artifact's id, name, version and via (session or api_key; key_id is on the row), plus the cascade the release carried: templates_released ({template_id, version}) for a set or a visualization, visualizations_released ({name, version}) for a dashboard — an empty list when none. Each cascaded release carries its OWN event first, the 142 order: template.version.released with cascade_from_parameter_set_id or cascade_from_visualization_id and cascade_from_version, visualization.version.released with cascade_from_dashboard_id — then the family's own event naming them

Learned-semantics audit events (same audit_log table, defined in the learned-semantic-layer design record §9; emitted by SemanticsService for the semantics_record / semantics_retire MCP tools — 118):

Value Trigger
semantics.recorded One learned fact was recorded. details carries fact_id, kind, scope, datasource, refs (as table.column keys), trust, via (mcp | session | api_key), evidence (whether a probe backed it), and supersedes / source_pipeline_id when present — never the fact text or the evidence SQL. The row the §9 acceptance counts ("facts recorded per session")
semantics.retired One learned fact was retired (semantics_retire). details carries fact_id, kind, scope, datasource, reason, and recorded_in_this_workspace — false when a workspace admin retired a DATASOURCE fact another workspace established

Mail audit events (same audit_log table, defined in Auth §5A.8; emitted by MailNotifier off the request thread, after the transport answered — 137):

Value Trigger
mail.sent The transport accepted a notice — the welcome / password-reset mail to a user or the "New user" notice to sys-ops. details carries kind (welcome | password_reset | new_user), to, act_id (the mail_sends claim's act) and message_id (the Message-ID it went out under). NEVER the body, NEVER the one-time password — the password exists in exactly one place, the message body handed to the transport
mail.failed The transport refused or failed a notice. details carries kind, to, act_id and error (the exception's class and message — a relay's refusal line, never a body). The mail_sends row carries the same error; the admin screen shows it

Schedule audit events (same audit_log table; emitted by the schedule REST routes, REST API §20, all session-only in slice 1 — #9). A person's acts only: what a schedule does when it FIRES is its runs' trail (Metadata DB §4.24), not an audit row, because nobody acted.

Value Trigger
schedule.created A schedule was created (an idempotent replay writes no second row). details carries schedule_id, name, revision, recipient_count and notification events (#442a) — never recipient addresses, the payload or the parameters
schedule.updated A schedule was edited (rename/move included). Same details
schedule.paused Paused. Same details (an already-paused schedule's pause still writes one — the act happened)
schedule.resumed Resumed. Same details
schedule.unblocked A block was cleared after the saved payload re-validated. Same details
schedule.deleted Soft-deleted. details carries schedule_id
schedule.run_requested Run now recorded a manual run (a replay writes no second row). details carries schedule_id, run_id
schedule.notification_resent A person queued one of a run's failed or uncertain notifications again (#442b, REST API §20.13). details carries schedule_id, run_id, delivery_id and prior_state — never the recipient address. The sends themselves are the run's trail (notification_sent / _failed / _uncertain), not audit rows: nobody acted

Dashboard audit events (same audit_log table; emitted by the dashboard runtime's refresh, Dashboards §5, #10 L2). ONE row per refresh, AWAITED (audit rows are authorization inputs — never the fire-and-forget path) and written at the refresh's END whatever its ending, the client gone or the process stopping included. user_id is the refreshing person. details carries refresh_id, dashboard_id, dashboard_version, workspace_id, instance_id, scope, status (a RefreshStatus), targets (name → outcome) and execution_ids — never a selection, a row or a driver message. A refresh refused at admission (429) writes no row and so no event.

Value Trigger
dashboard.refresh A dashboard refresh ended — COMPLETED, PARTIAL, FAILED, ABORTED or TIMED_OUT

16. Error Code Domains (prefix catalog)

Source: Pipeline Contract §13 — the ONLY catalog of concrete error codes. This section registers domains; deliberately no code list here, so there is exactly one place a code can drift from. Used by: every spec that defines error codes.

Error codes follow {domain}.{entity}.{failure} — three segments, all lowercase snake_case, dot-separated, ASCII. Two-segment codes exist only where the domain has no entity dimension (datasource.in_use, datasource.driver_not_loaded, datasource.not_found, datasource.lease_in_transaction, datasource.table_not_found, datasource.table_forbidden, template.not_found, the bare template.* block and citation codes (template.contract_invalid, …, template.implements_unresolved — about the version's own content, 7b/7e), rate_limit.exceeded, rate_limit.unavailable, every semantics.* code — a learned fact has no sub-entity — mcp.doc_not_found, the schedule's own states: schedule.not_found, schedule.name_taken, schedule.revision_conflict, schedule.blocked, schedule.not_blocked, and parameter.not_found and parameter.in_use (#320) — a parameter set is the entity, and "a dashboard pins it" is a state of the set, the template.in_use shape; visualization.not_found and dashboard.not_found likewise; and the request cap's request.body_too_large — the body itself is the refused thing). Additive-only — never reused, never renamed.

Domain Description Catalog section
pipeline.validation.* Pipeline JSON validation failures (write-time) pipeline-contract §13.1
pipeline.import.* Pipeline import failures pipeline-contract §13.2
pipeline.execution.* Pipeline execution failures (run-time) pipeline-contract §13.3
pipeline.node.* Individual node execution failures pipeline-contract §13.4
pipeline.staging.* Tempdb / staging failures pipeline-contract §13.5
type_mapping.* Type mapping warnings (not errors — in response warnings array) pipeline-contract §13.6
auth.api_key.*, auth.permission.*, auth.session.*, auth.login.*, auth.csrf.*, auth.password.* Authentication / authorization errors pipeline-contract §13.7 (defined in Auth §9)
datasource.* (incl. datasource.validation.*) Datasource CRUD, validation, driver availability pipeline-contract §13.8 (defined in Datasources §9)
template.* (incl. template.validation.*) Template CRUD, validation failures (incl. import cycles: template.validation.import_cycle) pipeline-contract §13.9 (defined in Templates §7)
result.* Result cursor retrieval failures pipeline-contract §13.10 (defined in REST API §7)
rate_limit.exceeded Rate limit hit (single code for all layers) pipeline-contract §13.11
rate_limit.unavailable The limiter could not decide; the request is refused (fail closed) pipeline-contract §13.11
idempotency.* Idempotency-key conflicts pipeline-contract §13.11
workspace.* Workspace resolution, membership and provisioning refusals pipeline-contract §13.12 (defined in Auth §5)
pipeline.version.*, pipeline.release.*, pipeline.promotion.* Draft/release version lifecycle and environment promotion — plus one WARNING, pipeline.release.template_needs_review (7e), returned in the release response's warnings array, never as an error pipeline-contract §13.13 (defined in Versioning)
pipeline.check.* Release checks — the server-run cross-checks gating release pipeline-contract §13.17
pipeline.script.* SCRIPT node execution-time refusals (input contract, pool, gate, invariants, strict) pipeline-contract §13.18
template.version.* Template draft/release lifecycle pipeline-contract §13.9 (defined in Versioning)
semantics.* The learned semantic layer: recording, evidence, duplicate and drift refusals pipeline-contract §13.15 (defined in the learned-semantic-layer design record)
mcp.* The MCP surface's own refusals (the resource surface's not-found is the JSON-RPC protocol's, not a code) pipeline-contract §13.16 (defined in MCP §6.2)
parameter.* (parameter.validation.*, parameter.evaluate.*, parameter.version.*, parameter.release.*, parameter.import.*, parameter.authoring.*, parameter.not_found, parameter.in_use) The parameter engine (#194): a parameter set's save-time validation, the evaluate runtime's whole-request and per-parameter refusals (the per-parameter ones are reported in state.errors[] of a 200, never as a response status), and the template.* twins of the lifecycle pipeline-contract §13.20 (defined in the parameter-engine design record §10)
visualization.* (visualization.validation.*, visualization.version.*, visualization.release.*, visualization.import.*, visualization.authoring.*, visualization.test.*, visualization.not_found) The visualization artifact (#10): save-time validation, the lifecycle, the D56 release gate, the test sessions pipeline-contract §13.22 (defined in Dashboards)
dashboard.* (dashboard.validation.*, dashboard.version.*, dashboard.release.*, dashboard.import.*, dashboard.authoring.*, dashboard.runtime.*, dashboard.refresh.*, dashboard.key.*, dashboard.not_found) The dashboard artifact (#10): save-time validation, the lifecycle, the runtime and the dashboard key kind pipeline-contract §13.23 (defined in Dashboards)
schedule.* (incl. schedule.validation.*, schedule.run.*, schedule.limit.*) Schedule save-time validation, state conflicts and not-found (#9) — never raised while a schedule fires; a run that could not start is a not_started run (§24) pipeline-contract §13.19 (defined in Scheduler)
request.* The platform's request refusals: 413 request.body_too_large from the body-cap filter on /api/v1 and /mcp, before any handler or parser (#279); and 400 request.query_too_long from the search-owning services for a search term over 200 characters after the trim (#504b) pipeline-contract §13.21
navigation.* (navigation.request.*, navigation.view.*, navigation.metadata.*) The session-only navigation trees (#464): a malformed, foreign or stale tree request or continuation (400), an unavailable promotion view (503, Retry-After), one node over the page budget (503) pipeline-contract §13.24 (defined in REST API §24)

Removed 2026-08-07 (D5): the auth.rate_limit.* domain (folded into rate_limit.exceeded), the template.import.* domain (folded into template.validation.*), the idempotency_key.* spelling (now idempotency.*), and result.claim_check_expired (now result.expired under the D9 result model).


17. HTTP Status Code Conventions

Source: REST API §2 Used by: every spec that defines HTTP behavior.

Code Meaning When used
200 OK Success (synchronous); SSE stream established GET, PUT (update), successful POST
201 Created Resource created POST that creates a new entity
204 No Content Success, no body DELETE
400 Bad Request Client-side validation failure All pipeline.validation.*, template.validation.*, datasource.validation.*, result.format_unsupported
401 Unauthorized Auth missing or invalid auth.api_key.missing, auth.api_key.invalid, auth.session.*
403 Forbidden Auth valid but the role does not hold the permission, or the route declares none auth.role_required, auth.permission.undeclared, auth.csrf.*
404 Not Found Resource doesn't exist pipeline.execution.not_found, result.execution_not_found, etc.
409 Conflict State conflict pipeline.import.version_conflict, result.execution_incomplete, idempotency.key_reused_for_different_request
410 Gone Resource expired / terminally unavailable result.expired, result.execution_failed
429 Too Many Requests Rate limited rate_limit.exceeded, rate_limit.unavailable, pipeline.execution.concurrency_limit
413 Content Too Large Request body over the platform cap (#279) request.body_too_large
500 Internal Server Error Server error Uncaught exceptions, pipeline.staging.*
502 Bad Gateway Upstream failure pipeline.node.datasource_connection_failed, pipeline.node.query_execution_failed
503 Service Unavailable Service not ready, or a transient fault of a store it depends on Readiness check failure, result.storage_unavailable (#487), pipeline.script.pool_exhausted
504 Gateway Timeout Execution timeout pipeline.execution.timeout, pipeline.node.query_timeout

18. ExecutionTrigger — how execution was initiated

Source: REST API §10.2 Used by: rest-api, mcp-server, persistence.

Value Description
UI User clicked "Run" in the pipeline editor
REST Direct REST API call (programmatic client)
MCP MCP tool invocation (agent)
PIPELINE Spawned by a parent execution's PIPELINE node (pipeline composition; metadata-db §4.6 lineage columns link the family)
ENDPOINT A published endpoint served a GET request on the published tree (074; re-rooted to /api/<category>/<version>/<path…> by 172). The execution runs in-process as the endpoint's workspace, executed_by is the key's owner with executed_by_key_kind = endpoint (§18A — the run lists for admins only, D11), and the serve's audit row carries the key id
SCHEDULE A schedule fired it (#9, V38) — a cron occurrence, a latest catch-up or a Run now. executed_by is the system identity, executed_by_key_kind is null, and the run is visible to every member with execution.read (R3); the schedule's run (schedule_runs.execution_id) links back to it
DASHBOARD A dashboard refresh started it (#10 L2, V43). The delegated act (D50): executed_by is the refreshing person, who needs dashboard.execute and not pipeline.execute; the run streams into the refresh's bounded collector, writes NO stored result, is linked to its refresh by dashboard_refresh_executions and is cancelled only through that refresh's abort (Dashboards §5)
SCHEDULED Never shipped — superseded by SCHEDULE (#9) before any row carried it. The old future placeholder, kept here only because this document never removes a value; no CHECK admits it
WEBHOOK (Future) External webhook trigger

Declaration reality (2026-08-10). Declared in the dag module, for the same layering reason as ExecutionStatus (§10) and SseEventType (§11): the executor owns the execution repository that persists pipeline_executions.trigger, and it sits below web. This document, rest-api and metadata-db remain the wire authorities.



18A. ExecutedByKeyKind — which kind of credential started an execution

Source: metadata-db §4.6 (pipeline_executions.executed_by_key_kind, V30), REST API §10.2 Used by: dag (ExecutedByKeyKind, the own-runs predicate), rest-api, mcp-server.

Roles design D11 (2026-09-20). executed_by names the user a run belongs to — the session's user, or a key's OWNER. This value says which KIND of credential started it when a key did; null means a signed-in session. The values mirror ApiKeyKind's wire names, but the enum is dag's own: the executor does not depend on auth and does not care what a key IS, only how the run is attributed.

Value Description
user A user API key (REST or MCP): the run is the key owner's, listed as their own
endpoint A published endpoint's key. executed_by still names the key's owner (the concurrency slot and the audit trail need a user), but the run is NOT that person's: the own-runs filter excludes it, so it lists for workspace admins only — and for the endpoint key itself through the serve audit row (auth.md §7.7)
server Reserved for the promotion peer's credential; nothing writes it today
dashboard (L5, #367, V45) A delegated dashboard refresh's key (D50): executed_by names the key's own service identity and the run is the key's — the own-runs predicate reads it as the key's own, and the refresh's row names the key in principal_key_id

The CHECK (chk_executions_executed_by_key_kind) admits these four and NULL. V30 backfilled ENDPOINT-triggered rows to endpoint and MCP rows to user; REST and UI rows stayed NULL (a REST run may have been a key or a cookie, and NULL reads as "a person's run", which is what those rows meant).


19. LearnedFactKind — what a learned fact is about

Source: Learned semantic layer design §4 (the ruling); the chk_learned_facts_kind CHECK of Metadata DB §4.18 is the enforcing copy. Used by: datasources (LearnedFactKind, the recorder), mcp-server (semantics_record's kind enum, derived), the introspection enrichment, the UI.

Closed. No type, nullable, key, comment, partition or row_count kind exists and none will (D-S2: nothing JDBC metadata already provides is stored — introspection stays the source for those and is what facts are checked against). Each kind belongs to exactly one scope; the database states the same rule in chk_learned_facts_kind_scope. Three sources — this table, the CHECK's list, the Kotlin enum — are held to one truth by LearnedFactKindSpecDriftTest.

Value Scope Meaning Example fact
unit DATASOURCE The unit of a numeric column "reading is in the standard unit named by unit (°C, mm, m/s) — never tenths"
time_zone DATASOURCE What a timestamp's wall-clock means "occurred_at is naive local time (Europe/Berlin); no UTC offset"
sampling DATASOURCE The population relation of a table "1-in-16 deterministic hash sample of all events; multiply counts by 16 to estimate"
grain DATASOURCE One row is one what "one row per (sensor, day, element)"
window DATASOURCE The data's coverage in time "2024-01-01 → 2025-12-31 inclusive"
enum_meaning DATASOURCE What a coded value means "status 3 = shipped, 4 = returned"
join DATASOURCE How two tables relate "orders.customer_id → customers.id, many-to-one"
caveat DATASOURCE A trap "cal_date is ISO-8601 TEXT — cast to DATE before joining a DATE column"
format DATASOURCE Encoding of a text column "holiday_name is '' (never NULL) on a non-holiday"
definition WORKSPACE A business measure or entity "revenue = SUM(amount); tips and tolls excluded"
exclusion WORKSPACE What a business question leaves out "regions means the five named ones; drop 'Unknown' and 'N/A'"
preference WORKSPACE How this organisation wants a thing computed "share comparisons within mode only — the events feed is a sample"

Trust (the companion state, design §5 — not an enum a caller supplies): asserted (no evidence — or a definition/exclusion/preference, a choice, whatever it carries; C.2 2026-09-13) → observed (evidence ran at record time, data-kind facts only) → verified (a human confirmed); the mechanical demotions needs_review (the table's column set changed around a still-resolving ref) and stale (a referenced column or table no longer exists); and retired (explicit, with a reason — never a delete). Written as the chk_learned_facts_trust CHECK.

20. CheckRunVerdict — a release check's outcome (140)

Source: pipeline-contract §13.17; the chk_pipeline_check_runs_verdict CHECK of Metadata DB §4.20 is the enforcing copy; the Kotlin enum is CheckRunVerdict (pipeline-contract ReleaseCheckGate.kt). Used by: pipeline-contract (the release gate), application (the check runner), mcp-server (pipelines_run_checks), rest-api (§5.16), the UI.

Value Meaning
pass The server's observed value satisfied the check's expectation
fail The run produced a value and it did NOT satisfy the expectation
error No verdict could be formed — datasource unreachable, statement refused, or a result shape the expectation cannot compare (a value/range check requires exactly one row and one column). The run row's message says which. Never silently a fail

Closed. A release refuses on fail OR error alike (versioning §5.3 precondition 4) — the distinction exists for the human reading the dialog, not for the gate.


21. CheckRunVia — who commissioned a check run (140)

Source: pipeline-contract §13.17; the chk_pipeline_check_runs_via CHECK of Metadata DB §4.20; the Kotlin enum is CheckRunVia. Used by: application (the check runner), mcp-server, rest-api, the UI.

Value Meaning
mcp The pipelines_run_checks tool
rest POST /pipelines/{id}/versions/{version}/checks/run
ui The release dialog / version page's run action
release The release gate's own fresh run (versioning §5.3 precondition 4)

Closed. The run rows are append-only; via is the record of WHICH surface commissioned each one.


22. MissedRunPolicy — what a schedule does about occurrences it missed (#9)

Source: Scheduler §4; the chk_schedules_missed_run_policy CHECK of Metadata DB §4.22; the Kotlin enum is MissedRunPolicy (scheduler ScheduleModel.kt). Used by: scheduler, rest-api §20.

Value Meaning
skip The default. Occurrences missed during an outage are recorded as ONE skipped / missed summary run; the next future occurrence runs
latest The latest missed occurrence runs once (origin = catch_up) if it is at most catch-up-max-age-seconds old; the earlier ones are summarized as missed

Closed. Paused and blocked time is never "missed" under either policy — resume and unblock recompute from now.


23. RunOrigin — why a schedule run exists (#9)

Source: Metadata DB §4.23 (chk_schedule_runs_origin); the Kotlin enum is RunOrigin. Used by: scheduler, rest-api §20.

Value Meaning
cron An occurrence of the schedule's pattern, recorded by the dispatcher; scheduled_at is the occurrence
catch_up The latest policy's one run for an outage; keeps the missed occurrence's scheduled_at
manual Run now; scheduled_at is null, requested_by names the person

24. RunState — a schedule run's state (#9)

Source: Scheduler §5 (states and every reason); Metadata DB §4.23 (chk_schedule_runs_state); the Kotlin enum is RunState. Used by: scheduler, web (the pipeline executor maps execution terminals onto it), rest-api §20.

Value Meaning
queued Recorded, waiting for admission
starting The start claim is committed; the launch is in progress
running The execution's record exists
succeeded The execution succeeded
failed The execution failed
cancelled Someone cancelled the execution
aborted The execution was aborted — an instance shutdown
unknown Nobody can say whether its work happened. Blocks the schedule
not_started Definitively never started (capacity, a refusal before launch)
skipped Never attempted, by policy (missed, overlap, paused, blocked, deleted)

queued, starting and running are ACTIVE — at most one per schedule. The run's reason column carries the why; the reasons are strings the scheduler and the executor define (Scheduler §5), not a closed enum.


25. TrailKind — one row of a run's trail (#9)

Source: Metadata DB §4.24 (chk_schedule_run_events_kind); the Kotlin enum is TrailKind. Used by: scheduler, rest-api §20.11.

Value Meaning
recorded The run was recorded — an occurrence, a catch-up or a Run now
capacity_retry No capacity slot; retried 30 s later
claimed The start claim committed; details.execution_id is the minted reference
execution_started The execution's record exists
not_started Definitively not started; reason says why
skipped Skipped by policy
finished A terminal outcome, read from the execution
unknown The outcome is unknown; the schedule blocks
updated_after_unknown A real terminal arrived after unknown — the same run, updated; the block stays
unblocked A person unblocked the schedule this run had blocked
notification_enqueued The outbox queued a notice for this run's transition (V51, #442b); details carry the notice id, its outcome and the queued/refused counts — never an address
notification_sent The relay accepted one delivery (§44 accepted)
notification_failed One delivery ended failed; details.code says why
notification_uncertain One delivery may have been sent and is not retried automatically (uncertain)

Append-only. A trail row is never changed or removed; the run row is the projection of its latest row.


26. ParameterCardinality — how many values one parameter carries (#194)

Source: Pipeline Contract §6.1/§6.2 (a pipeline parameter's optional cardinality) and the parameter-engine record §3.2 (a parameter set's); the Kotlin enum is ParameterCardinality (typesystem, ParameterDeclaration.kt). Used by: pipeline-contract, the parameter engine (#194's later lanes).

Value Meaning
SINGLE One value. The default when the key is omitted, and the only value a pipeline parameter accepts today
MULTI A JSON array of distinct values of the declared type; [] reads as nothing chosen. Refused on a pipeline parameter (pipeline.validation.cardinality_unsupported) until the dashboard round adopts list binding

Closed. Any other value is refused where it arrives (cardinality_unsupported on a pipeline).


27. ParameterKind — the two kinds of control (#194)

Source: the parameter-engine record §3.2, P23; the Kotlin enum is ParameterKind (parameters, ParameterSetModel.kt). Used by: the parameter engine; its REST/MCP surfaces (lane D).

Value Meaning
INPUT A value the user TYPES. Always SINGLE; its initial value is hard-coded (default_value) or comes from a one-row template (source.template)
SELECT A value the user PICKS. SINGLE or MULTI; its options are hard-coded (source.constants) or come from a template (source.template)

Closed. Any other value is parameter.validation.kind_invalid. A third, caller-supplied FIXED kind was considered and rejected by the owner (the record's §13).


28. SelectorSourceKind — where a parameter's options or initial value come from (#194)

Source: the parameter-engine record §3.3/§3.4; the Kotlin enum is SelectorSourceKind (parameters), DERIVED from which of source's keys is present — it is never a wire key itself. Used by: the parameter engine.

Value The source shape Meaning
constants {"constants": [{value, display_value, is_default}, …]} Options the author wrote into the definition — the default way to populate a select; no datasource, no query. SELECT only
template {"template": {"id", "version"}, "datasource": "<name>"} A pinned sql template run on a datasource: a SELECT's option rows, or an INPUT's one-row initial value

Closed. A source stating both or neither is parameter.validation.source_ambiguous; constants on an INPUT is source_not_allowed.


29. PresentationControl — how a renderer may show a parameter (#194)

Source: the parameter-engine record §3.7, P23c; the Kotlin enum is PresentationControl (parameters). Which values a parameter may name depends on kind × cardinality × type — the record's §3.7 table; the first allowed value of a row is the derived default a renderer receives when none is stated. Used by: the parameter engine (validation and echo only — nothing reads it for meaning, binding or evaluation).

Value Allowed on
text INPUT of every type but BOOLEAN (the derived default for STRING/BINARY)
textarea INPUT STRING/BINARY
number INPUT numeric (INTEGER, BIGINTEGER, DECIMAL, BIGDECIMAL) — the derived default
toggle INPUT BOOLEAN — the derived default
checkbox INPUT BOOLEAN
calendar INPUT DATE — the derived default
clock INPUT TIME — the derived default
datetime INPUT TIMESTAMP — the derived default
dropdown SELECT SINGLE and MULTI — the derived default of both
radio SELECT SINGLE
list SELECT SINGLE and MULTI
checkboxes SELECT MULTI

Closed but additive — a new value is a change-log row, never a bump. An unknown value is parameter.validation.presentation_invalid; a known one outside its row is control_not_applicable.


30. NumericFormatKind — a numeric parameter's display format (#194)

Source: the parameter-engine record §3.7; the Kotlin enum is NumericFormatKind (parameters), the kind of a numeric presentation.format. A temporal type's format is a pattern instead; STRING/BOOLEAN/BINARY take none. Used by: the parameter engine (validation and echo only).

Value Meaning
plain The number as it is
currency The deployment's currency, carried once per evaluate response as org (datapipelines.org.currency-symbol / -name). Decimal places come from scale, never from here
percent As a percentage

Closed. Any other value — or a kind on a non-numeric type — is parameter.validation.format_invalid.


31. RendererKind — what a visualization's configuration targets (#10)

Source: the dashboard implementation spec §3.1 (D4, D12, D13); the Kotlin enum is RendererKind (visualization, VisualizationModel.kt), renderer.kind of a visualization (Dashboards §2.1). Used by: the visualization module; the client runtime's adapters (L3).

Value Meaning
plotly A Plotly figure (data traces, layout, Plotly's own config) — the preferred chart renderer (D12)
table A table of labelled columns (Dashboards §2.1.2)
kpi A single value with no label of its own, titled by its card heading; optional format and comparison (comparison.label names the comparison line)
html RESERVED — authored HTML (D13, D46); refused in round one
svg RESERVED — authored SVG (D13, D46); refused in round one

Closed. An unknown value — and, in round one, html or svg — is visualization.validation.renderer_unsupported.


32. AssertionKind — what a visualization test case asserts (#10)

Source: the spec's §3.1 tests.cases[].assertions (D34, D56 (a)); the Kotlin enum is AssertionKind (visualization). Used by: the visualization module; the agent's test session (L4).

Value Argument Meaning
rendered — The renderer reported completion
trace_count equals (≥ 0) The rendered trace count
no_console_errors — No console error while rendering
text_visible text A title, legend or cell string is visible
no_data — The case expects the empty state
value_visible text A KPI value is visible

Closed. An unknown kind, a missing argument or an argument the kind does not take is visualization.validation.test_case_invalid.


33. TestRunStatus — a visualization test run's state (#10)

Source: the spec's §2.1 and §11.2; the CHECK of visualization_test_runs.status (V42, metadata-db §4.32). The Kotlin enum is TestRunStatus (visualization); the tests lane (L4) writes the rows. Used by: the release gate (L4), the test-session routes.

Value Meaning
RUNNING A session is open; no verdict yet
GREEN Every case's verdict green AND the server's mechanical test passed at submit
RED A case red, or the mechanical test failed
INCOMPLETE A case has no verdict
EXPIRED The session timed out unsubmitted, or the version's content moved on — never qualifies a release

Closed. The database's chk_visualization_test_runs_status refuses any other value.


34. DashboardObjectType — the type of a dashboard object (#10)

Source: the spec's §3.2 (D15, D17); the Kotlin enum is DashboardObjectType (visualization). Every object carries its type, and each list admits exactly one. Used by: the visualization module; the runtime's object-directed events (L2, the spec's §8.3).

Value The list it sits in
visualization visualizations[] — an occurrence of a pinned visualization release
group groups[] — a container (D47)
refresh actions[] — the one action kind of round one
action_control action_controls[] — a button, or a parameter's automatic binding

Closed. A missing type, or one that is not its list's, is dashboard.validation.body_invalid (details.reason = wrong_object_type).


35. ActionScope — what a refresh action targets (#10)

Source: D55 (superseding R11/D18's empty-list shorthand); the Kotlin enum is ActionScope (visualization). Used by: the visualization module; the runtime's refresh request (L2).

Value Meaning
all Every visualization of the dashboard; the action carries no targets
targets Exactly the action's non-empty targets list

Closed. Any other value is dashboard.validation.body_invalid; targets with no list, or all with one, is dashboard.validation.empty_targets.


36. StateSetting — a parameter-state override (#10)

Source: D20 and the record's §4.4; the Kotlin enum is StateSetting (visualization), each dimension (visible, enabled) of a parameter_state override. Used by: the visualization module; the runtime's parameter operation (L2).

Value Meaning
inherit The parameter engine's own state
force_true Shown / enabled whatever the engine says
force_false Hidden / disabled whatever the engine says — the value is still submitted (D23)

Closed. Any other value is dashboard.validation.body_invalid.


37. LayoutPosition — a layout region (#10)

Source: R6 and the record's §7.4; the Kotlin enum is LayoutPosition (visualization) — layout.parameter_set.position and a parameter placement's region. Used by: the visualization module; the client runtime's layout (L3).

Value Meaning
left The left region
right The right region
top Above the grid
bottom Below the grid

Closed. Any other value is dashboard.validation.body_invalid.


38. RefreshStatus — a dashboard refresh's state (#10)

Source: Dashboards §5, the implementation spec's §2.2; the Kotlin enum is RefreshStatus (visualization) — the CHECK of dashboard_refreshes.status (V43) and refresh_completed.status on the stream. Used by: the visualization module, the dashboard runtime, rest-api §23.3, metadata-db §4.34.

Value Meaning
RUNNING Admitted and running. The only non-terminal value; a row with finished_at NULL
COMPLETED Every target produced data (or the no-data state)
PARTIAL Some targets did, some did not — including a refresh that crossed max-bytes-per-refresh
FAILED No target produced anything
ABORTED An abort was requested, the client stayed away past the disconnect grace, or the process stopped
TIMED_OUT The refresh's own deadline passed — or a RUNNING row an instance crash left was closed by the sweeper

Closed. A refresh refused at admission has NO status: it writes no row.


39. EvaluationCaller — who ran a parameter-set evaluation (#376)

Source: the parameter-set workspace spec §2.1 (R2) and the owner's §11.10 ruling; the Kotlin enum is EvaluationCaller (parameters) — the CHECK of parameter_evaluations.caller (V48). Every evaluate names one in its REQUIRED EvaluationAttempt. Used by: the parameter engine's recorder, the History tab's Caller column, metadata-db §4.37.

Value Meaning
PAGE The Parameter Sets page's observed evaluation (POST /api/v1/parameter-sets/{id}/evaluations, rest-api §21.5) — the record's id is the client-minted evaluation_id
DASHBOARD The dashboard runtime — its parameters route, or a refresh's evaluation (the record's correlation_id is then the refresh id)
PIPELINE Dormant. Declared so no migration widens the CHECK later; the recorder refuses it until a pipeline binds a parameter set (the engine record's §13)
REST POST /api/v1/parameter-sets/{id}/evaluate
MCP The parameter_sets_evaluate tool

Closed. The value is code-chosen per call site, never read from a request.


40. ParameterEvaluationStatus — a parameter-set evaluation record's state (#376)

Source: the workspace spec §2.1 with the owner's §11.7 ruling (ABORTED); the Kotlin enum is ParameterEvaluationStatus (parameters) — the CHECK of parameter_evaluations.status (V48). Its four terminal words are EvaluationOutcome's (#375). Used by: the recorder, the stale sweep (metadata-db §8.5), the History tab, metadata-db §4.37.

Value Meaning
RUNNING Admitted, not yet ended. The only non-terminal value; a row with finished_at NULL
COMPLETED A response was produced; valid carries its flag (an invalid form is COMPLETED with valid = false, never a failure)
ABORTED The caller stopped it — the observed route's disconnect grace or a shutdown
TIMEOUT The whole-request parameter.evaluate.timeout (outcome_code names it)
FAILED A whole-request refusal after work began (outcome_code, e.g. parameter.evaluate.response_too_large), or a defect (outcome_code NULL)
INCOMPLETE No terminal write ever landed (an instance crash, a refused write) — written ONLY by the stale sweep; never claims a timeout

Closed. A request refused before admission (parameter.evaluate.unknown_parameter) has NO status: it writes no row.


41. QueryAttemptOutcome — one statement attempt's outcome (#376)

Source: the workspace spec §2.2 with the owner's §11.7 ruling (ABORTED); the Kotlin enum is QueryAttemptOutcome (parameters) — the CHECK of parameter_evaluation_queries.outcome (V48). NULL while the statement is in flight. Used by: the recorder, the record detail's attempt table, metadata-db §4.38.

Value Meaning
EXECUTED The statement ran and its rows were read (row_count) — whatever the parameter then made of them
REFUSED It never ran: a render failure, a datasource the workspace cannot see or reach, a missing bind, too many binds, the read-only gate, a full bulkhead — refusal_code names which
FAILED It ran and failed — a driver error, its own statement timeout, a forbidden table (error_code; none for a runtime defect)
TIMEOUT The evaluate deadline abandoned it (cancel + discard); the worker may still be running — no end stamp
ABORTED Its evaluation's caller stopped it, so it was abandoned the same way — never claimed as a TIMEOUT

Closed. A constants selector and a plain-default input run no statement and have no row.


Cross-Reference: Where Each Enum Is Authored

Enum Authoring spec Consuming specs
LogicalType type-system pipeline-contract, templates, dag-executor, staging, mcp-server
NodeType pipeline-contract dag-executor
OutputTarget pipeline-contract dag-executor, staging
WriteMode pipeline-contract dag-executor
Dialect type-system datasources, templates, pipeline-contract, mcp-server
CredentialKind datasources.md §3.4 metadata-db, rest-api, mcp-server
TemplateEngine templates templates
TemplateType template-hierarchy-design templates, pipeline-contract
StagingEngine pipeline-contract staging, dag-executor
UserKind auth.md §4.7 auth, metadata-db (the V34 CHECK)
KeyRole auth.md §7.5 auth, metadata-db (the V34 CHECK), rest-api §16.1
Permission auth.md §11A / §7.6 auth, every endpoint and MCP tool
WorkspaceRole auth.md §11A auth, metadata-db (the V29 CHECKs), rest-api §17, the members dropdown
NodeStatus dag-executor rest-api, mcp-server
ExecutionStatus rest-api dag-executor, mcp-server, persistence
SseEventType rest-api dag-executor, mcp-server
ResultFormat rest-api mcp-server
SslMode datasources datasources
AuthAuditEvent auth (+ datasource/mcp event tables in §15) observability
ExecutionTrigger rest-api mcp-server, persistence
ExecutedByKeyKind metadata-db §4.6 (dag declares it; §18A here is the wire table) rest-api, mcp-server, persistence
ApiKeyKind auth.md §7.7 metadata-db, rest-api, mcp-server
LearnedFactKind learned-semantic-layer design §4 (§19 here is the wire table) datasources, metadata-db (the V25 CHECK), mcp-server, rest-api
CheckRunVerdict pipeline-contract (ReleaseCheckGate.kt; §20 here is the wire table) metadata-db (the V28 CHECK), application, mcp-server, rest-api, the UI
CheckRunVia pipeline-contract (ReleaseCheckGate.kt; §21 here is the wire table) metadata-db (the V28 CHECK), application, mcp-server, rest-api, the UI
MissedRunPolicy, RunOrigin, RunState, TrailKind scheduler.md (scheduler declares them; §22–§25 here are the wire tables) metadata-db (the V38 CHECKs), rest-api §20
ParameterCardinality pipeline-contract §6.1 (typesystem declares it; §26 here is the wire table) the parameter engine (#194), rest-api, mcp-server
ParameterKind, SelectorSourceKind, PresentationControl, NumericFormatKind the parameter-engine record §3 (parameters declares them; §27–§30 here are the wire tables) rest-api, mcp-server (#194 lane D)
RefreshStatus Dashboards §5 (visualization declares it; §38 here is the wire table) metadata-db (the V43 CHECK), rest-api §23.3, the dashboard runtime
EvaluationCaller, ParameterEvaluationStatus, QueryAttemptOutcome the parameter-set workspace spec §2 (parameters declares them; §39–§41 here are the wire tables) metadata-db (the V48 CHECKs), the History tab (ui-screens §4.23)
RendererKind, AssertionKind, TestRunStatus, DashboardObjectType, ActionScope, StateSetting, LayoutPosition the dashboard implementation spec §3 (visualization declares them; §31–§37 here are the wire tables) Dashboards, metadata-db (the V42 CHECK), rest-api and mcp-server (L1b)

Validation Discipline

When a spec or code change introduces or renames an enum value:

  1. Update this document first. This is the source of truth.
  2. Search all specs for the old spelling — grep for the value across docs/*.md.
  3. Search the codebase — grep for the value across modules/**/*.kt.
  4. Bump the spec's schema_version if the change is non-additive (per the spec's stability promise).
  5. Document the change in the spec's Change Log appendix and in ROADMAP if it was previously tracked there.
  6. Run scripts/docs-audit.sh — it mechanically enforces steps 2's doc sweep (cross-references, error codes, config keys, forbidden legacy spellings, cited test files) and must exit 0 before the change lands.

This document itself is additive-only — values are never removed (only marked deprecated). The authoring spec governs its own stability promise; this doc tracks usage.


42. NotificationEvent — a schedule's notification choices (#442)

Stored in schedules.notification_events (Metadata DB §4.22), serialized in declaration order by REST §20. These settings select which transitions mail is sent for (#442 part b): each outcome of §43 belongs to one event (Scheduler §2.3).

Enum Wire value Meaning Default
START start Execution confirmed running Off
SUCCESS success Successful completion Off
FAILURE failure Failed, not started, cancelled or aborted On
UNKNOWN unknown Outcome unknown and its later reconciled update On
BLOCKED blocked Transition of the schedule to blocked On

No mail for skipped ticks or pause/resume. Empty recipients means off; recipient addresses are never audit fields.


43. NotificationOutcome — what one notice reports (#442b)

Stored in schedule_notification_events.outcome (Metadata DB §4.39), the notice of a delivery in REST §20.11, and the trail rows' reason for the notification kinds (§25). Each outcome belongs to one §42 event — the schedule's choice of that event decides whether the notice is written — and fixes the subject's state words; the subject is <schedule name> — <state words>, with control characters stripped from the name.

Enum Wire value Event (§42) Subject state words Written when
STARTED started start Started The run reached running
COMPLETED completed success Completed The run succeeded
FAILED failed failure Failed The run failed
COULD_NOT_START could_not_start failure Could not start The run is not_started
CANCELLED cancelled failure Stopped — cancelled The run was cancelled
ABORTED aborted failure Stopped — instance shut down The run was aborted
UNKNOWN unknown unknown Outcome unknown — action required The run's outcome is unknown
UNKNOWN_UPDATE unknown_update unknown Update on an unknown run A real terminal arrived after unknown (updated_after_unknown)
BLOCKED blocked blocked Schedule blocked The schedule moved to blocked

No notice for queued, starting or skipped. One notice per (run, outcome) — a duplicate callback writes none.


44. DeliveryState — one recipient's copy of a notice (#442b)

Stored in schedule_notification_deliveries.state (Metadata DB §4.40), shown in REST §20.11's deliveries. Never "delivered": the product knows only what the relay said.

Enum Wire value Meaning
PENDING pending Queued, or due again after a send that provably never reached the relay
ACCEPTED accepted The SMTP relay accepted the message — not proof it reached an inbox
FAILED failed Never sent, refused, or out of tries; last_error_code says which
UNCERTAIN uncertain The send may have reached the relay; never retried automatically — a schedule.update holder may resend it (REST §20.13), which may duplicate

A resend (REST §20.13) returns a failed or uncertain delivery to pending under the same Message-ID; nothing else moves a settled delivery.

last_error_code — sanitized, never transport text or an address:

Code Meaning Retried automatically
domain_not_allowed The recipient's domain is outside the allowlist (at enqueue, or re-checked at send) No
backlog_full At least backlog-max deliveries were pending; never queued for sending No — admission is serialized across the deployment; resend at the hard cap is refused with 409 schedule.notification.backlog_full
invalid_address The address fails strict parsing at send No
connect_failed The relay could not be reached; nothing was sent Yes, until max-attempts
smtp_transient The relay answered 4xx; nothing was accepted Yes, until max-attempts
auth_failed The relay refused the credentials; nothing was sent Yes, until max-attempts
smtp_refused The relay refused the message (5xx) No
io_after_connect The connection failed after the message may have been submitted (uncertain) No
interrupted A claimed send never reported back — the instance stopped mid-send (uncertain) No
render_failed The notice could not be rendered; nothing was sent No
disabled / mail_unconfigured / no_allowlist Mail was off when the delivery was due (the deployment's off reason, Scheduler §2.3) No

Appendix A: Change Log

Date Version Author Change
2026-11-01 v1.41 759 (#759) §16: the request.* domain names both the 413 body-cap refusal and the 400 search-term length refusal.
2026-10-27 v1.40 464 (#464) §16: registers the navigation.* domain (pipeline-contract §13.24): the three refusals of the session-only navigation trees.
2026-10-24 v1.39 #517 release dependencies, core (S1) §15: pipeline.version.released and its twins: one row per actual flip, dependencies first, root last; the detail keys scope, release_id, cascade_root and the root's dependencies_released. No new event name.
2026-10-25 v1.38 442c (#442) §44: backlog_full is decided by serialized deployment admission; explicit resend at the hard cap is refused.
2026-10-24 v1.37 442b (#442) §25 TrailKind gains the four V51 notification kinds; new §43 NotificationOutcome and §44 DeliveryState with the sanitized last_error_code set; §42 no longer says nothing is sent; §15 gains schedule.notification_resent.
2026-10-22 v1.36 8b (#8) The JavaScript type is enabled with the isolate and stores main(input) statements.
2026-10-21 v1.35 #640 the script-node rename (script-node-rename lane, V50, owner ruling 2026-10-20) The node_type row TRANSFORM is SCRIPT; the template-type rows say "the script types" / "a script"; the error-prefix row is pipeline.script.*; the JavaScript save refusal is script.js.unavailable; the 503 example names pipeline.script.pool_exhausted. A rename only: no behaviour, route, permission or key change beyond the names, and no alias (the ruling). The visualization block's transform key, transform.template / transform.inputs and the CSS/SVG sense keep their word.
2026-10-09 v1.34 487 (#487) The HTTP status table moves result.storage_unavailable from 500 to 503 (Pipeline Contract §13.10 v1.56) and names pipeline.transform.pool_exhausted beside it, the 503 the catalog already carried.
2026-10-03 v1.33 442a (#442) §15 audit details gain counts/events without addresses; new §42 defines NotificationEvent and its defaults. Settings delivered; mail remains off pending part b.
2026-10-02 v1.32 S3 (#376) the parameter-set evaluation history §39 EvaluationCaller (PAGE, DASHBOARD, PIPELINE — dormant, REST, MCP; the owner's §11.10 ruling), §40 ParameterEvaluationStatus (the spec's five plus ABORTED, the §11.7 ruling) and §41 QueryAttemptOutcome (the spec's four plus ABORTED) — the three V48 CHECKs, each pinned to its Kotlin enum by ParameterEnumsSpecDriftTest. No audit event (§15 unchanged): recording is the evaluate's side effect, visible through the History tab; the spec names an awaited parameter.evaluation row only if review rules one necessary.
2026-10-02 v1.31 372 (#372, #373) the families' lifecycle rows carry the mould's full shape §15's three family rows now promise the pointer pair current_version_before/current_version_after on a discard AND a restore, from/to on a switch and scope on every purge — produced by the verbs' own results inside the write's transaction (the artifact services' discard/restore/switch statements answer the pair; the purge's own version-count branch is the scope), never a controller re-read. The false "the artifact services answer no record read" clause is gone. Authority: rest-api §21/§22/§23; versioning §7.
2026-10-02 v1.30 235 (#164) the screenshot upload's audit row §15 gains visualization.test.screenshot_uploaded — the one audited write an agent makes over REST: actor = the run's starter, no key, ids/size/type only, never the token. The Status line read v1.27 under a v1.28 row (332); it now reads the current version.
2026-10-01 v1.29 L5 (#367, #10) the dashboard key kind §8A gains dashboard (V45) — the runtime routes' credential, dashboard_key_bindings folders are its reach, unbound = unservable; §8D gains dashboard_viewer — exactly dashboard.read (bindings as the lens) and dashboard.execute; §15 gains dashboard.key_bound / dashboard.key_unbound; §18A gains dashboard (V45) — a delegated refresh's attribution (executed_by the key's identity), the CHECK widens with it.
2026-10-01 v1.28 332 (#332, #330, #331) the three families' lifecycle audit §15 gains the parameter-set, visualization and dashboard twins of the five human verbs' events, and the family twins of pipeline.version.released with their cascade provenance (cascade_from_parameter_set_id / cascade_from_visualization_id / cascade_from_dashboard_id) — emitted by the three families' REST verbs, the pipelines mould's LifecycleVerbs.audit; the family drift guards carry the five-event non-vacuity floor. Authority: rest-api §21/§22/§23; versioning §7's "every verb emits its audit event".
2026-09-29 v1.27 L1c (#10 L1c) the transfer's audit events — renumbered at merge after L2's v1.26 §15 gains the four transfer events — visualization.exported / visualization.imported and the dashboard twins — the pipelines' lifecycle-audit shape (ids, versions, counts) plus imported_with_evidence recorded verbatim from the envelope manifest (the #332 review's rule for the transfer verbs; the lifecycle verbs' own rows remain that issue's). Authority: rest-api §22/§23; emitted by the two transfer controllers.
2026-09-29 v1.26 L2 (#10) the dashboard runtime — renumbered at merge after 320's v1.25 §15 gains the dashboard audit event dashboard.refresh (one awaited row per refresh, at its end). §18 gains DASHBOARD (V43): the delegated-act trigger — executed_by the refreshing person, no stored result, cancelled only through its refresh. New §38 RefreshStatus.
2026-09-30 v1.25 320 (#320) dependency guards §16 registers parameter.in_use (pipeline-contract §13.20, 409 — a dashboard pins the set) as a two-segment code beside parameter.not_found: a parameter set is the entity, and "a dashboard pins it" is a state of the set, the template.in_use shape; the parameter.* domain row lists it. The pipeline.purged audit row gains kept_draft_templates.
2026-09-29 v1.24 L1a (#10) the visualization module New §31 RendererKind, §32 AssertionKind, §33 TestRunStatus, §34 DashboardObjectType, §35 ActionScope, §36 StateSetting, §37 LayoutPosition — the dashboard documents' closed vocabularies, each held to its Kotlin enum by VisualizationEnumsSpecDriftTest; §16 registers the visualization.* and dashboard.* domains (pipeline-contract §13.22/§13.23) and their entity-level not_founds as two-segment.
2026-09-28 v1.23 279 (#279) the request-body cap §16 registers the request.* domain (pipeline-contract §13.21) with request.body_too_large as its one two-segment code; §17 gains the 413 Content Too Large row.
2026-09-26 v1.22 194b (#194) parameter engine lane B New §27 ParameterKind (INPUT, SELECT), §28 SelectorSourceKind (constants, template — derived, never a wire key), §29 PresentationControl (twelve controls, the record's §3.7 table) and §30 NumericFormatKind (plain, currency, percent); §16 registers the parameter.* domain (pipeline-contract §13.20) and parameter.not_found as its one two-segment code.
2026-09-26 v1.21 194a (#194) parameter engine lane A New §26 ParameterCardinality (SINGLE, MULTI) — a pipeline parameter's optional cardinality, shared with the parameter engine; MULTI is refused on a pipeline until the dashboard round. Cross-reference row added.
2026-09-25 v1.20 scheduler lane 1 (#9) §18 gains SCHEDULE (V38); the never-shipped SCHEDULED placeholder is marked superseded (kept — this document never removes a value). §15 gains the seven schedule audit events (schedule.created … schedule.run_requested). §16 registers the schedule.* domain (pipeline-contract §13.19). New §22 MissedRunPolicy, §23 RunOrigin, §24 RunState, §25 TrailKind.
2026-09-25 v1.19 7e (#7) the semantic link §16: the pipeline.release.* row names its one WARNING code (pipeline.release.template_needs_review, in the release response's warnings, never an error), and the two-segment list names the bare template.* block/citation codes (7b's, and 7e's template.implements_unresolved). No enum value added, removed or renamed.
2026-09-24 v1.18 215b (#215) key identities, key roles §8 Scope is replaced by §8 UserKind (human / service / system, V34) and new §8D KeyRole (api_caller, promotion_receiver) — scopes were removed (PK8). §8A's kinds restated as where a credential may be presented, the role as what it may do; §8B is one axis. §15's auth.scope.denied keeps its name, now for every authorization refusal; §16/§17 name auth.permission.undeclared. Cross-reference rows for the two new enums.
2026-09-24 v1.17 215a (#215) the permission catalog §8B Permission is the catalog: the seven coarse values (view … super_admin) are replaced by the 65 <functionality>.<permission> values of auth §7.6, which is now their single authority — this section points there instead of restating them. §8 and §8C name the new code homes (RolePermissions, the permission key-scope table). Status caught up with the change log (it read v1.15 while the rows had reached v1.16).
2026-09-21 v1.14 179 (#179) keys §8A user: minted ONLY by the login/switch hook, one per user per workspace (D16) — the "default for any key minted without an explicit kind" sentence is gone with on-demand minting. No value added, removed or renamed; the wire set is unchanged
2026-09-20 v1.16 177 (#177) roles R1 §8B Capability → Permission (D21), redefined as the role SETS of the ratified matrix: switch gone (release and switch are the author's, D8), promotion_read new (owner rule 13), execute no longer admits the promoter (D5), ws_admin gains the workspaces page (D13). New §8C WorkspaceRole — the ONE role a membership holds (V29; viewer | author | promoter | workspace_admin). New §18A ExecutedByKeyKind (user | endpoint | server, V30) beside the executed_by rename in §18. §15: workspace.member_flags_changed → workspace.member_role_changed; the member-added / invited / materialised rows carry role, not flags.
2026-09-19 v1.15 172 (#172) §18's ENDPOINT row reworded for the re-rooted published-endpoint URL shape (R-EP5); the enum and its wire value are unchanged.
2026-09-16 v1.14 149 / #125 node_progress §11 gains node_progress — the measured per-node operation sample (REST API §6.4.9); zero or more between a node's node_started and its terminal event, never terminal.
2026-09-15 v1.12 142 release cascade §15's pipeline.version.released row gains templates_released; template.version.released gains the cascade source (cascade_from_pipeline_id, cascade_from_version) when the pipeline release made it.
2026-09-14 v1.11 140 release checks New §20 CheckRunVerdict (pass | fail | error) and §21 CheckRunVia (mcp | rest | ui | release) — the wire values of pipeline_check_runs (metadata-db §4.20, V28), authored in pipeline-contract ReleaseCheckGate.kt. §15's pipeline.version.released row gains the override record (checks_overridden, override_reason); §16 registers the pipeline.check.* domain (pipeline-contract §13.17).
2026-09-14 v1.10 137 mail notices §15 gains the mail audit events sub-table: mail.sent / mail.failed (MailAuditEvents, drift-guarded by MailAuditEventsSpecDriftTest) — kind, recipients, act and Message-ID or error in details; never a body, never a password.
2026-09-09 v1.9 T202 node query timeout §17's 504 row gains pipeline.node.query_timeout.
2026-09-08 v1.8 091 keys §8A ApiKeyKind gains server — the promotion peer's credential as a stored key (auth.md §7.7, V15). Three kinds now, and the note that a scopeless kind is refused everywhere off its own family, /mcp and the UI pages included.
2026-09-02 v1.7 046 typed templates New §6A TemplateType (sql | html, template-hierarchy-design §5) beside TemplateEngine — a template's kind, chosen at create and immutable across versions; the cross-reference table gains its row.
2026-08-05 v1.0 initial draft Initial enums reference: 18 enum categories cataloged, cross-reference table, validation discipline
2026-08-07 v1.1 consistency campaign Case/serialization convention added; OutputTarget default → caller (D1); ResultDelivery removed (D9); execution_aborted SSE event added (D7); AuthAuditEvent synced to auth §10.1 (no password/lockout events); §16 reduced to domain registry pointing at the single concrete catalog (pipeline-contract §13), D5 renames applied; single authority per enum; broken source links fixed. See SPEC-REVIEW-2026-08
2026-08-11 v1.2 gate C review §16: registered template.not_found / datasource.not_found as two-segment codes (read/mutate-path misses; pipeline-contract §13 v1.3); template domain row widened to template.*.
2026-08-16 v1.3 pipeline composition §18 ExecutionTrigger gains PIPELINE — a child execution spawned by a parent's PIPELINE node (V3 migration widens chk_triggered_via to match).
2026-08-17 v1.4 pipeline composition §2 NodeType gains PIPELINE — a node that executes a version-pinned pipeline as a child execution (pipeline-contract §4.9/§8.5; guarded by the new NodeTypeSpecDriftTest in pipeline-contract).
2026-08-31 v1.6 026 post-merge follow-up §15 registers auth.password.change_failed / auth.password.change_locked — the self-service change path's failure and lockout events added by the session-only credential fix (22be7b2) after the v1.5 sync. Unregistered, docs-audit check C flagged them at auth.md:487; they are audit events, not pipeline-contract error codes.
2026-08-30 v1.5 local password auth §15 AuthAuditEvent gains the local-account events: auth.login.bad_credentials, auth.login.locked, auth.password.{seeded,changed,reset,disabled}, auth.user.{created,unlocked}; auth.login.success/user_inactive re-described as shared OIDC/local. The "no password or lockout events" note is replaced — they exist for the optional local accounts only (auth.md §5A).
2026-09-02 v1.7 MCP audit (052) §15 gains the MCP audit events table: mcp.tool.called (registered here for the first time — the dispatcher has emitted it since the original mcp-server build) and mcp.tool.write (new, 052/R4: one event per mutating tool call, node runs included). Authority for both: MCP §14; same audit_log sink as the auth/datasource events. Cross-reference row widened to name the §15 sub-tables.
2026-09-08 v1.8 101 version lifecycle §15 gains the version lifecycle audit events table: pipeline.version.discarded/restored/purged, pipeline.purged, pipeline.current_switched, and the template twins — the first lifecycle audit events anywhere (release and draft writes were previously unaudited). Authority: Versioning §3/§7; emitted by the 101 REST verbs, all session-only. scripts/docs-audit.sh's §15 event extraction widened to the pipeline/template domains (the endpoint/074 and mcp/052 precedent) so these are recognised as events, not demanded as §13 error codes.
2026-09-07 v1.5 087 connector seams New §5A CredentialKind (password | token | private_key | service_account_json | none) — what a datasource's stored credential IS, authored by Datasources §3.4; the cross-reference table gains its row. §5 Dialect gains LAKE (object storage read in place; DuckDB is the engine, with a different §5.6 posture from DUCKDB) — not a reserved value: it ships with an adapter, a driver mapping and a CHECK.
2026-09-10 v1.9 112 RBAC round 1 New §8B Capability — the ROLE axis, carried by the workspace MEMBERSHIP rather than by a credential (D-R1), and deliberately not a hierarchy: an author may not release and a promoter may not author, so each value is a predicate over the membership row, never an ordinal. §8 Scope gains an "issuable to a key?" column: admin left the key wire (O-2), and a session carries no scopes at all. §15 gains the round's audit events — auth.super_admin_acting (D-R8, emitted on READS too, because the 404 rule's promise is that a workspace is invisible from outside and the one principal exempt from it is the one whose reads most need recording), workspace.member_added/removed/flags_changed, workspace.deactivated/reactivated, datasource.granted/revoked — and loses auth.workspace.provisioned with the auto-per-user mode that emitted it. Cross-reference table gains the Capability row.
2026-09-11 v1.11 118 learned semantic layer New §19 LearnedFactKind — the closed kind list of the learned semantic layer (twelve kinds, each with its scope; no JDBC-provided kind by design, D-S2) and its companion trust states; three sources (this table, the V25 CHECK, the Kotlin enum) drift-tested to one truth. §15 gains semantics.recorded / semantics.retired; §16 registers the semantics.* error domain (§13.15, two-segment).
2026-09-10 v1.10 113 workspace invitations §15 gains the invitation audit events — workspace.member_invited (also fired on every RE-INVITE, because the upsert is the latest admin decision winning and it is audited every time), workspace.invitation_revoked, and workspace.invitation_materialised (fired at login, carrying inviter — the actor whose decision the invitee's sign-in is executing). Authority: Auth §4.6; emitted by WorkspaceService.
2026-09-12 v1.12 120 docs as tools + resource-read audit §16 registers the mcp.* error domain (§13.16, two-segment mcp.doc_not_found — the tool-surface answer to a resource read's not-found, which is the JSON-RPC protocol's RESOURCE_NOT_FOUND and carries no §13 code). §15's MCP table gains mcp.resource.read — every resources/read audited at the reader's one choke point; until now the read side of the MCP surface left no trace at all.
2026-09-13 v1.13 123 table-not-found §16's two-segment enumeration gains datasource.table_not_found and datasource.table_forbidden (§13.8 — the introspector's table resolution; the datasource domain has no entity dimension, same shape as its siblings).

This is the documentation packaged with the running version. The same files live in the repository on GitHub.