Skip to content

Declarative validation ​

triggers.yaml accepts a top-level validate: key that lists rules the runtime evaluates on every INSERT and UPDATE event. A rule is just a boolean DSL expression plus a mode telling the runtime what to do when the rule fails.

Schema ​

yaml
version: 1
gpkg: ./data/parcels.gpkg
validate:
  - id: surface_min
    rule: "geom_area_m2() >= 50"
    mode: warn
    message: "Parcel surface < 50 m²"

  - id: shape_valid
    rule: "geom_is_valid()"
    mode: tag
    tag_field: validation_status
    message: "Geometry self-intersects"
FieldRequiredNotes
idyesStable identifier used in log lines and tag values.
ruleyesBoolean DSL expression — see DSL geom functions and the SQL dialect contract.
modeno (default warn)warn logs and broadcasts. tag writes the failure on the row.
tag_fieldonly when mode: tagColumn receiving failed:<id> on failure.
messagenoHuman-readable detail attached to the log / WS event.
enabledno (default true)Toggle without removing the rule.

Modes ​

mode: warn ​

The default. Emits a structured log line and a validation.failed event over the runtime's event hub. Use this when downstream consumers (dashboards, alerts) need to know about failures but the data should keep flowing untouched.

mode: tag ​

The runtime calls a tag_field action that writes failed:<rule.id> into the column you point at via tag_field:. This is the right default when QGIS / portal clients need to render bad rows in red, or when a downstream pipeline filters on a status column. The column is auto-created on first use; subsequent failures of the same rule overwrite the value.

Note (v1.6.0 scope): the validation runner ships end-to-end — every INSERT and UPDATE event triggers a per-rule evaluation, every failure logs and broadcasts validation.failed over the event hub, and a mode: tag failure dispatches a synthetic TAG_FIELD action through the regular ActionDispatcher. The dispatcher auto-creates the target column on first use (PRAGMA + ALTER TABLE) and writes failed:<rule.id> onto the row.

Wiring the validation runner from GISPulseConfig.validate_rules at build_runtime time is a separate piece — the runtime accepts a runner injection today, the auto-instantiation step is tracked as a follow-up because it needs a product decision on how rules map to tables when multiple triggers are configured.

Without an action_dispatcher injected into the runner, mode: tag configs degrade to mode: warn semantics (log + WS event, no row mutation).

Cross-source validation (preview) ​

Two cross-source helpers are planned for the v1.6.x line:

yaml
validate:
  - id: in_known_commune
    rule: "geom_within(layer='communes', match='code_insee')"
    mode: warn

  - id: no_overlap
    rule: "not geom_overlaps_any(layer='self', exclude_self=true)"
    mode: tag
    tag_field: validation_status

Both compile to a DuckDB sub-query and require the engine to attach the cross-source layer; the runtime piece is tracked in #122. The schema side already accepts the syntax so you can prepare your YAML now.

Migration from ESRI Attribute Rules ​

If you are coming from ESRI's Attribute Rules, see Migrating from ESRI for a side-by-side table of kind: aliases and rule equivalents.

Published under AGPL-3.0 license.