Rule suites
A rule suite is a named set of expectations — declarative assumptions about your data — that PLACEHOLDER Cloud runs against a data asset. A suite is the unit of authoring; a checkpoint is the unit of execution.
PLACEHOLDER Cloud is built on Great Expectations. A rule suite is conceptually a GX ExpectationSuite, stored as a portable JSON blob so you can move suites between environments.
Data model
RuleSuite lives in the per-tenant schema.
| Column | What it stores |
|---|---|
name | Human label (orders-completeness). |
suite | A JSONB blob: {"rules": [{"rule_type": "expect_column_values_to_not_be_null", "kwargs": {"column": "email"}}, …]}. |
Browsing the expectation gallery
The rule-suite editor is fine when you already know which expectation you want. If you don't, open the expectation gallery (sidebar → Rule Suites → "Browse expectations") for a searchable catalog of every rule type the editor supports. Each entry carries a one-sentence description, an example payload, the kwargs schema, and the column types it applies to (numeric / string / date / any).
Click Use this rule on any card to jump back to the rule-suite list with that expectation queued for pre-fill — pick a suite, open the editor, and the rule is added with the example kwargs already filled in. Saves a trip to the docs. The catalog itself is served by GET /api/v1/expectations/gallery and is identical for every tenant. Shipped in FEATURE-40.
Supported expectation types (form-builder)
The PLACEHOLDER Cloud form-builder covers these expectations today:
| Expectation | Fields |
|---|---|
expect_column_values_to_not_be_null | column |
expect_column_values_to_be_between | column, min, max, inclusive? |
expect_column_values_to_be_in_set | column, value list |
expect_column_values_to_match_regex | column, pattern |
expect_column_values_to_be_unique | column |
expect_column_value_lengths_to_be_between | column, min, max |
expect_table_row_count_to_be_between | min, max |
The expectation gallery (FEATURE-40) is the discovery surface for these — see Browsing the expectation gallery above. Raw JSON authoring via PUT /api/v1/.../rule-suites/{id} is still supported for power users and CI flows.
Custom SQL expectations
Beyond the seven form-builder rule types above, the editor exposes a power-user escape hatch: Custom SQL (unexpected_rows_expectation). Write a SELECT that returns rows where the rule is broken; an empty result set is a pass.
{
"rule_type": "unexpected_rows_expectation",
"kwargs": {
"query": "SELECT id, amount, status FROM {batch} WHERE amount < 0 AND status = 'shipped'",
"description": "no shipped order has a negative amount",
"severity": "error"
}
}
The {batch} placeholder is the only template variable allowed. At run time it's substituted with the fully-qualified table name of the data asset bound to the checkpoint (e.g. "analytics"."raw_orders"). The schema and table parts are double-quoted before the substitution, so a table or schema name with unusual characters is treated as a single SQL identifier rather than as runnable SQL. The result drill-down shows up to 10 sample unexpected rows so you can inspect the failures without re-running the query yourself.
Safety model
The validator enforces an allowlist on the way in:
- The query must be a non-empty string containing
{batch}. - The query must start with
SELECTorWITH(leading whitespace and parentheses are skipped) — it has to be a read query that returns the broken rows. Anything else is rejected up front. - The query may not contain
;,--, or/*(statement chaining or comment-injection markers). - The query may not contain a write/DDL/DML or side-effecting verb as a whole word (case-insensitive):
INSERT,UPDATE,DELETE,DROP,TRUNCATE,ALTER,CREATE,GRANT,REVOKE,COPY,MERGE,CALL,EXEC,EXECUTE,DO,SET,LOCK,VACUUM, orANALYZE. - The query may not reference Postgres system catalogs or privileged built-ins (
pg_catalog,information_schema,pg_authid,pg_sleep,pg_read_file,dblink, and similar) — this blocks credential reads, catalog enumeration, thepg_sleepdenial-of-service, and filesystem disclosure. - A non-empty
descriptionis required so reviewers can tell what the rule asserts without reading the SQL.
The keyword filter is defense in depth, not the primary protection. The real safety boundary is the least-privilege read-only role configured on the customer datasource — see datasources. If a query that should be allowed (e.g. one that selects from a column literally named
created_at) trips the filter, please open a bug; we intentionally use word-boundary matching to avoid that.
Severity is also part of the rule contract: every entry in RuleSuite.suite.rules accepts an optional severity (info / warning / error / critical, default error) under kwargs.severity. The validation-result detail colour-codes each rule by its severity and the alert layer's AlertRule.min_severity (FEATURE-18) filters off the per-rule severity surfaced here — see Alerts → Severity routing. Shipped in FEATURE-10.
Row conditions
Column-map rules accept an optional row_condition that scopes the rule to a subset of rows. The canonical example: "shipping_address is not null when status = 'shipped'".
{
"rule_type": "expect_column_values_to_not_be_null",
"kwargs": {
"column": "shipping_address",
"row_condition": "status = 'shipped'"
}
}
The condition is a SQL boolean expression. PLACEHOLDER Cloud pairs it with Great Expectations' SQL condition parser at run time — the parser is fixed (we run against SQL backends; the pandas parser is out of scope) so you only write the expression. The validator rejects empty strings, semicolons, and DDL/DML keywords as defense in depth; the real safety boundary is the same least-privilege read-only role used everywhere else. Shipped in FEATURE-11.
If your condition is non-trivial — joins, subqueries, multiple tables — you probably want the full Custom SQL rule above instead.
Custom expectation plugins
If your team has a long-standing Great Expectations codebase with custom expectation classes, you can run them inside PLACEHOLDER Cloud without forking. An operator drops the Python package into the worker image, lists its module in EXPECTATIONS_PLUGIN_PACKAGES, and restarts the worker. The worker walks each package on startup, finds every Expectation subclass, and registers one row per (org, class) in public.custom_expectations. The rule-suite editor's "Custom expectations" section lists everything registered, and picking one renders a form derived from the class' parameter_schema.
A failing import is logged + skipped — discovery never blocks worker startup, and the org-settings "Custom expectations" page surfaces the per-package import status so an operator can see the failure message without tailing worker logs.
Out of scope for this milestone: uploading custom expectation Python source through the UI (sandboxed execution is a separate, hard problem) and versioned plugin manifests. Shipped in FEATURE-35.
Operations you can do
- Author — pick rules from the gallery, configure their kwargs, save.
- Edit — same form, persists in place. Per-suite change history is surfaced via the audit-events API (
GET /api/v1/organizations/{org_id}/audit-events?resource_type=rule_suite&resource_id={suite_id}); the suite detail page has a History button that opens that slice. Shipped in FEATURE-8. - Test against an asset — click Run now on the suite to enqueue an ad-hoc checkpoint without setting one up explicitly.
- Accept AI suggestions — when PLACEHOLDER Cloud's AI profiler proposes rules, you can append them to an existing suite via the accept flow.
Synthetic suites
PLACEHOLDER Cloud also writes "synthetic" suites — ones it produces from external signals rather than ones a user authored — when you ingest from third-party tools. The naming convention is <source>:<scope>; e.g. dbt artifact ingestion produces one synthetic suite per datasource named dbt:<datasource_id> that owns the imported run-result rows. The suite carries suite.synthetic = true and an empty rules list — it isn't a runnable suite, it's a labelling row that lets the UI group "dbt runs" separately from authored checkpoints. See Ingest dbt artifacts (shipped in FEATURE-27).