bigquery-sql
BigQuery-specific SQL patterns: UNNEST for array expansion, STRUCT, ARRAY_AGG,…
Write a technical spec after research. Distills exploration into structured decisions. On retries, read the existing spec instead of re-researching.
$ npx -y skills add signalpilot-labs/signalpilot --skill knowledge-base --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/knowledge-baseContext preview
The summary Claude sees to decide when to auto-load this skill.
Write a technical spec after research. Distills exploration into structured decisions. On retries, read the existing spec instead of re-researching.
name: knowledge-base description: "Write a technical spec after research. Distills exploration into structured decisions. On retries, read the existing spec instead of re-researching."
Check if `<project_dir>/technical_spec.md` exists.
After Step 5 research, MUST write `<project_dir>/technical_spec.md`.
The spec distills exploration into decisions. NOT a copy of research output. NOT SQL. NOT domain knowledge. The spec answers: "what will I build, from what sources, with what joins and filters, and why?"
STOP writing the spec when every model has all seven fields from Section 3 and every decision in Section 4 has WHAT + WHY + EVIDENCE.
For EACH model, MUST include ALL seven fields:
1. **Source**: `ref('model_name')` or raw table name. If an intermediate model already computes a value you need, MUST write `ref('that_model')`. Do NOT recompute from raw tables - recomputing introduces precision drift from rounding order differences. Before finishing the spec, check it against itself: when Key expressions names a model as the source of an expression, Source must list `ref()` of that same model - fix whichever is wrong. 2. **Driving table**: which table in the FROM clause. State the row count from the Step 1 scan or a `query_database` COUNT. 3. **Joins**: each join with type (LEFT/INNER/FULL OUTER) and exact keys. "Join some dimension table" is not a plan. `LEFT JOIN dim_customers ON fct.customer_id = dim.customer_id` is a plan. 4. **Key expressions**: computed columns. If a sibling or upstream model defines this expression, copy it exactly and name the source model and column. Untraced expression reuse causes silent divergence. 5. **Filters**: WHERE/HAVING clauses with justification (domain skill rule, YML description, or `query_database` discovery). If no filter, write "none." Unjustified filters silently drop rows and break row counts. 6. **Expected grain**: what one row represents (one per customer, one per line item, one per day-product pair). 7. **Expected rows**: from `query_database` cardinalities or entity counts. Do NOT guess.
Example:
## Model: fct_revenue
- Source: ref('int_order_items') - already computes item_total, do NOT recompute
- Driving table: int_order_items (600K rows)
- Joins: dim_customers ON customer_id = customer_id (LEFT JOIN)
- Key expressions:
- total_revenue = SUM(item_total) [from int_order_items.item_total]
- order_count = COUNT(*)
- Filters: WHERE status != 'returned' (domain-ecommerce: exclude returns from revenue)
- Expected grain: one row per customer
- Expected rows: ~75K (query: 75K distinct customers excluding returns)MUST include a Decisions section after all model plans. Each decision states WHAT you chose, WHY, and what EVIDENCE supports it. These are the choices a reviewer needs to verify your work.
One decision per line. Example:
## Decisions - int_order_items computes item_total -> fct_revenue refs it (avoids precision drift from recomputation) - Returns excluded in fct_revenue, not in int_order_items (intermediate preserves all rows for other consumers) - LEFT JOIN dim_customers (query: 75K customers vs 73K in dim - some deleted, keep all) - COUNT(*) not COUNT(DISTINCT order_id) (grain check: COUNT(*) != COUNT(DISTINCT) on int_order_items)
MUST list models in dependency order at the top of the spec. For each model, note its layer (staging/intermediate/mart) and dependencies. Build failures from wrong ordering waste turns on debugging instead of building.
## Build Order 1. stg_payments (staging - no dependencies) 2. int_order_items (intermediate - depends on stg_payments) 3. fct_revenue (mart - depends on int_order_items)
When a verifier reports FAIL, update `technical_spec.md` BEFORE changing SQL. The spec is the source of truth - SQL follows the spec, not the other way around. Changing SQL without updating the spec causes the spec to drift, making future retries unreliable.
1. Find the model section in the spec. 2. Edit the field that caused the failure. 3. Add a decision line explaining the change. 4. Rewrite the SQL from the updated spec.
rules live in the dbt-write skill. The spec records your DECISIONS about which rules apply, not the rules themselves.
Repo: signalpilot-labs/signalpilot
BigQuery-specific SQL patterns: UNNEST for array expansion, STRUCT, ARRAY_AGG,…
Load when dbt run or dbt parse fails. Covers YML duplicate patches, ref errors, passthrough…
Populate the knowledge base from dbt project research. Proposes entries across all 6…
Load when task involves dbt snapshots, SCD Type 2, or tracking data changes over time. Covers…
Load when task involves adding, writing, or fixing dbt unit tests. Covers unit_tests YAML…
Load when task involves dbt model versioning, creating v2 of a model, or backward-compatible…