Schedule icon
Request icon
Eval icon
Get icon
If icon
ChatCompletion icon
GoogleGemini icon
SlackIncomingWebhook icon
Pause icon
Log icon
Queries icon
Set icon

Gate Open Dataset Loads on CKAN Schema, Licence and Row-Count Drift with Gemini Impact Notes and Human Approval

Detect schema, licence and row-count drift in CKAN open datasets, explain breaking changes with Gemini, pause for human approval and log to DuckDB.

Categories
AIData

Open-data publishers change datasets without telling anyone: a column disappears, a text column becomes numeric, the licence changes, or a partial file replaces a full one, and the pipelines loading from CKAN portals (data.gov.au, data.gov, open.canada.ca and hundreds of others) find out when they fail. This flow diffs the live DataStore schema, licence and row count against the last approved snapshot, asks Gemini what a breaking change means for consumers, pauses for a human only when the change is breaking, and appends every check to a DuckDB ledger.

How it works

  1. A daily Schedule trigger (06:00 UTC) runs the check before the pipelines that consume the dataset.
  2. package_show (HTTP) fetches title, licence, modification date and resources from the CKAN Action API; pick_resource (GraalVM JS) selects the resource given as input, or the first one loaded into the DataStore.
  3. datastore_fields (HTTP) calls datastore_search with limit=0: the typed column list and total row count, no rows downloaded.
  4. previous_snapshot (KV Get) loads the snapshot saved by the last clean or approved run; it is missing on the first run.
  5. diff_schema (GraalVM JS) classifies the change. Removed or retyped columns, a licence change or a row-count move beyond row_count_tolerance are BREAKING; added columns or a new publication date are INFO; no change is NONE; the first run is BASELINE.
  6. breaking_gate (If) runs only for BREAKING: explain_impact (Gemini ChatCompletion, JSON schema) names the affected columns and recommends UPDATE_LOADER, REVIEW_LICENCE, CONTACT_PUBLISHER or SAFE_TO_APPROVE; notify_owner posts to Slack with a deep link; approve_change (Pause) waits for approve or hold plus a note. Model call and Slack post use allowFailure, so a missing key never blocks the pause.
  7. finalize_decision resolves who decided; write_ledger (If) creates or appends the DuckDB ledger in internal storage (URI in the KV store) and returns per-dataset counts of checks, breaking changes, human decisions and holds.
  8. save_snapshot (KV Set, runIf) moves the baseline to the observed schema. On hold it is skipped, so the next run alerts again until the loader or the publisher is fixed.

What you get

  • Severity, decision, change list and ledger summary as flow outputs.
  • Breaking changes stop at a Pause with the diff and the model's note; additive changes and clean runs page nobody.
  • A drift_events ledger (DuckDB) showing how often each publisher changes a dataset under you and how often a human had to step in.
  • Works unchanged against any CKAN portal whose DataStore API answers anonymously; dataset, resource and tolerance are inputs.

Who it's for

  • Data engineers loading open government, humanitarian or research datasets into a warehouse.
  • Analytics engineers who own models built on public reference data and need to know before a column vanishes.
  • Data governance teams tracking licence terms of third-party datasets they redistribute.

Why orchestrate this with Kestra

  • Pause with onResume inputs is an approval step without building a UI; decision and note land in the ledger.
  • The KV store carries the snapshot and the ledger URI between runs; no external database or object store needed.
  • If, runIf and allowFailure express the gate declaratively; outputs and logs are kept per execution for audit.
  • The watched dataset is an input, not a code change; a ForEach over a list of datasets reuses the same flow.

Prerequisites

  • Kestra 2.0 with the GraalVM, AI, JDBC DuckDB and Slack plugins.
  • Outbound HTTPS to the CKAN portal. The portal must allow anonymous datastore_search (data.gov.au and data.qld.gov.au do; HDX requires a token).

Secrets

  • GEMINI_API_KEY: Google AI Studio key for the impact note (optional; without it the pause shows the raw diff only).
  • SLACK_WEBHOOK_URL: Slack incoming webhook for breaking-change alerts (optional).

Quick start

  1. Import the flow and click Execute with the default inputs: the first run records a BASELINE snapshot and creates the ledger.
  2. Execute again: with no change on the portal the run ends with severity NONE and auto_pass.
  3. To see the gate, edit the KV entry ckan_snapshot_<resource_id> (rename a column or change license_id) and execute again; the run pauses with the diff. Pick approve to move the baseline or hold to keep alerting.
  4. Add GEMINI_API_KEY and SLACK_WEBHOOK_URL as secrets to get the impact note and the alert.
  5. Point portal_url and dataset_id at the dataset you load and leave the daily schedule on.

How to extend

  • Watch several datasets with a ForEach that calls this flow as a Subflow.
  • Add an Authorization header with a portal API token to both HTTP tasks for portals that gate the DataStore.
  • Chain your loader after the gate: download and load only when severity is not BREAKING or the decision is approve.
  • Compare values, not just types, by raising limit in datastore_search and hashing the first rows.
  • Export drift_events to your warehouse and chart publisher stability over time.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.