Governance
Governance runs are automated review checks that gate publishing. Before an asset can be promoted from is_test=true (visible only to the creator + admins) to live, it has to pass — or at least be reviewed against — a curated set of advisory and blocking checks.
The pipeline shipped in v0.0.62 along with Cobuild and the promote_to_live lifecycle. It is available for recipes, dashboards, and agents.
Checks
| Check | Severity | Asset types | What it verifies |
|---|---|---|---|
asset_resolved | blocking | recipe, dashboard, agent | The asset exists in (project, org) scope. A cross-tenant ID can't slip through. |
has_executable_body | advisory | recipe | The recipe has at least one step — not an empty stub. |
has_cards | advisory | dashboard | The dashboard has at least one card. |
has_tools | advisory | agent | The agent has at least one row in agent_tool_assignments. A toolless agent is a stub — the LLM can chat but can't query datasets / search a KB / call any external surface. |
no_unmasked_pii | advisory | recipe, dashboard, agent | Walks the assets' referenced datasets, classifies columns through masking.classify_columns, flags columns whose effective masking strategy is 'none' on a non-None sensitivity. |
Severity drives the verdict:
- A single blocking failure makes the run verdict
fail—promote_to_liverefuses to fire. - One or more advisory failures make the verdict
warn— promotion is still allowed, but the run records which checks flagged for audit. - All checks pass → verdict
pass.
no_unmasked_pii — masking resolution order
The PII check uses the same column classifier as the rest of the platform, so the rule resolution stays in one place:
- Dataset-level override —
datasets.settings.masking[col].strategy. - Org-level default —
organizations.data_policies.masking_defaults[semantic_type]. - Auto-mask —
PII_DEFAULTSbased on the column's inferred semantic type (email,phone,ssn,address, ...).
A per-dataset 'partial' rule beats an org-wide 'none' — tenants that opt out of an org policy via a dataset override remain unflagged. DB errors on the lookup degrade to "no defaults" (empty {}); the verdict falls back to whatever per-dataset overrides + auto-mask produce. Posture is degrade, don't block — a governance probe that can't load data_policies shouldn't 500 the activation gate.
Where governance runs from
Three entry points:
From Cobuild
The planner can call run_governance_review as a tool mid-loop. The verdict surfaces in the next PlanCard along with the contributing checks; if the verdict is pass, the planner can chain promote_to_live in the same approval batch. See Cobuild → Governance and promotion lifecycle.
From an asset page (recipe / dashboard / agent)
Recipe and dashboard pages carry a Run governance action that fires a one-off review. The result drawer lists each check's verdict and a friendly explanation for any warn / fail.
From the API
# Fire a review
curl -X POST https://platform.your-domain.com/api/governance/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "asset_type": "dashboard", "asset_id": 42 }'
# List runs in scope
curl https://platform.your-domain.com/api/governance/runs?asset_type=dashboard&asset_id=42 \
-H "Authorization: Bearer $TOKEN"
API reference
| Endpoint | Description |
|---|---|
POST /api/governance/run | Fires run_governance_review on one asset, persists the row, returns the serialized GovernanceRun. |
GET /api/governance/runs | Lists in (project, org) scope, newest first; optional asset_type + asset_id filters. Cap 200, default 50. |
Pydantic Literal["recipe", "dashboard", "agent"] rejects unsupported asset_type values at the boundary (HTTP 422).
Promote-to-live
A new is_test boolean on published_assets underpins the lifecycle:
- Newly created assets default to
is_test=true— visible only to the creator and org admins. - The
promote_to_liveplanner tool flipsis_test=false. The tool is gated by a passing F10 governance run: if there's noGovernanceRunrow withverdict='pass'for this asset within a reasonable window,promote_to_liverefuses and the planner surfaces the failing checks. - Once flipped, the asset becomes visible to everyone with the matching read permission (
dashboard.read,recipe.read, etc.).
Promotion is one-way in the current release — to revert an asset to test mode, edit it manually. A future release may add a demote_to_test lifecycle once we see the access patterns it should support.
Schema
governance_runs
├── id bigserial
├── project_id int
├── org_id int
├── asset_type text CHECK (asset_type IN ('recipe','dashboard','agent'))
├── asset_id int
├── verdict text CHECK (verdict IN ('pass','warn','fail'))
├── checks jsonb -- per-check result records
├── started_at timestamptz
├── finished_at timestamptz
└── created_by int
checks is a JSONB array of {name, severity, status, detail} entries — one per check that ran. Use JSONB containment queries to audit specific check histories ("which recipes ever had no_unmasked_pii: fail?").