Skill: Log Correction
Purpose
Record analyst mistakes, their fixes, and reusable learnings so future analyses
learn from past errors. Runs in two modes against the same store (defined in
docs/KNOWLEDGE.md): auto mode detects corrections and learnings in the
user's messages without being asked, and manual mode handles explicit
"log a correction" requests with full detail.
When to Use
- Auto: the user corrects your work ("that's wrong", "actually it's...",
"you used the wrong column") or teaches a reusable rule ("always use X",
"never do Y", "remember that our fiscal year starts in February") without
asking you to log anything
- Manual: user says "log a correction", "save this mistake", "record this
lesson", or similar
- After discovering and fixing an error mid-analysis worth preserving
Auto Mode
Watch every user message for these signals. When one fires, capture it
immediately; the user never has to ask.
Correction signals (something you produced was wrong):
- "that's wrong", "that's incorrect", "actually it's...", "it should be..."
- "the column is X not Y", "you used the wrong...", "off by...",
"double-counted", "that join is wrong", "missing a filter", "forgot to exclude..."
Learning signals (a reusable methodology or fact):
- "always use...", "never use...", "next time...", "prefer X over Y"
- "remember that...", "the convention here is...", "our team uses...",
"going forward...", "don't forget to..."
If both match, treat it as a correction. If neither matches, do nothing and
say nothing about it.
On a correction signal: run Steps 1-5 below, but never interrogate the
user. Infer severity, category, dataset, and tables from context; leave fields
you cannot infer as null. Acknowledge in one line ("Got it, logged as CORR-008.")
and then immediately continue with the user's underlying request; logging is
never the whole response.
On a learning signal: append a bullet to .knowledge/learnings/index.md
under the closest category heading (Data Patterns, Query Techniques, Business
Context, Stakeholder Preferences, Visualization Insights, Methodology Notes),
formatted - {concise learning} (source: user feedback, {YYYY-MM-DD}).
Acknowledge in one line ("Noted for future analyses.") and continue with the
user's request.
Auto-mode rules:
- Silent operation: no execution reports, no "I detected a correction signal",
no file-path listings. One brief acknowledgment line, then the analysis.
- Never ask "should I log this?" Classify and log.
- Never block: if a read or write fails, skip capture and answer the user's
question as if nothing happened. Do not retry or announce the failure.
- Never fabricate detail; use null for anything not stated by the user.
Manual Mode Instructions
Step 1: Gather Details
Extract from conversation context or ask the user:
- What was wrong? — One-sentence description of the error
- What is the correct answer? — The fix or corrected approach
- Which dataset/tables? — Dataset name and affected table(s)
- How severe? —
critical (wrong numbers shared) | high (changes conclusions) | medium (directionally correct) | low (no impact)
- SQL before/after? — If the error involved a query, capture both versions
If any required field is unclear, ask the user. Do not guess severity.
Step 2: Categorize
IMPORTANT: Assign exactly ONE category from the following list. These are the only valid categories — do not create custom categories.
| Category |
Description |
Examples |
sql |
Wrong query — bad join, missing filter, incorrect aggregation, wrong GROUP BY, missing WHERE clause |
INNER JOIN instead of LEFT JOIN; forgot WHERE clause to filter test users; COUNT(*) instead of COUNT(DISTINCT); aggregation before filtering |
metric |
Wrong metric definition — numerator/denominator error, wrong time window, wrong column |
Used revenue_usd instead of order_total_usd for GMV; calculated DAU as total events instead of distinct users; wrong date range for YoY comparison |
schema |
Wrong column or table reference — stale schema, misnamed field, wrong table |
Referenced old_column_name after schema migration; queried staging.users instead of prod.users; assumed column existed but it doesn't |
logic |
Flawed reasoning — Simpson's paradox missed, survivorship bias, wrong comparison |
Compared current users to all-time users (survivorship bias); aggregated across segments hiding a reversal; compared apples to oranges |
other |
Anything that does not fit the above |
Data interpretation error, visualization mistake, wrong stakeholder audience |
If the user mentions a category not in this list (e.g., "filter_missing", "metric_definition"), map it to the closest match from the allowed categories above and confirm with the user.
Step 3: Write the Correction
- Read
.knowledge/corrections/index.yaml (treat a missing or corrupt file per Rule 3: recreate from scratch)
- Derive next ID: if
last_correction_id is null, use CORR-001; otherwise
parse the numeric suffix, increment, and zero-pad to 3 digits
- Build the entry in exactly this format:
- id: "CORR-{N}"
date: "{YYYY-MM-DD}"
severity: "{severity}"
category: "{category}"
dataset: "{dataset_name}"
tables: ["{table1}", "{table2}"]
description: "{what was wrong}"
fix: "{what the correct approach is}"
sql_before: "{original query, if applicable, else null}"
sql_after: "{corrected query, if applicable, else null}"
prevented_by: "{which validation layer should have caught this}"
The prevented_by field should reference one of these validation layers:
structural — schema checks, PK validation, null checks, row count validation
logical — aggregation consistency, trend direction, progression rates < 100%
business-rules — metric plausibility, known data quality rules, domain constraints
Simpson's check — segment-first analysis to detect reversals
source tie-out — pandas vs DuckDB comparison on foundational metrics
Examples of prevented_by:
- For wrong aggregation:
"logical (progression rates should never exceed 100%)"
- For missing filter:
"business-rules (check for test account filtering in conversion metrics)"
- For wrong column:
"structural (column validation against schema)"
- Read
.knowledge/corrections/log.yaml (missing or corrupt: recreate per Rule 3)
- Append the new entry to the
corrections list
- Write the YAML back (write the full file in one go so a failed write cannot leave a half-written log)
Step 4: Update Index
- Read
.knowledge/corrections/index.yaml (already loaded in Step 3)
- Increment
total_corrections
- Increment the matching
by_severity.{severity} counter
- Increment
by_category.{category} (create the key if it does not exist)
- Set
last_correction_id to the new ID
- Set
last_updated to today's date
- Write the YAML back (full file in one go)
Step 5: Confirm
Report to the user:
Correction logged: {id}
Severity: {severity} | Category: {category}
Description: {description}
Fix: {fix}
Future analyses will check for this pattern during validation.
Rules
- Never overwrite existing corrections -- always append
- Always read current state before writing (no blind overwrites)
- If
log.yaml or index.yaml is missing or corrupt, create from scratch
with schema_version 1
- SQL snippets in
sql_before/sql_after should be trimmed to the relevant
clause, not the entire multi-hundred-line query
prevented_by should reference a specific validation layer from the list
in Step 3. Be specific about what check should have caught this.
- ONLY use the 5 allowed categories (sql, metric, schema, logic, other).
If the user suggests a different category, map it to the closest match.
Edge Cases
- No SQL involved: Set
sql_before and sql_after to null
- Dataset unknown: Set
dataset to "unknown" and note in description
- Duplicate correction: Still log it -- repeated errors signal a systemic gap
- Correction to a correction: Log as a new entry referencing the prior ID in description
- User suggests custom category: Map to closest allowed category and confirm. Example: "filter_missing" →
sql category with description noting the missing filter.
1---2name: log-correction3description: Record analyst mistakes, fixes, and reusable learnings so future analyses never repeat an error. Fires automatically when the user corrects work ("actually it's Y", "that's wrong") or teaches a rule ("always use X", "never include test users", "remember that our fiscal year starts in February"), and manually on "log a correction", "save this mistake", "record this lesson". Writes the .knowledge store per docs/KNOWLEDGE.md.4---56# Skill: Log Correction78## Purpose9Record analyst mistakes, their fixes, and reusable learnings so future analyses10learn from past errors. Runs in two modes against the same store (defined in11`docs/KNOWLEDGE.md`): **auto mode** detects corrections and learnings in the12user's messages without being asked, and **manual mode** handles explicit13"log a correction" requests with full detail.1415## When to Use16- **Auto:** the user corrects your work ("that's wrong", "actually it's...",17 "you used the wrong column") or teaches a reusable rule ("always use X",18 "never do Y", "remember that our fiscal year starts in February") without19 asking you to log anything20- **Manual:** user says "log a correction", "save this mistake", "record this21 lesson", or similar22- After discovering and fixing an error mid-analysis worth preserving2324## Auto Mode2526Watch every user message for these signals. When one fires, capture it27immediately; the user never has to ask.2829**Correction signals** (something you produced was wrong):30- "that's wrong", "that's incorrect", "actually it's...", "it should be..."31- "the column is X not Y", "you used the wrong...", "off by...",32 "double-counted", "that join is wrong", "missing a filter", "forgot to exclude..."3334**Learning signals** (a reusable methodology or fact):35- "always use...", "never use...", "next time...", "prefer X over Y"36- "remember that...", "the convention here is...", "our team uses...",37 "going forward...", "don't forget to..."3839If both match, treat it as a correction. If neither matches, do nothing and40say nothing about it.4142**On a correction signal:** run Steps 1-5 below, but never interrogate the43user. Infer severity, category, dataset, and tables from context; leave fields44you cannot infer as null. Acknowledge in one line ("Got it, logged as CORR-008.")45and then immediately continue with the user's underlying request; logging is46never the whole response.4748**On a learning signal:** append a bullet to `.knowledge/learnings/index.md`49under the closest category heading (Data Patterns, Query Techniques, Business50Context, Stakeholder Preferences, Visualization Insights, Methodology Notes),51formatted `- {concise learning} (source: user feedback, {YYYY-MM-DD})`.52Acknowledge in one line ("Noted for future analyses.") and continue with the53user's request.5455**Auto-mode rules:**561. Silent operation: no execution reports, no "I detected a correction signal",57 no file-path listings. One brief acknowledgment line, then the analysis.582. Never ask "should I log this?" Classify and log.593. Never block: if a read or write fails, skip capture and answer the user's60 question as if nothing happened. Do not retry or announce the failure.614. Never fabricate detail; use null for anything not stated by the user.6263## Manual Mode Instructions6465### Step 1: Gather Details6667Extract from conversation context or ask the user:68691. **What was wrong?** — One-sentence description of the error702. **What is the correct answer?** — The fix or corrected approach713. **Which dataset/tables?** — Dataset name and affected table(s)724. **How severe?** — `critical` (wrong numbers shared) | `high` (changes conclusions) | `medium` (directionally correct) | `low` (no impact)735. **SQL before/after?** — If the error involved a query, capture both versions7475If any required field is unclear, ask the user. Do not guess severity.7677### Step 2: Categorize7879**IMPORTANT:** Assign exactly ONE category from the following list. These are the only valid categories — do not create custom categories.8081| Category | Description | Examples |82|----------|-------------|----------|83| `sql` | Wrong query — bad join, missing filter, incorrect aggregation, wrong GROUP BY, missing WHERE clause | INNER JOIN instead of LEFT JOIN; forgot WHERE clause to filter test users; COUNT(*) instead of COUNT(DISTINCT); aggregation before filtering |84| `metric` | Wrong metric definition — numerator/denominator error, wrong time window, wrong column | Used revenue_usd instead of order_total_usd for GMV; calculated DAU as total events instead of distinct users; wrong date range for YoY comparison |85| `schema` | Wrong column or table reference — stale schema, misnamed field, wrong table | Referenced old_column_name after schema migration; queried staging.users instead of prod.users; assumed column existed but it doesn't |86| `logic` | Flawed reasoning — Simpson's paradox missed, survivorship bias, wrong comparison | Compared current users to all-time users (survivorship bias); aggregated across segments hiding a reversal; compared apples to oranges |87| `other` | Anything that does not fit the above | Data interpretation error, visualization mistake, wrong stakeholder audience |8889**If the user mentions a category not in this list** (e.g., "filter_missing", "metric_definition"), map it to the closest match from the allowed categories above and confirm with the user.9091### Step 3: Write the Correction92931. Read `.knowledge/corrections/index.yaml` (treat a missing or corrupt file per Rule 3: recreate from scratch)942. Derive next ID: if `last_correction_id` is null, use `CORR-001`; otherwise95 parse the numeric suffix, increment, and zero-pad to 3 digits963. Build the entry in exactly this format:9798```yaml99- id: "CORR-{N}"100 date: "{YYYY-MM-DD}"101 severity: "{severity}"102 category: "{category}"103 dataset: "{dataset_name}"104 tables: ["{table1}", "{table2}"]105 description: "{what was wrong}"106 fix: "{what the correct approach is}"107 sql_before: "{original query, if applicable, else null}"108 sql_after: "{corrected query, if applicable, else null}"109 prevented_by: "{which validation layer should have caught this}"110```111112**The `prevented_by` field** should reference one of these validation layers:113114- `structural` — schema checks, PK validation, null checks, row count validation115- `logical` — aggregation consistency, trend direction, progression rates < 100%116- `business-rules` — metric plausibility, known data quality rules, domain constraints117- `Simpson's check` — segment-first analysis to detect reversals118- `source tie-out` — pandas vs DuckDB comparison on foundational metrics119120**Examples of prevented_by:**121- For wrong aggregation: `"logical (progression rates should never exceed 100%)"`122- For missing filter: `"business-rules (check for test account filtering in conversion metrics)"`123- For wrong column: `"structural (column validation against schema)"`1241254. Read `.knowledge/corrections/log.yaml` (missing or corrupt: recreate per Rule 3)1265. Append the new entry to the `corrections` list1276. Write the YAML back (write the full file in one go so a failed write cannot leave a half-written log)128129### Step 4: Update Index1301311. Read `.knowledge/corrections/index.yaml` (already loaded in Step 3)1322. Increment `total_corrections`1333. Increment the matching `by_severity.{severity}` counter1344. Increment `by_category.{category}` (create the key if it does not exist)1355. Set `last_correction_id` to the new ID1366. Set `last_updated` to today's date1377. Write the YAML back (full file in one go)138139### Step 5: Confirm140141Report to the user:142143```144Correction logged: {id}145 Severity: {severity} | Category: {category}146 Description: {description}147 Fix: {fix}148149Future analyses will check for this pattern during validation.150```151152## Rules1531. Never overwrite existing corrections -- always append1542. Always read current state before writing (no blind overwrites)1553. If `log.yaml` or `index.yaml` is missing or corrupt, create from scratch156 with schema_version 11574. SQL snippets in `sql_before`/`sql_after` should be trimmed to the relevant158 clause, not the entire multi-hundred-line query1595. `prevented_by` should reference a specific validation layer from the list160 in Step 3. Be specific about what check should have caught this.1616. **ONLY use the 5 allowed categories** (sql, metric, schema, logic, other).162 If the user suggests a different category, map it to the closest match.163164## Edge Cases165- **No SQL involved:** Set `sql_before` and `sql_after` to null166- **Dataset unknown:** Set `dataset` to "unknown" and note in description167- **Duplicate correction:** Still log it -- repeated errors signal a systemic gap168- **Correction to a correction:** Log as a new entry referencing the prior ID in description169- **User suggests custom category:** Map to closest allowed category and confirm. Example: "filter_missing" → `sql` category with description noting the missing filter.