| name | recipe-qa-rules |
| description | Full data QA flow for a recipe view โ discovers pipeline topology, manages QA Requirements Documents (QRDs), runs hardcoded and QRD-defined quality checks, detects JOIN fan-outs, and resolves issues with user approval. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
Trigger phrases: "check recipe", "recipe QA", "test recipe", "recipe duplicates", "recipe discrepancy", "write qa for recipe", "create QRD", "update QRD", "recipe requirements", "ะฟัะพะฒะตัั ัะตัะตะฟั", or when the view name ends in _recipe.
Phase 1 โ Recipe State Discovery
Step 1.1 โ Retrieve recipe meta info
Call getRecipeTool with the recipe identifier to get the recipe's current meta state (name, status, owner, last run timestamps, etc.).
Step 1.2 โ Check actual database state via system.tables
Query system.tables to discover which views exist for this recipe. A recipe may have any combination of:
| Suffix | Meaning |
|---|
_staging | Initial development view โ recipe SQL with a date-range filter applied. Created when the recipe is first saved. Used for iterative development on a limited data sample. |
_pre_prod | Activation-ready view produced after a successful run that passes all backward-compatibility and complexity validations (column deletions, type changes, MAX_EXECUTION_TIME, MAX_MEMORY_USAGE). Replaced on each successful run. |
| (no suffix / prod) | The current production view. Replaced when the user clicks Activate โ the _pre_prod view is promoted in place. |
SELECT name, engine, create_table_query
FROM system.tables
WHERE database = '<db>'
AND name LIKE '%<recipe_base_name>%'
State interpretation matrix:
| Found views | Interpretation |
|---|
_staging only | Recipe is in active development, never activated |
_staging + _pre_prod | Recipe passed validation, awaiting activation |
_staging + prod | Recipe is live; _staging still present from dev |
_staging + _pre_prod + prod | Normal mid-activation state |
| prod only | Recipe activated, staging cleaned up |
| Neither | Unexpected โ activation may have failed |
If there is a state inconsistency that prevents clear QA (e.g., prod view exists but meta says staging, or _pre_prod is stale), stop and invoke notebook-editor-skill first to resolve the state before proceeding.
Phase 1.5 โ QRD Lookup & Generation
Step 1.5.1 โ Check for existing QRD
Search for an existing QA Requirements Document for this recipe:
listDocumentsTool(tag: "qrd, recipe:{notebook_id}")
If no results, try a fallback search:
listDocumentsTool(query: "QRD: {recipe_title}")
If multiple QRDs are found for the same recipe, use the most recently updated one. Warn the user about the duplicates so they can clean up.
Step 1.5.2a โ No QRD exists (First Run Flow)
When no QRD is found, generate one from the recipe:
-
Analyze the recipe โ call getRecipeStepTool(notebookId, stepId) for each step listed in the getRecipeTool response. Extract:
- Source tables (all
FROM / JOIN references)
- Column names and inferred types
- Filters,
GROUP BY, DISTINCT patterns
- Calculated fields and business formulas (e.g.,
spend / clicks AS cpc)
- JOIN conditions and JOIN types
- Any deduplication logic (ROW_NUMBER, QUALIFY, DISTINCT)
-
Fill the QRD template โ use qrd_template.md as the skeleton. Populate each section:
- Transformation Summary: one paragraph describing what the recipe does end-to-end
- Input Contract: all source tables with their columns (from step SQL)
- Output Contract: final view columns (from the last step or
SHOW CREATE VIEW)
- Data Quality Rules: infer from column types โ e.g.,
spend >= 0 for monetary columns, NOT NULL for ID columns
- Deduplication Rules: infer grain from
GROUP BY or DISTINCT patterns
- Business Logic Rules: infer from calculated fields โ e.g.,
cpc = spend / clicks
- Row Count Expectations: default to
output rows > 0 for first run
- Freshness Rules: default to
max(date) within 48 hours of today()
- Version History:
v1.0 with current date and session ID
-
Present the draft to the user โ show the complete QRD with clear explanations of what was inferred vs assumed. Ask the user to review, edit, add, or remove rules.
-
Wait for user approval. Do not proceed until the user explicitly approves the QRD.
-
Save the QRD โ after approval, call:
createDocumentTool(
title: "QRD: {recipe_title} ({view_name})",
tags: "qrd, recipe:{notebook_id}, view:{view_name}",
content: <approved QRD markdown>
)
-
Proceed to Phase 2.
Step 1.5.2b โ QRD exists (Subsequent Run Flow)
When a QRD is found:
- Call
getDocumentTool(id) to load the full QRD content.
- Parse the QRD by
## section headers into structured data for Phase 3.
- Proceed to Phase 2.
Phase 2 โ Pipeline Topology
Step 2.1 โ Fetch the target view DDL
Use SHOW CREATE VIEW on the view that represents the current intended state (prefer _pre_prod if activation is pending, otherwise prod):
SHOW CREATE VIEW <db>.<view_name>
Extract:
- All
FROM / JOIN source references
- Key dimension columns (date, ids, datasource)
- Numeric metric columns (spend, impressions, clicks, conversions, revenue)
- JOIN types and conditions (for fan-out detection in Phase 3)
Step 2.2 โ Recursively explore upstream dependencies
Any table or view reference without an _all_data suffix is not raw data โ it is a derived recipe or view. For each such reference, recursively fetch its DDL and repeat until only _all_data sources remain. Build a full upstream dependency tree.
Step 2.3 โ Discover downstream dependencies
Search for views that reference the current recipe view:
SELECT name, create_table_query
FROM system.tables
WHERE database = '<db>'
AND create_table_query LIKE '%<view_name>%'
AND name != '<view_name>'
Collect all downstream consumers into the dependency tree.
Step 2.4 โ Document the pipeline (if non-trivial)
If the combined upstream + downstream tree contains more than one recipe layer or more than two nodes total:
- Search existing documents for any prior pipeline or recipe documentation (
listDocuments / getDocument) to reference or update rather than duplicate.
- Create a new document via
createDocument with:
- A prose description of the data pipeline
- A Mermaid diagram of the full node graph (sources โ recipes โ consumers)
- State of each node (staging / pre-prod / prod)
Phase 3 โ Quality Rule Application
Step 3.1 โ Select applicable hardcoded rules
Read the rule files in this directory and determine which apply given the pipeline schema:
Auto-detect which rules are relevant:
- Any recipe with a defined grain โ duplicate check applies (Template A or B)
- Any recipe with JOINs โ JOIN fan-out check applies (Template C in duplicate_check.md)
- Any recipe with aggregated metrics from
_all_data sources โ source discrepancy applies
If the user specified a particular check, generate only that one. Otherwise generate all applicable checks.
Step 3.2 โ Infer datasource name
Infer the datasource display name from the source table prefix. Common mappings:
facebook โ Facebook Ads
google_ads โ Google Ads
linkedin_ads โ LinkedIn Ads
dbm / dbmbp โ Google Display and Video 360
ttd โ The Trade Desk
amazon_dsp / ams_dsp โ Amazon DSP
For unknown prefixes, infer the display name from the table prefix by converting underscores to spaces and capitalizing each word (e.g., tiktok_ads โ TikTok Ads, snapchat โ Snapchat).
Step 3.3 โ Generate check queries
For each applicable rule, generate the SQL following the template in the corresponding rule file. Rules must cover:
- Direct data quality on the recipe itself: duplicates, nulls, zeroes, unexpected metric values
- JOIN integrity: ensure JOINs did not cause fan-out (row multiplication)
- Pipeline integrity: ensure changes to the recipe do not silently break downstream consumers (column set, types, grain stability)
Compute efficiency requirements โ mandatory for all queries:
- Always apply a date-range filter (
toStartOfMonth(today()) or similar) โ never scan unbounded history
- Use
SAMPLE clauses where statistical approximation is acceptable
- Prefer
countIf / sumIf over subqueries where possible
- Avoid
SELECT * โ always enumerate columns
- Add
LIMIT to any exploratory or diagnostic queries
- Do not run the same aggregation twice; use CTEs to share intermediate results
Step 3.4 โ Output format
Each check: a single code block with a comment header (-- Duplicate Check, -- Source Discrepancy, -- JOIN Fan-Out Check). No trailing semicolons. No CREATE VIEW wrappers.
Step 3.5 โ Logic verification
Run each query via the MCP query tool and evaluate all output values for logical consistency:
- Duplicate check:
max_cnt must be โฅ 1. If 0, the table is empty or the date filter is too narrow.
- Source discrepancy: both
all_data_* and recipe_* values must be > 0. If either is 0, the source table or date filter is wrong.
- JOIN fan-out:
fan_out_ratio > 1.5 indicates likely row multiplication. If 0 or NULL, check that the date filter returned rows.
If anything looks wrong, identify the root cause, fix the query logic, and re-run.
Step 3.6 โ Validation
Validate results: row count 1โ5000, mandatory columns present (check_entity_id, datasource, check_result), check_entity_id unique, datasource / check_result contain valid values. Regenerate if invalid.
Step 3.7 โ Update pipeline document
After all hardcoded checks are complete, update the pipeline document created in Phase 2 with a summary of rules applied, checks run, and their outcomes.
Step 3.8 โ QRD-Defined Validation
Skip this step if no QRD was loaded in Phase 1.5 (user rejected, first run not yet approved, or error). Run only hardcoded checks in that case.
When a QRD is available, generate and run validation queries for each QRD section:
3.8.1 โ Data Quality Rules
For each rule in the QRD's Data Quality Rules table, generate a SQL query:
SELECT
count(*) AS violation_count,
multiIf(count(*) > 0, 'rule broken', 'rule followed') AS check_result
FROM <db>.<recipe_view>
WHERE date >= toStartOfMonth(today())
AND NOT ({sql_expression_from_qrd})
Run the query. Report pass/fail for each rule.
3.8.2 โ Deduplication Rules
Generate a duplicate check using the grain columns specified in the QRD (not the generic grain from the hardcoded check). Use the same 5-CTE pattern as Template A/B in duplicate_check.md but with QRD-specified grain columns.
If the hardcoded duplicate check and QRD dedup check both run, the QRD version is authoritative for reporting but both results are logged.
3.8.3 โ Business Logic Rules
For each business logic rule in the QRD, generate a SQL check. Use tolerance-based comparison for floating-point values:
SELECT
count(*) AS violation_count,
multiIf(count(*) > 0, 'rule broken', 'rule followed') AS check_result
FROM <db>.<recipe_view>
WHERE date >= toStartOfMonth(today())
AND NOT ({sql_expression_from_qrd})
3.8.4 โ Row Count Expectations
Run the row count SQL from the QRD and compare the result against the expected range:
SELECT count(*) AS row_count
FROM <db>.<recipe_view>
WHERE date >= toStartOfMonth(today())
Fail if row_count falls outside the QRD's expected bounds.
3.8.5 โ Freshness Rules
Run the freshness SQL from the QRD:
SELECT
max(date) AS latest_date,
dateDiff('hour', max(date), now()) AS hours_stale
FROM <db>.<recipe_view>
Fail if hours_stale exceeds the QRD's max staleness threshold.
3.8.6 โ Consolidated results
After all checks (hardcoded + QRD), produce a combined summary table:
| Rule Source | Rule Name | Result | Details |
|-------------|-----------|--------|---------|
| Hardcoded | Duplicate Check | PASS/FAIL | max_cnt = X |
| Hardcoded | Source Discrepancy | PASS/FAIL | diff = X% |
| Hardcoded | JOIN Fan-Out | PASS/FAIL | ratio = X |
| QRD | {rule description} | PASS/FAIL | {actual vs expected} |
| ... | ... | ... | ... |
Phase 4 โ Issue Resolution
Step 4.1 โ Root cause investigation
If any check reveals a data quality or pipeline integrity issue, investigate potential root causes in the DDL and upstream dependencies.
Step 4.2 โ Expected vs Actual reporting
For every failed check, present:
- Rule: which rule failed and its source (hardcoded or QRD)
- Expected: the condition that should hold (from the rule definition)
- Actual: the observed value (from the query result)
- Likely cause: which recipe step or JOIN most likely produces the violation
Step 4.3 โ Fix proposal with approval gate
For each proposed fix:
- Show the concrete change (before โ after: SQL modification, filter addition, type cast, etc.)
- Explicitly ask the user: "Shall I apply this fix?"
- NEVER apply a fix without explicit user confirmation. This applies to ALL fixes, including trivial ones.
- After the user approves and the fix is applied, re-run the failed check to confirm resolution.
- If the re-run still fails, report the new state and discuss next steps with the user.
Step 4.4 โ Escalate complex issues
If the issue is complex or requires a design decision (e.g., grain change, backfill strategy, breaking schema change), create a separate document via createDocument describing:
- The issue and its observed symptoms
- Root cause analysis
- Decision options with trade-offs
- Recommended resolution path
This allows the user to handle it in a focused follow-up session.
Phase 5 โ QRD Maintenance
Skip this phase entirely on first run (when the QRD was just created in Phase 1.5). Only run when a QRD already existed before this QA session.
Step 5.1 โ Detect recipe changes
Compare the current recipe state against the QRD:
- Re-read all recipe steps via
getRecipeStepTool.
- Compare source tables against the QRD's Input Contract โ look for added/removed tables or columns.
- Compare output columns against the QRD's Output Contract โ look for schema changes.
- Check for changes in JOIN logic, filters, calculated fields, or deduplication strategy.
Step 5.2 โ Assess impact
If changes are detected:
- List each change (added column, removed source, modified formula, new JOIN)
- For each change, identify which QRD sections are affected
- Classify as: "QRD update required" or "QRD still valid"
Step 5.3 โ Propose QRD edits
If updates are needed, for each affected section:
- Show the current QRD section content
- Show the proposed replacement
- Explain why the change is needed (which recipe modification triggered it)
Step 5.4 โ User approval and update
You MUST present all proposed QRD changes to the user and wait for explicit approval before updating.
After approval:
- Call
editDocumentTool(id, content, tags) with the full updated QRD content.
- Add a new entry to the Version History section:
| v{N} | {YYYY-MM-DD} | {description of changes} | {session_id} |
- Confirm the update was saved successfully.
Step 5.5 โ No changes detected
If no recipe changes are detected relative to the QRD, skip this phase and report: "QRD is up to date โ no changes detected since last QA run."
Additional resources