Skip to content

Configuration Reference

Every setting Paridity resolves at run time, where it lives, how conflicting values are combined, and what it changes about a reconciliation.

How to read this document. Paridity does not have one settings screen. A value can be declared at up to seven levels, and the platform combines them by an explicit rule per setting — usually "the most specific wins", but sometimes "the tightest wins" or "all must agree". If you only read one section, read The seven layers and the Reachability legend.

Source of truth. The tables below are rendered from src/services/config_resolver.py and the migration chain in db/migrations/, not from a design spec. Where a spec and the code disagreed, the code won and the spec was corrected (2026-08-01).

The "Default (fresh install)" cell of every L1 row is generated by scripts/gen_config_reference_defaults.py and gated in CI: it is the value the cfg.platform_defaults row holds on a freshly migrated database (#3106). Your deployment can differ - an operator may have changed the row since (the development stack's demo data, for example, raises egress_row_limit) - and Settings → Platform & Tenant Defaults always shows the live value.


The seven layers

Configuration resolves from seven layers. For most settings the most specific layer that defines a value wins; the exceptions are listed in Resolution rules and flagged on every affected row.

Layer Where it lives Scope Who sets it
L1 Platform defaults cfg.platform_defaults (one row) The whole installation Your Paridity operator, at deployment. Visible read-only in Settings → Platform & Tenant Defaults
L2 Tenant defaults cfg.tenant_defaults One tenant Platform admin, in Settings → Platform & Tenant Defaults. Canonicalization defaults are visible there but operator-managed
L3 System profile cfg.systems One source or target system Platform admin, in Systems
L4 Mapping cfg.mappings One mapping Operator, in Mappings → Advanced
L5 Field override cfg.mapping_fields One column of one mapping Operator, in Field Config
L6 Schedule cfg.schedules One schedule of one mapping Operator, in Schedules
L7 Run override execution_requests.run_overrides One run platform_admin, at run submission

A run is pinned to the configuration that existed when it started. Resolution produces an immutable snapshot stored in cfg.config_versions, and the run references that snapshot by id. Editing a mapping mid-run never changes the run in flight, and the snapshot's config_checksum is what proves to an auditor which policy was in force.


Reachability legend

Every stored value is visible in the product. What varies is where you can change it. Each option below carries one of these markers.

Marker Meaning
UI Settable in the web application.
API Settable through the REST API (and therefore automatable), but with no dedicated screen.
Ops Visible in Settings → Platform & Tenant Defaults, but changed by your Paridity operator with direct SQL rather than from a form. (The platform row and its defaults come from the database migrations; db/seeds/platform_defaults.sql sets no L1 value.)
Derived Not set directly — computed from other settings or introspected from the engine.

Why "Ops" exists — and why it no longer means "hidden". Every L1 and L2 value is listed in Settings → Platform & Tenant Defaults, grouped, with the layer it comes from. An auditor or evaluator can therefore read the platform's entire comparison policy — including exactly what it treats as equal — without database access.

A subset stays operator-managed rather than editable in a form. That is deliberate, and there are two reasons:

  • Canonicalization defaults (null_token, numeric_scale, timestamp_precision, timezone, trim_strings, null_equals_empty, hash_spec_version) define what "equal" means. Changing one mid-stream silently alters every future comparison and makes prior evidence no longer directly comparable — there is no re-baseline step that would make that safe as a settings toggle. The screen shows each one with that explanation.
  • Signing key references point into your secrets backend and belong to the deployment, not the application. (The evidence key pointer, evidence_kek_ref, is the exception since #2395: it is editable in-app by a platform admin, because a tenant that cannot set it has masked-evidence reveal permanently off.)

Everything else at L2 — capacity, readiness, diff-safety limits, retention floors, evidence privacy, and the comparison-methodology flags — is editable on that screen by a platform admin, and every change is written to the audit log. L1 is read-only throughout: it is the floor a tenant inherits when it sets nothing.


Resolution rules

Most settings are "lowest layer wins". These are the exceptions — each is a deliberate safety property, not an inconsistency.

Setting(s) Rule Why
max_concurrency MIN across L1, L2, L3, L4, L6 A system's own limit is a hard ceiling — a mapping can never ask a database for more parallelism than that database was declared to tolerate.
max_partition_concurrency MIN across L4, L1 default, and both systems' L3 max_concurrency Same ceiling logic, applied to parallel partitions.
egress_row_limit, per_column_diff_limit, global_diff_cell_limit MIN across L1, L2, L4, L6 — and an L7 run override may only tighten Cost and blast-radius caps must not be raisable by a single run.
store_raw_diff_values AND across L2, L4, L5 — any FALSE suppresses A privacy suppression at any level must be final. Values are stored as NULL with values_masked = TRUE.
retain_*_days L4 wins, else the L1 default; cannot go below the L2 minimum A mapping can retain evidence longer than the tenant floor, never shorter.
priority MAX across L1 and L6 A platform floor can be raised for an urgent schedule and never dropped below.
cdc_integration_mode L4 only Changing how CDC signals gate a comparison is a policy change; a schedule or a single run must not be able to weaken it.
cdc_table_semantics L4 only Declares how the table's commit point relates to the one the comparison reads (#2853); a schedule or a single run must not be able to switch the hold off.
read_pin_views L4 only Declares that the mapping's Snowflake view(s) may be pinned at the readiness instant (#2933); what a run reads must not vary by schedule or run.
fail_fast_threshold_pct Lowest wins; an L7 override may lower it —
diff_truncation_method L1 → L2 → L4, L4 wins; L6/L7 may select only first_n or random_sample stratified has resource implications, so it stays a mapping-level decision.
mask_in_ui L5 (field) only A tenant-wide "mask everything" policy is not implemented — do not assume one exists.

Comparison semantics (canonicalization)

These decide what "equal" means. They are the highest-consequence settings in the product: changing one changes which rows are reported as differences.

Option Layers Default (fresh install) Values What it changes
null_token L1, L2 — Ops <NULL> any string The literal a NULL becomes before hashing. Both sides must agree or every NULL-bearing row mismatches.
trim_strings L1, L2, L4 — Ops/API true bool Strips the six ASCII whitespace characters (space, tab, newline, vertical tab, form feed, carriage return) — not Python's 29 Unicode whitespace codepoints. Non-breaking space and U+3000 are deliberately kept.
null_equals_empty L1, L2, L4 — Ops/API false bool When true, an empty string (after trimming) is treated as NULL. Turn this on only when one side genuinely stores '' for absent data.
numeric_scale L1, L2 — Ops 6 1–38 Decimal places every numeric is quantized to before hashing. Too low silently equates genuinely different values; too high can surface float noise as differences. Per-field overrides are rejected — this is global by design.
timestamp_precision L1, L2 — Ops millis seconds, millis, micros Truncation applied to every timestamp. Set this to the coarsest precision your two systems agree on.
timezone L1, L2 — Ops (DB-only: the config-defaults API lists it read-only) UTC IANA name How naive timestamps are interpreted. Non-UTC values change which rows fall inside an incremental window, not just how they render — and, for a time partition strategy, which rows each partition holds: a partition day is a day in this timezone, rendered per side by the column's comparison frame (naive columns compare wall-clock digits, timezone-aware columns compare instants). There is no per-mapping timezone; a change takes effect on the next config version each mapping mints.
numeric_tolerance L4 only — API none decimal Absolute difference below which two numerics are considered equal. Use for known float drift; it weakens exactness, so prefer fixing the source.
hash_spec_version L1 — Ops 1.0 — The canonicalization contract version. Changing it changes every hash.

Both sides always use the same rules. Canonicalization is resolved once per mapping and applied identically to source and target. That is what makes a cross-engine comparison meaningful.

SCD scope (SCD2 / bitemporal tables)

Scopes the comparison to the current rows of a side that legitimately keeps closed history rows. Mapping-level only, always operator-declared — the platform never infers SCD2-ness. See Mappings — declared scope.

Option Layers Default Reach What it changes
scd_scope L4 only none UI The whole declaration: scoped side, mode (current_only is the only v1 mode), a typed current_predicate (flag_equals, end_ts_sentinel, or end_ts_null — columns and values, never SQL), optional interval_columns, and assertions. Stored in the checksummed config snapshot, so every run records the exact predicate it was scoped by.
assertions.unique_current_per_key — true UI (fixed) The I1 invariant — always checked when a scope is declared; not disableable.
assertions.no_interval_overlap — false UI The I2 invariant — opt-in (extra self-join per run), requires interval_columns.

Rules the reference should not let you discover the hard way: the scoped side's engine must be live-verified for scope rendering (currently postgres, snowflake — anything else is rejected, fail-closed); scope columns may not also be key or mapped comparison columns; key_columns must be declared; and the scope composes with (never replaces) the side's filter_sql. Watermark queries are never scoped by scd_scope — see Watermarks.

Per-field overrides (L5)

Set in Field Config. All are UI-reachable unless noted.

Option Values What it changes
exclude_from_hash bool Column is omitted from the row fingerprint — it stops affecting pass/fail. Rejected on a key column (422 KEY_COLUMN_EXCLUDED_FROM_HASH): the proof phase compares an order-insensitive aggregate of the row hashes, so with the key outside the hash two datasets whose payloads are swapped between keys compare equal, and a proof-only mapping never runs the diff phase that would catch it.
exclude_from_diff bool Column still proves equality but is not reported cell-by-cell. Rejected on a key column (422 KEY_COLUMN_EXCLUDED_FROM_DIFF): the field diff finds each row by its key. A key column stored excluded before this rule still runs correctly, because the field diff always fetches the key.
hash_column_order int Position in the row serialization. Must match on both sides; normally leave to the platform.
blob_compare_mode hash, metadata_only, skip, full How a BLOB/CLOB is compared. Defaults to hash (content-exact) when unset. metadata_only compares only length and existence - a same-length content change becomes invisible, so it is an explicit opt-in, never a default. The length unit is the category's: bytes for BLOB/binary (BLOB:<n>), code points for CLOB/text (CLOB:<n>) and for JSON / XML (SEMI:<n>, one render on the proof, the recheck and the field diff; JSON / XML metadata_only always runs on the reference implementation, never native - #2996). skip is rejected on a key column (422 KEY_COLUMN_BLOB_SKIPPED): it removes the column from the row hash, so payloads swapped between keys compare equal.
sensitivity_level, mask_in_ui — / bool Masks the value in evidence views. Field-level only.
store_raw_diff_values bool The third term of the AND-chain. Any FALSE above suppresses storage.
per_field_diff_limit int Overrides the per-column cap for this column only.
transform_chain, transform_chain_target ordered list Per-side normalizations applied before comparison (e.g. case folding). Five transforms deliberately force the reference implementation rather than engine SQL — see the transform matrix in CLAUDE.md.
canonical_type_override date, timestamp, string Forces how a column is canonicalized when the engine's declared type is misleading. date / timestamp remap a date/timestamp column (inert on any other type). string declares that a ClickHouse FixedString(N) holds NUL-padded text, compared as text with the trailing NULs dropped; it is rejected on any other column type and with blob_compare_mode metadata_only / skip (422 CANONICAL_TYPE_OVERRIDE_NOT_APPLICABLE). A mapping with an effective override runs on the reference implementation on both sides unless that engine pair is verified for native rendering (none is for string).
rounding_mode, timestamp_rounding mode Rounding semantics for numerics/timestamps. Engines disagree on .5 boundaries, so this is explicit.
dual_compare_mode, float_compare_mode mode Qlik dual values and float comparison strategy.
decrypt_algo, decrypt_params, decrypt_key_ref, decrypt_expression — Encrypted-column handling. decrypt_algo/decrypt_params are API-read-only today (set them with the field-config import path). Keys are always resolved through the secrets backend and never stored.
declared_type — Derived — introspected from the engine, refreshed by schema-refresh.

Compute and diff safety

Option Layers Default (fresh install) Reach What it changes
compute_mode L1, L4, L6, L7 hybrid UI Where comparison work happens (pushdown vs. platform-side).
proof_only_mode L1, L4, L6, L7 false UI Stop after the proof phase. A failure becomes terminal fail_hash with no field-level diff — cheap, but no evidence of what differed.
max_concurrency L1, L2, L3, L4, L6 (MIN) 5 UI Parallel queries against a system.
max_partition_concurrency L4, L1, both L3 (MIN) 4 UI Parallel partitions in a partitioned run.
query_timeout_seconds L1, L3, L4 300 UI Per-query timeout. Size it for the proof query at full volume.
max_diff_rows L1, L4 500000 UI Cap on rows handed to the diff engine. (No schedule-level override exists.)
diff_sampling_mode / diff_sample_size mode: L1, L4 · size: L4 only all UI Diff every mismatched row (all), a deterministic hash-selected N (sample_n), or percentage (sample_pct) of them. Selection is stable per run+key, not first-N. Sampled diffs are flagged is_sampled = TRUE in evidence and the total mismatch count is always recorded, so a sampled run still tells you the true scale of the difference. A sample_* mode with no diff_sample_size set on the mapping diffs 100% (the safe direction).
egress_row_limit L1, L2, L4, L6 (MIN), L7 tighten-only 1000000 UI Max mismatched rows pulled back to the worker — the main egress-cost control.
per_column_diff_limit same 10000 UI Max diff cells per column.
global_diff_cell_limit same 5000000 UI Max diff cells for the whole run.
fail_fast_threshold_pct L1, L2, L4, L7 50.00 UI If this share of rows mismatch, skip the diff phase and fail immediately — the run is already conclusive. It ends fail_count if the row counts differ, otherwise fail_hash (never fail_field: no column was compared).
diff_truncation_method L1 → L2 → L4 (L4 wins); L6/L7 may select only first_n / random_sample first_n UI Which rows survive truncation once a limit binds.

Readiness (when a comparison may begin)

Readiness is what makes Paridity CDC-aware: it refuses to compare a window that replication has not finished delivering, which is the difference between a real failure and a false alarm.

Option Layers Default (fresh install) Reach What it changes
readiness_mode L1, L4, L6 hybrid API How strictly CDC/watermark signals gate the run.
max_lag_seconds L1/L4, MIN with L6 900 UI Maximum tolerated replication latency before the run defers.
quiescence_seconds L1, L4, L6 60 UI How long the watermark must be stable before proof starts.
retry_max_attempts L1, L4, L6 10 API Readiness re-check attempts before the run is skipped.
retry_backoff_seconds L1 60,120,300,600,900 Ops Backoff ladder between re-checks.
cdc_integration_mode L4 only advisory UI strict halts on CDC degradation; advisory warns and proceeds - except that a snapshot mapping is held (not compared) while a reachable replication tool reports a connector paused, stopped or failed; disabled skips CDC checks. Forced to disabled when no replication profile is attached.
cdc_table_semantics L4 only none UI uniform_iceberg_mirror: the Iceberg side is a Databricks UniForm mirror of a Delta table; each run is held (not_ready, cause uniform_metadata_lag) until the Iceberg table property delta-version has reached the Delta version (the commit-instant comparison is the fallback when the property is unreadable; unreadable both ways holds). Needs an Iceberg commit_snapshot watermark on one side and a Delta one on the other (422 otherwise). replacing_merge_tree_final / clickpipes_peerdb (#2900, ClickHouse): the ClickHouse side holds unmerged row versions (ReplacingMergeTree and the other merge-dependent engines); every read of it goes through FINAL, and clickpipes_peerdb also excludes the ClickPipes soft-delete rows (_peerdb_is_deleted = 0). Needs a ClickHouse system on one side (422 CDC_TABLE_SEMANTICS_SYSTEM_MISMATCH otherwise); the run refuses with the reason if the table's engine or columns do not fit. none on a merge-dependent ClickHouse table is allowed but flagged (an advisory, not a block): unmerged versions are counted until they merge. none changes nothing.
read_pin_views L4 only false UI Snowflake only (#2933). true: readiness also pins the mapping's Snowflake view side(s) at the readiness instant (AT(TIMESTAMP)), so every phase of a run reads the same state; the pinned read is verified first and a base table whose DATA_RETENTION_TIME_IN_DAYS is 0 refuses the run (a configuration hold naming that table). Needs a Snowflake system on one side (422 READ_PIN_VIEWS_SYSTEM_MISMATCH otherwise). Native tables need no flag: they are pinned when their retention is 1 day or more and read latest per phase at retention 0. false changes nothing.

Evidence and retention

Option Layers Default (fresh install) Reach What it changes
evidence_kek_ref L2 only (unset) UI / API — platform admin only Secrets-backend pointer (aws-secrets://, azure-keyvault://, gcp-secretmanager://, infisical://, vault://; env:// in development only) to the tenant secret that protects masked evidence values — the KEK that wraps each run's data key and the HMAC key behind the fingerprints are both derived from it. A pointer, never the key: the platform resolves it at run time and never stores or returns the material. While unset, masked diffs are written fingerprint-only and "Reveal masked values" is unavailable for every run of the tenant (the diff worker degrades to NULL rather than lose evidence, so nothing fails — the UI states the reason instead). Runs recorded before it was set stay fingerprint-only; a change takes effect on each mapping's next run. Set on Settings → Platform & Tenant Defaults → Evidence key (secrets pointer), which also shows Masked-value reveal: available / not configured; every change is audit-logged with the caller's IP. Added #2395 — until then this was DB-only (no API, no UI, no seed).
store_raw_diff_values L2 AND L4 AND L5 true UI (L4/L5) Whether actual differing values are stored. Any FALSE in the chain stores NULL and sets values_masked. (The L1 column exists but is not read — it cannot suppress storage platform-wide.)
retain_summary_days L4, else L1; floored by the L2 minimum 540 UI Retention for run summaries.
retain_mismatch_keys_days L4, else L1; floored by the L2 minimum 365 UI Retention for mismatched-row keys.
retain_field_diffs_days L4, else L1; floored by the L2 minimum 180 UI Retention for cell-level differences — usually the largest and shortest-lived evidence.
bundle_retention_days L2, floored by L1 2555 UI Retention for audit evidence bundles and their stored period containers. A bundle ages from its own generation time. The tenant value cannot go below the platform value. Enforced by the bundle leg of the same retention purge; GET /evidence-bundles/retention reports the resolved value, what awaits the next purge run, and the last purge on record.

Evidence tables are append-only. Deletion happens only through the retention purge, which writes its own audit record of what it removed.

Legal holds override every value above. A legal hold (Settings → Legal Holds) freezes a tenant, a mapping, or a single bundle so the purge skips it — a held record is never deleted no matter how far past its retention window it is. Placing and releasing a hold is approval-gated (evidence.legal_hold, a second person approves) and audited; the purge records each skip. A hold is an action, not a resolved-config field — it does not appear in the seven-layer resolution — but it is the control that lets you preserve evidence for litigation or an audit without changing anyone's retention numbers. See docs/runbooks/evidence_retention.md.

The retention purge runs on a built-in schedule (#2025). These four settings define how long evidence is eligible to be kept; the platform's built-in scheduler (RetentionPurgeWorkflow, a durable Temporal singleton on the scheduler worker) enforces them automatically. It is on by default but dry-run-first: for the first retention_purge_dry_run_days after install (default 7) it computes and attests what would be deleted without deleting anything, so a fresh deployment never deletes on day one. Every fire writes one cfg.audit_log attestation row. An ad-hoc manual purge stays available and approval-gated (evidence.retention_purge).

Deployment settings (env / Helm values, not per-tenant resolved config): retention_purge_enabled (default on), retention_purge_interval_seconds (86400 = daily), retention_purge_hour_utc (4), retention_purge_dry_run_days (7), retention_purge_force_dry_run (off — keep dry-run indefinitely).

Pausing the schedule is an approval-gated, audited operator act (evidence.retention_purge elevation → retention_purge.paused/.resumed), distinct from disabling it in deployment config. A paused schedule still fires but deletes nothing — the no-op is attested — so a paused schedule is a compliance fact GET /evidence-bundles/retention.enforcement reports as retention_purge_paused. Pause and resume it in Settings → Retention Schedule. See docs/runbooks/evidence_retention.md for the full procedure.

Legal holds (above) still override every value here: automatic deletion always skips a held scope. This is why the schedule ships default-on safely — evidence under a preservation order can always be frozen.


Windows, watermarks and partitions

Option Layers Reach What it changes
windowing_mode L4 API fixed_interval (a rolling time window), watermark_range (bounded by observed watermarks), or partitioned_snapshot (per-partition frozen snapshots).
watermark_domain L4 UI time_utc or sequence. Never mix domains within a mapping — an SCN and a timestamp are not comparable.
Source/target watermark config L4 UI Strategy and field per side. Prefer, in order: control_marker, delta_version, ingest_ts, partition_max, timestamp, custom_sql, then snapshot modes.
window_interval_seconds, min_advance_seconds L4 API Window size and the minimum advance required for a new run to be worth executing.
partition_strategy L4 UI time (with a grain), key_range, list, or composite (two components, no nesting). Per-partition SQL scoping is decided per (engine, kind, column-type family) cell and per side — see the matrix below: on an unverified cell the partition is proven over the whole window on that side (correct, no speed benefit). The mapping page shows the per-side prediction; the run page shows what each run recorded.
max_partitions_per_run L1 only UI Safety cap on partitions per run (default 256).
window_mode L6 UI auto (incremental) or full (forced full-table comparison).

Per-partition SQL scoping — the live-verified cells (generated from src/services/partition_coverage.py by scripts/gen_partition_scoping_matrix.py; do not hand-edit). Scoping is decided per side of a mapping and per (engine, partition kind, column-type family) cell: a partition's proof and extract SQL are scoped to that partition on a side only when the side's engine has a live-verified cell for the partition's kind and the partition column's type family — on any other cell the partition's proof runs over the whole window on that side (results stay correct: the coverage check still holds; the per-partition speed benefit does not apply). A composite partition is scoped only when every component is. The mapping page shows the pre-run prediction per component and side; the run page shows what each run actually recorded.

Engine time is_null key_range list
bigquery date, timestamp null numeric, date numeric, date, string
databricks date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
db2_luw date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
mysql date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
oracle date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
postgres time, date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
redshift date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
sap_hana date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
snowflake date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
sqlserver date, timestamp null numeric, date, timestamp numeric, date, timestamp, string
trino date, timestamp null numeric, date, timestamp numeric, date, timestamp, string

Family names: time = a family-less legacy time predicate (PostgreSQL's type-agnostic cell); null = the NULL bucket; numeric / date / timestamp / string = the partition column's source-introspected type family (predicate_type_family). A column whose type the schema snapshot does not know has no family and is never scoped (refresh the schema).

time partitions under a non-UTC global timezone additionally need the side's comparison frame (classified when the run starts, from the column's type) to be live-verified for the engine — so the mapping page reports such a component as classified at run time until a run records it. PostgreSQL keeps its own per-side cast path and needs no frame cell.

Engine verified frames (tz ≠ UTC)
bigquery utc_frame, wall_frame
databricks utc_frame, wall_frame
db2_luw wall_frame
mysql utc_frame, wall_frame
oracle utc_frame, wall_frame
postgres own per-side cast path (any classified frame)
redshift utc_frame, wall_frame
sap_hana wall_frame
snowflake utc_frame, wall_frame
sqlserver utc_frame, wall_frame
trino utc_frame, wall_frame

key_range / list partitions on a timestamp column under a non-UTC global timezone (#2329) are rendered per side frame too — a naive authored bound means local wall-clock time in the tenant timezone, an offset-bearing bound the instant as written — but the >= / < / = bound shapes are verified SEPARATELY from time partitions, per (engine, kind, frame); PostgreSQL has no exemption here. An engine with no cell for the kind keeps such a partition an honest whole-window proof on that side (reason unverified_bound_frame); an engine with a cell reports the component as classified at run time until a run records the frame.

Engine key_range (tz ≠ UTC, timestamp column) list (tz ≠ UTC, timestamp column)
bigquery — —
databricks — —
db2_luw wall_frame wall_frame
mysql utc_frame, wall_frame utc_frame, wall_frame
oracle utc_frame, wall_frame utc_frame, wall_frame
postgres utc_frame, wall_frame utc_frame, wall_frame
redshift — —
sap_hana — —
snowflake — —
sqlserver utc_frame, wall_frame utc_frame, wall_frame
trino utc_frame, wall_frame utc_frame, wall_frame

Reason codes (the reason / explanation fields on GET /runs/{id} → partition_scoping, GET /runs/{id}/partitions → scoping_detail and GET /mappings/{id} → partition_scoping; a composite reports composite_component:<index>:<reason> for its first failing component):

Reason What it means
composite_shape The composite partition is malformed (a nested composite, a non-object component, or no components); the partition's proof ran over the whole window.
frame_classified_at_run Partition scoping for a partition kind partition on a timestamp column under a non-UTC global timezone depends on the affected partition column's comparison frame, which is classified when the run starts; until a run records it the mapping reports scoping as not applied.
no_side_column The partition predicate names no affected-side column (declare source_column and target_column on the strategy); the partition's proof ran over the whole window.
no_typed_spelling The the engine engine has no exact literal spelling for this column-type partition kind bound (a bound that does not parse in the column's type, a missing bound, or a string list without a known trim setting); the partition's proof ran over the whole window.
quantize_unverified This column-type partition kind partition compares a column whose canonical value is rounded (or truncated) before hashing, and quantized membership on the engine has not been live-verified (only PostgreSQL half-up rounding is); a raw-column bound could place a boundary row in different partitions on the two sides, so the partition's proof ran over the whole window.
timestamp_bound_non_utc_tz This run was recorded before per-frame rendering of timestamp partition kind bounds existed (#2320, superseded by #2329): under a non-UTC global timezone the bounds could not be rendered per comparison frame (wall-clock vs instant), so the partition was not scoped on any engine; re-run the mapping to get per-frame scoping where the engine is verified; the partition's proof ran over the whole window.
unclassified_frame Under a non-UTC global timezone the affected partition column's comparison frame could not be classified (wall-clock vs instant), so a time bound cannot be rendered safely; the partition's proof ran over the whole window.
unknown_family The partition column's type is not known to the mapping's schema snapshot (refresh the schema, or partition on a mapped column), so no typed boundary literal can be rendered; the partition's proof ran over the whole window.
unsupported_kind This partition predicate is not one of the scopable kinds (time, is_null, key_range, list, or a composite of those); the partition's proof ran over the whole window.
unverified_bound_frame The the engine engine has not been live-verified to render a timestamp partition kind partition against a comparison-frame column under a non-UTC global timezone (the >= / < / = bound shapes are verified separately from time partitions); the partition's proof ran over the whole window.
unverified_cell The the engine engine has not been live-verified to place every boundary row of a column-type partition kind partition exactly; the partition's proof ran over the whole window.
unverified_frame The the engine engine has not been live-verified to render a time partition against a comparison-frame column under a non-UTC global timezone; the partition's proof ran over the whole window.

Schedules

Schedules are interval-based, not cron. Set in Schedules; all UI.

Option Values What it changes
interval_value + interval_unit int + minutes/hours/days/weeks Recurrence.
days_of_week ISO 1–7, or empty Restrict to specific days.
active_start_time, active_end_time time Daily active window.
effective_from, effective_until date Calendar bounds — how a quarter-end or year-end schedule is expressed.
tags list Grouping used by close mode.
priority low, normal, high, critical Ordering when the platform is congested. Resolved as MAX with the platform floor.
window_mode auto, full Forces a full comparison for this schedule (e.g. a quarter-end sweep).
enabled bool Master toggle.

Close mode is a bulk convenience toggle over tags, not a separate scheduling engine: it enables/disables by tag, may step priority by −3..+3 enum levels, and may apply the two tightening overrides (max_lag_seconds, quiescence_seconds). Overrides looser than a schedule's mapping are skipped per schedule and reported back. Available at Configure → Schedules → Close Mode, and as POST /schedules/bulk-toggle. See Schedules and close mode.


Deployment settings

Set as environment variables by your operator; not part of run configuration. The complete list lives in src/config/settings.py. The ones a deployer must understand:

Variable Purpose
DATABASE_URL Control-plane Postgres. Required, no default.
APP_ENV development / test / local / production. Must be set explicitly — the dev auth bypass is refused unless this names a non-production environment.
TEMPORAL_HOST, TEMPORAL_NAMESPACE Workflow engine.
SECRETS_BACKEND aws, azure, gcp, infisical, or vault.
OIDC_ISSUER, OIDC_AUDIENCE, OIDC_JWKS_BASE_URL Identity provider.
SCIM_AUDIENCE, MCP_AUDIENCE Dedicated audiences for the provisioning and agent surfaces. A token minted for one surface is rejected on the others.
PUBLIC_BASE_URL Used in SCIM resource URLs and OAuth metadata.
CFG_AI_ENABLED, CFG_AI_REQUIRE_TENANT_OPT_IN, MCP_RATE_LIMIT_PER_MINUTE AI/agent surface controls. Production forces tenant opt-in.
AUDIT_SIGNING_KEY_REF Signing key for the audit hash-chain anchor.
AUDIT_SIGNING_PREVIOUS_KEY_REFS Retired signing keys, verify-only (comma-separated). Move the outgoing ref here when you rotate: a checkpoint is verified under the key that signed it, and only declared keys are trusted. Keys left out are reported as unverified on GET /audit/verify — not as tampering, but no longer provable either.

  • Glossary — Mapping, Replication Profile, watermark, hash_total vs row_hash.
  • Float column handling — the one canonicalization topic with its own guide.
  • docs/specs/spec_canonical_hashing.md — the full canonicalization contract.
  • docs/specs/spec_watermark_config.md — per-strategy watermark configuration.