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 thegetRecipeToolresponse. Extract:- Source tables (all
FROM/JOINreferences) - Column names and inferred types
- Filters,
GROUP BY,DISTINCTpatterns - Calculated fields and business formulas (e.g.,
spend / clicks AS cpc) - JOIN conditions and JOIN types
- Any deduplication logic (ROW_NUMBER, QUALIFY, DISTINCT)
- Source tables (all
-
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 >= 0for monetary columns,NOT NULLfor ID columns - Deduplication Rules: infer grain from
GROUP BYorDISTINCTpatterns - Business Logic Rules: infer from calculated fields — e.g.,
cpc = spend / clicks - Row Count Expectations: default to
output rows > 0for first run - Freshness Rules: default to
max(date) within 48 hours of today() - Version History:
v1.0with 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/JOINsource 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
createDocumentwith:- 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:
- Duplicate check (includes JOIN fan-out detection) → duplicate_check.md
- Source discrepancy → source_discrepancy.md
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_datasources → 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 Adsgoogle_ads→Google Adslinkedin_ads→LinkedIn Adsdbm/dbmbp→Google Display and Video 360ttd→The Trade Deskamazon_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
SAMPLEclauses where statistical approximation is acceptable - Prefer
countIf/sumIfover subqueries where possible - Avoid
SELECT *— always enumerate columns - Add
LIMITto 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_cntmust be ≥ 1. If 0, the table is empty or the date filter is too narrow. - Source discrepancy: both
all_data_*andrecipe_*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
- QRD template: qrd_template.md
- Duplicate check template + grain columns + JOIN fan-out: duplicate_check.md
- Source discrepancy templates (single + multi-source): source_discrepancy.md