---
name: "data_quality_auditor"
description: "Find and triage data quality issues in CSV datasets: profile columns, run reproducible checks for completeness, uniqueness, validity, consistency, and timeliness, log each issue with evidence, grade severity, and decide fix, quarantine, or block. Triggers when an agent needs to audit a dataset, profile a table, investigate bad or duplicate data, or set up repeatable quality checks before publishing or loading data."
---

# Data Quality Auditor

## Purpose

Most bad data is found by a consumer, after it has already shaped a report or a model. This skill moves that discovery earlier. It profiles a CSV, runs a fixed set of checks from a checks file that lives with the data, and writes an issue log where every finding has evidence: how many rows, what share, which values, which row numbers. Each issue gets a severity grade and a decision, fix, quarantine, or block, so the next step is clear instead of argued from scratch each time.

The checks map to the six DAMA UK dimensions used in DAMA-DMBOK: completeness, uniqueness, timeliness, validity, accuracy, and consistency. The bundled script covers five of them directly. Accuracy, whether a value matches the real world, needs a trusted second source to compare against. The reference check is the closest scripted proxy. Label accuracy findings as comparisons against a named source, never as proof on their own.

## Workflow

1. **Scope the audit.** Name the dataset, its grain (what one row means), its owner, and its consumers. Mark the critical columns: keys, amounts, and any column a consumer makes decisions from. Quality is fitness for a specific use, so a column that does not matter to any consumer does not get critical grading.
2. **Profile before you judge.** Run the profiler with no checks file first. Read null rates, distinct counts, inferred types, and min and max values. Surprises here, a numeric column full of strings, a status column with 40 distinct values, point at the checks worth writing.
3. **Write the checks file.** Turn what the data owes its consumers into explicit checks: required columns, not_null, unique on the grain, accepted_values for coded columns, ranges for measures, patterns for formatted fields, a reference check for foreign keys, and a freshness floor for dated data. Start from `assets/example-checks.json`. Thresholds and value lists come from the owner or the contract, never from a guess.
4. **Run the audit.** Run the profiler with the checks file and `--quarantine`. Read summary.md, then issues.json for the evidence behind any grade you plan to act on.
5. **Triage each issue.** Follow `references/triage-guide.md`: confirm the check is right, assign one owner, apply fix, quarantine, or block, and tell consumers what is affected when a HIGH or CRITICAL issue is open.
6. **Verify the fix by re-running.** Close an issue only when the same command passes on the same input scope, or the owner accepts what remains in writing. Record root cause and outcome in the issue log.

## Output Contract

`bin/profile_csv.py` writes to the out dir:

- `profile.json`: row count plus per-column null count and rate, distinct count, inferred type (int, float, date, bool, string, mixed, empty), top values, and min, max, and mean for numeric columns.
- `issues.json`: one entry per failed check with `id` (DQ-001 and up), `check`, `dimension`, `columns`, `severity` (LOW, MEDIUM, HIGH, CRITICAL), `decision` (log_and_monitor, quarantine, block), `affected_rows`, `affected_share`, up to 5 `sample_values`, up to 10 `sample_row_numbers` (spreadsheet rows, header is row 1), `evidence`, and `suggested_fix`.
- `summary.md`: the same issues in one readable list, for consumers and owners.
- With `--quarantine`: `clean.csv` with the rows that passed every quarantine or block check, and `quarantine.csv` with the failing rows plus a `_dq_reasons` column listing the issue ids each row failed.

Exit codes: 0 when no HIGH or CRITICAL issue is found, 1 when one is, 2 on a usage or input error. The same input and checks file always produce the same issues, which is what makes a re-run a real verification.

## Operating Rules

1. Every check runs from the checks file. No ad hoc checks in chat that leave no file behind. If a check matters, it goes in the file so the next run repeats it.
2. Never silently drop rows. Failing rows are quarantined with their issue ids attached, counted in the summary, and kept until the owner decides.
3. Never invent a fix value for a key, an amount, or a coded field. Deterministic formatting fixes are fine. Filled-in facts are not.
4. A missing required column, or a critical check failing on 10 percent or more of rows, blocks publication. Keep the last good version live and say so.
5. Thresholds, accepted value lists, and ranges change only with owner sign-off, recorded next to the change. Widening a range to make a failure pass is falsifying the audit.
6. Sample values in an issue log are evidence, capped on purpose. Do not paste whole sensitive columns into logs or reports. If the data carries personal data, keep samples to the minimum that proves the finding.
7. Accuracy claims need a named trusted source. A value that passes format checks is valid, not automatically accurate. Keep those two words separate in every report.
8. Every factual claim in a skill must be verified via web search or a primary source. Never invent numbers, dates, quotes, or events. Unverifiable means cut it or frame it as opinion.

## Quickstart

```bash
# 1. Profile only, to see what you are dealing with
python3 bin/profile_csv.py orders.csv --out-dir dq-out

# 2. Audit with checks, splitting clean from quarantined rows
cp assets/example-checks.json checks.json   # then edit it for your columns
python3 bin/profile_csv.py orders.csv --checks checks.json --out-dir dq-out --quarantine
echo "exit=$?"   # 1 means a HIGH or CRITICAL issue needs triage
cat dq-out/summary.md
```

Worked example: a 5-row orders file checked with the example checks plus a reference check against a customers file. The orders file has a duplicated order_id, one null customer_id, one customer_id missing from customers, a status of `unknown`, amounts of -5 and 99999 against a 0 to 10000 range, one malformed email, and one row dated before the freshness floor. It produces 7 issues: 3 CRITICAL (block), 4 HIGH (quarantine), a clean.csv with the 3 good rows, and a quarantine.csv with the 2 bad rows and their issue ids. That exact case was run and verified when this skill was written.

Supported check types in the checks file: `required_columns`, `not_null`, `unique` (a column name, or a list of column names for a composite key), `accepted_values`, `ranges` (`min` and `max`), `patterns` (regex), `reference` (`{"file": "customers.csv", "column": "id"}` per column), and `freshness` (`{"column": "updated_at", "min_date": "2026-09-15"}`). `critical_columns` lists the columns graded at critical weight. For severity grades and the fix, quarantine, or block decision rules, read `references/triage-guide.md`.
