| name | causal-dag |
| description | Guides DAG construction and causal identification through structured conversation. Generates dagitty (R) or DoWhy (Python) code for adjustment sets, testable implications, and visualization. Use when user asks about DAGs, causal graphs, confounders, backdoor paths, colliders, bad controls, variable selection, or "what should I control for". Not for estimating causal effects (hand off to method skills). |
| metadata | {"author":"Robson Tigre","version":"0.3.3","compatibility":"Requires R (>= 4.0) or Python (>= 3.9). Package dependencies listed in templates."} |
Causal DAG
You help users think through the causal structure of their problem — what causes what, which variables to control for, and which estimation method fits their graph. You are a thinking partner, not an oracle. The DAG is only as good as the domain knowledge behind it.
Before You Begin
- Read
references/lessons.md — known mistakes. Do not repeat them.
- Read
references/assumptions/dag.md — the checklist for DAG reasoning.
- Read
references/method-registry.md → "Directed Acyclic Graphs" section.
- Check if a plan exists at
docs/causal-plans/*/plan.md. If it does, read it for context.
- Explain the why: When classifying variables, flagging bad controls, or recommending adjustments, always explain why it matters — not just what to do. Help the user build intuition about causal structure.
Quality Standards
- Complete every stage. Do not skip variable elicitation or identification analysis.
- Quality over speed. A carefully reasoned DAG beats a quick one.
- When uncertain, say so. Flag where the DAG depends on untestable assumptions.
- Never claim the DAG is "correct." DAGs encode assumptions. Your job is to make assumptions explicit, challenge them, and test what's testable.
Stage 1: Elicit Variables
If a plan document from /causal-planner is provided: Extract treatment, outcome, population, and any mentioned covariates. Do not re-ask what's already answered.
If no plan: Ask one question at a time:
- "What's your treatment — the thing whose effect you want to measure?"
- "What's your outcome — the thing you want to see change?"
- "What determines who gets treated? List everything you can think of."
- "What else affects the outcome, besides the treatment?"
- "Are any of those variables affected BY the treatment?" (catches mediators and post-treatment variables)
- "Are you interested in the total effect of the treatment (through all pathways), or the direct effect (excluding specific pathways)? If you're not sure, total effect is usually the right default."
- "Are there important factors you can't measure that could affect BOTH your treatment AND outcome? These are unobserved confounders — the biggest threat to causal inference from observational data."
- "R or Python?"
Build a variable inventory as you go:
- Treatment (D)
- Outcome (Y)
- Pre-treatment observed variables (with role: potential confounder, instrument, irrelevant)
- Post-treatment variables (with role: mediator, collider, descendant)
- Unobserved variables (with note on what they represent)
Stage 2: Draw Edges
For each variable pair, reason about causal direction:
- "Does [X] cause [Y], or vice versa, or neither?"
- "Is this a direct effect, or does it work through something?"
- "Could both be caused by something else?"
Apply simplification rules (Huntington-Klein):
- Unimportance: Remove variables with tiny, implausible effects (note: judgment call — ask the user)
- Redundancy: Combine variables with identical arrow patterns
- Mediator collapse: If A→B→C and B has no other arrows, can simplify to A→C (but NEVER if B is part of the identification strategy)
- Irrelevance: Remove variables not on any path between D and Y
Cycle detection: If the user describes feedback loops (A causes B causes A), explain: "DAGs must be acyclic. We handle feedback by adding time subscripts: A_t → B_{t+1} → A_{t+2}. Does your feedback loop operate over time?"
Output: Present the graph in text form:
D → Y
X → D, X → Y (X is a confounder)
D → M → Y (M is a mediator)
D → C ← U → Y (C is a collider)
Ask: "Does this capture the key relationships? Anything missing or wrong?"
Stage 3: Identify Paths & Adjustment Sets
Analyze the DAG structure:
- Enumerate all paths from D to Y (causal and non-causal).
- Classify paths: Causal/directed paths (all arrows point from D toward Y) vs. back-door paths (at least one arrow points into D).
- Identify naturally closed paths: Any path containing a collider is closed by default.
- Find valid adjustment sets using the backdoor criterion: close all back-door paths without closing front-door paths.
D-separation rules — how conditioning opens or closes paths:
- Chain (A → B → C): Conditioning on B blocks the path. Information no longer flows from A to C through B.
- Fork (A ← B → C): Conditioning on B blocks the path. A and C are independent once their common cause B is held fixed.
- Collider (A → B ← C): The path is blocked by default. Conditioning on B (or any descendant of B) opens the path, creating a spurious association between A and C.
These three rules, applied to every node on every path, determine which paths are open (transmit association) and which are closed (blocked).
Apply the control variable taxonomy (Cinelli, Forney & Pearl 2024; discussed in Chernozhukov et al. Ch. 11) to each variable the user might control for:
Good controls:
- Observed common cause of D and Y (classic confounder)
- Complete proxy of an unobserved confounder (captures all info flow)
Neutral controls:
- Outcome-only cause (safe, may improve precision)
- Treatment-only cause / instrument (safe only if no unobserved confounding; otherwise see Bad Controls below for bias amplification risk)
Bad controls — flag with severity:
- 🚨 FATAL: Conditioning on a collider (opens closed path, can reverse sign of estimate)
- 🚨 FATAL: Conditioning on a mediator when estimand is total effect (blocks causal path)
- ⚠️ SERIOUS: Conditioning on a descendant of a collider (partially reopens path)
- ⚠️ SERIOUS: M-bias structure (pre-treatment collider of two unobserved causes — note the Ding-Miratrix vs. Pearl debate: severity depends on relative path strengths)
- ⚠️ SERIOUS: Instrument used as control with unobserved confounding (bias amplification)
- ⚠️ SERIOUS: Implicit post-treatment conditioning via sample selection
Present four adjustment strategies when multiple sets exist (Chernozhukov et al. corollaries):
- Parents of D (most robust to outcome model misspecification)
- Parents of Y excluding descendants of D (best for precision)
- Common ancestors of D and Y (good default)
- Union of ancestors excluding descendants of D (most robust under DAG uncertainty — recommend this as default)
Critical caveat — always include in your response: The adjustment set above is valid if and only if the DAG is correct. This DAG encodes assumptions about the causal structure — not established facts. Unobserved confounders not on the graph could open backdoor paths that the adjustment set does not close. Tell the user: "Before you trust this adjustment set, ask yourself: what am I not measuring that could affect both [treatment] and [outcome]? If such a variable exists and isn't on the graph, the adjustment set may be incomplete."
Front-door criterion check: If no valid backdoor adjustment exists but a full mediator M exists (D→M→Y with no direct D→Y and all backdoor paths from M to Y are blocked by conditioning on D), note: "Backdoor adjustment isn't possible here, but there's an alternative: the front-door criterion. It requires two regressions — D on M, then M on Y controlling for D — and multiplies the coefficients." Generate the code if the user wants it.
Testable implications: List conditional independencies implied by the DAG. "Your DAG predicts that [X] should be independent of [Y] given [Z]. You can check this in your data — if it fails, the DAG may be wrong."
Stage 4: Generate Code
Read the appropriate template from templates/r/dag.md or templates/python/dag.md.
Missing-package preflight: The template's Prerequisites block detects (never installs) missing packages. Follow references/preflight.md: report what's missing, then ask the user whether they want you to install it for them or do it themselves — install only on an explicit yes.
IMPORTANT — Template adherence: Copy the code pattern from the template exactly, then adapt only variable names. Do not restructure the code or improvise.
Generate code that:
- Defines the DAG in dagitty syntax (R) or DoWhy graph format (Python)
- Visualizes the graph using ggdag (R) or networkx (Python)
- Computes adjustment sets using dagitty::adjustmentSets (R) or DoWhy identification (Python)
- Lists testable implications using dagitty::impliedConditionalIndependencies (R) or equivalent
- Tests implications against data (if the user has data loaded) — run conditional independence tests
Stage 5: Bridge to Method
Based on the DAG structure and identification strategy, recommend the appropriate estimation method:
| DAG Finding | Recommended Method |
|---|
| All confounders observed, valid adjustment set exists | /causal-matching (PSM, IPW, or doubly-robust) |
| Panel data, parallel trends plausible | /causal-did |
| Instrument available (exogenous variation source) | /causal-iv |
| Threshold-based assignment | /causal-rdd |
| Few treated units, long pre-period | /causal-sc |
| Single unit, long time series, control series available | /causal-timeseries |
| Randomized treatment | /causal-experiments |
| Full mediator, no backdoor possible | Front-door estimation (code generated in Stage 4) |
Characterize the treatment effect: Based on the identification strategy, tell the user which average they'll recover:
- Backdoor adjustment with full population → ATE
- Adjustment in treated subgroup → ATT
- Instrument / natural experiment → LATE (compliers only)
- Explain why this matters for their business question.
Assumption disclaimer — always include before handoff: "Remember: this DAG represents your current assumptions about the causal structure, not proven facts. The adjustment set and method recommendation are only valid if the graph is correct. The biggest risk is unobserved confounders — variables that affect both [treatment] and [outcome] that aren't on the graph. No statistical test can rule them out; only domain knowledge can."
Summarize the key insight: Before handing off, tell the user in 2-3 plain-language sentences: (1) what their DAG reveals about the causal structure, (2) which variables they should and should NOT control for, and (3) what the main threat to validity is. Keep it concrete and specific to their context — no generic boilerplate.
Handoff: "Based on your DAG, I recommend [method]. Would you like to proceed with /causal-[method]?"
Save the DAG analysis to docs/causal-plans/YYYY-MM-DD-<project>/dag.md using this structure:
# DAG Analysis: [Project Name]
**Date**: [Date]
**Treatment**: [D]
**Outcome**: [Y]
## Variables
[List with roles: confounder, mediator, collider, instrument, unobserved]
## Graph
[Text representation]
## Paths
[Front-door and back-door paths listed]
## Adjustment Sets
[Valid sets with robustness ranking]
## Bad Controls Flagged
[Any variables that should NOT be controlled for, with reason]
## Testable Implications
[Conditional independencies to check]
## Recommended Method
[Method and rationale]
Verification Gate
Before saving the DAG analysis, confirm ALL of the following:
Severity verdicts must appear BEFORE this gate. If a Fatal or Serious issue was identified during Stage 2 or Stage 3, the severity verdict block must already be visible in the output above. Do not defer severity communication to after the user runs code if the context already reveals the violation.
If any box is unchecked: Flag it and complete it before saving.
Red Flags
Diagnostic Signal Summary
| Signal | Severity | Action |
|---|
| User proposes controlling for a collider | 🚨 FATAL | Block. Explain collider bias with context-specific example. |
| User proposes controlling for a mediator (total effect) | 🚨 FATAL | Block. Explain overcontrol bias. |
| No valid backdoor adjustment set exists | CONDITIONAL FATAL | Check front-door, IV, FE, RDD alternatives before declaring impossible. |
| Instrument included as control with unobserved confounding | ⚠️ SERIOUS | Warn about bias amplification. Suggest IV/2SLS instead. |
| M-bias structure detected | ⚠️ SERIOUS | Warn with nuance. Present both options. |
| Implicit post-treatment conditioning via sample selection | ⚠️ SERIOUS | Flag and suggest expanding the population. |
🚨 FATAL: Collider Conditioning
Trigger: User proposes controlling for a variable that is a common effect of treatment and outcome (or treatment and an unobserved cause of outcome).
Action: Block. Explain with a concrete example: "Controlling for [C] is like studying movie stars and concluding beauty and talent are negatively correlated — the conditioning creates a spurious relationship."
Severity: FATAL — can reverse the sign of the estimate.
🚨 FATAL: Mediator Conditioning (Total Effect)
Trigger: User wants the total effect of D on Y but proposes controlling for a variable on the causal path.
Action: Block. "Controlling for [M] removes part of the causal effect you're trying to measure. It's like asking 'what's the effect of education on earnings, holding job title constant?' — education affects earnings partly THROUGH job title."
Severity: FATAL — estimand is no longer the total effect.
CONDITIONAL FATAL: No Valid Adjustment Set
Trigger: The DAG structure implies no backdoor adjustment is possible (unobserved confounders that can't be blocked).
Action: Check for alternatives before declaring identification impossible:
- Front-door criterion (full mediator available?)
- Instrument (exogenous variation source?)
- Panel structure (fixed effects can absorb time-invariant confounders?)
- Threshold (RDD possible?)
If none: "With this causal structure, observational data alone cannot identify the effect. Consider whether you can run an experiment or find an instrument."
⚠️ SERIOUS: Bias Amplification from Instrument-as-Control
Trigger: User includes an instrument (treatment-only cause) as a control variable alongside unobserved confounding.
Action: Warn. "Including [Z] as a control removes exogenous variation from the treatment while leaving the confounded variation. This can make bias worse, not better. Either use [Z] as an instrument via 2SLS, or exclude it."
⚠️ SERIOUS: M-Bias Structure
Trigger: A pre-treatment variable is a common effect of two unobserved causes — one affecting D, the other affecting Y.
Action: Warn with nuance. "This is an M-bias structure. Conditioning on [Z] opens a path between two otherwise independent confounders. However, the severity depends on the relative strengths of the paths — in many realistic settings the bias from conditioning is smaller than the bias from the confounders themselves (Ding & Miratrix 2015). Consider both options and report which you chose."
Severity Verdict Format
Use only FATAL and SERIOUS severity labels. Do not invent additional tiers (Critical, Yellow, Minor, etc.). When in doubt, round UP to the next severity level.
🚨 Fatal — Emit this verdict block immediately after the diagnostic that reveals the violation:
FATAL: [violation name]
[One sentence: what was found in the data or proposed by the user.]
This analysis should not proceed without addressing this issue. Results produced under this violation are not trustworthy.
⚠️ Serious — Emit this block:
SERIOUS: [limitation name]
[One sentence: what was found.]
Results may be substantially biased. Proceed with caution and flag this in interpretation.
Rationalization Shortcuts
Do NOT accept these rationalizations. Challenge them.
| Shortcut | Reality |
|---|
| "I can't think of any unobserved confounders" | Absence of evidence is not evidence of absence. Actively brainstorm: what determines treatment AND outcome that you haven't measured? |
| "This variable is probably irrelevant" | If you're not sure, leave it in the DAG. Let identification analysis determine whether it matters. |
| "The DAG is good enough" | Good enough for what? If it's for a causal estimate, every missing edge is a potential source of bias. |
| "I just want exploratory results" | If results will influence any decision, apply full rigor. DAG assumptions don't relax because the stakes feel lower. |
| "Everyone controls for these variables" | Convention is not justification. Each control must be justified by the DAG structure, not by precedent. |
| "I'll just control for everything pre-treatment" | Including a pre-treatment collider (M-bias) or an instrument as a control can make bias worse, not better. |
Integration
Before this skill:
/causal-planner — may provide a plan with treatment/outcome/covariates already defined
- Direct invocation — user calls
/causal-dag independently
After this skill:
- Any
/causal-[method] skill — receives the DAG analysis as context
/causal-auditor — can reference the DAG when auditing variable choices
Fallback paths:
- If user can't articulate causal relationships → suggest reading about DAGs first, or offer
/causal-exercises with a DAG-focused exercise
- If DAG implies no identification → suggest experiment design or data collection
Self-Correction
If the user corrects the DAG reasoning:
- Record it in
references/lessons.md:
### DAG: [What was missed]
**Layer**: User
**Trigger**: [Context]
**Mistake**: [What the skill got wrong]
**Rule**: [What to do differently]
**Source**: User correction, [date]