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:
stagingdepends only ontypesystem, so it cannot see a type declared inpipeline-contract. In v1 the enum has a single value (H2), sopipeline-contractdeclares it (for thesettings.tempdb.enginewire value, with@JsonValue) andstagingdeclares an identical localenum class StagingEngine { H2 }forStagingFactorydispatch;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, moveStagingEngineintotypesystem(the shared lower layer, exactly asDialectresolved 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
mcpkey) 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
serverkey authenticates nothing outside the promotion routes. Presented as an ordinaryDP-API-Keyit is refused on every route — REST, htmx partials,/mcpand every UI page — withendpoint.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
dashboardkey 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
dagmodule, for the same layering reasonSseEventTypeis (§11):webimplements rest-api at layer 5 and depends ondagat layer 3, never the reverse (module-structure §4.2). The executor is what produces the status and what writespipeline_executions.status, so the enum lives at the lowest layer that needs it andwebconsumes 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
dagmodule, for the same layering reason asExecutionStatus(§10) andSseEventType(§11): the executor owns the execution repository that persistspipeline_executions.trigger, and it sits belowweb. 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:
- Update this document first. This is the source of truth.
- Search all specs for the old spelling —
grepfor the value acrossdocs/*.md. - Search the codebase —
grepfor the value acrossmodules/**/*.kt. - Bump the spec's
schema_versionif the change is non-additive (per the spec's stability promise). - Document the change in the spec's Change Log appendix and in ROADMAP if it was previously tracked there.
- 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). |