Record a Skill Works. Then You Feed It a Real CSV.

Abstract tech illustration: Record a Skill Works. Then You Feed It a Real CSV.

Every Record-a-Skill demo shows the recording. None of them show run two, when a real client hands you a CSV with mixed date formats, comma decimals, and a column someone renamed last week. That's where 90% of recorded skills die silently — no error, just wrong numbers in the output. Here's the fix Claude never writes for you: the input contract.

The three lines Claude writes that quietly kill your skill

When you finish a Record-a-Skill session, Claude generates markdown that describes the input in roughly three lines. Something like: "The input is a CSV file containing bank transactions with columns for date, description, and amount." That's a description, not a contract. It tells the model what the file looked like the one time you recorded. It says nothing about what to do when the file drifts — and client files always drift.

The scene that made me stop trusting the default: a small invoicing business closes their books monthly. Their accountant drops a bank statement CSV into a shared Drive folder, and someone reconciles every line item against issued invoices. Three and a half hours of copy-paste, once a month. Textbook Record-a-Skill target. Recording took eight minutes. It ran clean on the file I recorded against. The next month, the accountant exported from a slightly different tool. Skill produced garbage. No exception. No warning. Just wrong totals landing in an accountant's inbox.

The recording is 10% of the work. The input contract is the other 90%.

What an input contract actually contains

A contract answers six questions the auto-generated markdown skips. Every field is explicit and machine-checkable, and there's a refusal clause at the bottom that turns silent corruption into a loud stop.

  • Column names — exact strings, case-sensitive, order-independent
  • Encoding — UTF-8, Windows-1251, Latin-1, whatever the source actually emits
  • Decimal separator — comma or period, stated explicitly
  • Date formats — every format that legitimately appears, listed
  • Required vs optional fields — with behavior for each
  • Refusal clause — what to do when any of the above fails

Here's the block I paste into the skill markdown, right where Claude's three-line description used to be:

## Input contract

File type: CSV, one row per bank transaction.

Required columns (exact strings, case-sensitive):
  - Datum          -> transaction date
  - Opis           -> free-text description
  - Iznos          -> signed amount, negative = debit
  - Valuta         -> ISO 4217 currency code

Optional columns:
  - Referenca     -> payment reference, may be blank
  - Partner       -> counterparty name, may be blank

Encoding: try UTF-8 first. On decode error, retry as Windows-1251.
  Do NOT silently fall back to Latin-1.

Decimal separator: comma. "1.234,56" means 1234.56.
Thousands separator: period or none.

Date formats accepted (try in order):
  1. DD.MM.YYYY
  2. DD/MM/YYYY
  3. YYYY-MM-DD
  4. Excel serial (integer > 30000, < 60000)

Refusal clause:
  If any required column is missing, misspelled, or renamed,
  STOP. Do not guess a mapping. Do not proceed on partial data.
  Return: "Contract violation: expected <field>, found <field or none>.
  Please confirm before re-running."

That's the whole thing. It reads like a config file because it is one — Claude parses it as instructions, not prose.

Why "refuse" beats "handle gracefully"

The instinct when writing a data pipeline is to be lenient. Fuzzy-match column names. Assume UTF-8 if unsure. Skip rows that don't parse. Every one of those defaults will bite you eventually, and the bite is always the same shape: the run completes, the output looks reasonable, and the error surfaces weeks later during an audit.

The refusal clause inverts this. Month seven of the invoice reconciliation skill, the accountant renamed a column from Iznos to Iznos u RSD because they added a second currency to the export. Skill loaded the file, checked the contract, saw the column name didn't match, refused to run, sent a message: "Contract violation: expected Iznos, found Iznos u RSD. Please confirm before re-running."

Five-minute human decision. If the auto-generated skill markdown had been in place, it would have grabbed whatever column sat in position three (probably the new RSD column, which happened to have the same values that month) and produced output that looked right and was wrong. You don't discover that class of bug from the output. You discover it when someone else reconciles the reconciliation.

The math on refusal is asymmetric. A false refusal costs you five minutes. A silent corruption costs you a client relationship and, depending on the domain, an audit.

When to refuse vs. when to normalize

  • Normalize things where the intent is unambiguous: 1.234,561234.56, Windows-1251 → UTF-8 internally, DD.MM.YYYY → ISO date
  • Refuse things where the intent is a guess: renamed columns, unknown date formats, new currency codes, unexpected extra columns that might carry meaning

Run one: clean CSV. Run two: real client file.

The contract has to survive the messy case, not the demo case. Two runs, same skill, same 45 seconds each.

Run one — the file I recorded against:

Datum,Opis,Iznos,Valuta
15.08.2026,"Invoice #2041 payment",12500,00,EUR
16.08.2026,"Office rent",-1800,00,EUR
17.08.2026,"Client wire ACME Corp",8420,50,USD

Skill reads it, matches line items against issued invoices, drops two files back into the shared folder: matched_2026-08.csv and exceptions_2026-08.csv. Forty-five seconds.

Run two — real client file from the following month:

Datum;Opis;Iznos;Valuta;Referenca
01/09/2026;"Плаћање фактуре 2087";15200,00;EUR;INV-2087
02.09.2026;"Nabavka materijala Партнер д.о.о.";-4300,75;EUR;
45907;"Wire in USD";2100,00;USD;

Three things happen that the auto-generated skill would have silently mishandled:

  1. Encoding — the file is Windows-1251, Cyrillic vendor names would render as mojibake under a UTF-8 assumption. Contract says retry as Windows-1251 on decode error. Decodes clean.
  2. Mixed date formats01/09/2026, 02.09.2026, and an Excel serial 45907 all in the same column because the accountant opened the file in Excel and re-saved one row. Contract lists all three, each row parses.
  3. Semicolon delimiter — this one I actually hit, added delimiter: auto-detect ; or , to the contract on the next revision. This is what iteration looks like.

Same 45 seconds, same clean output. The difference isn't cleverness — it's that the failure modes were enumerated up front.

The 8-minute record, 34-minute contract split

Real time breakdown from the invoice reconciliation build:

Step Time What actually happened
Record the skill in Claude 8 min Walk through reconciliation once on a sample file
Test the generated skill on the recorded file 3 min Works — this is the trap
Test on last month's file 6 min Fails silently, produces plausible-but-wrong output
Open skill markdown, delete input description 1 min Three lines gone
Write input contract 34 min Column-by-column, format-by-format, refusal clause
Test on three historical files 12 min Two pass, one triggers refusal correctly
Adjust contract (added ; delimiter case) 4 min Real iteration, not rework
Total 68 min Skill has run 11 months without a silent failure

Compare that to the "just record it" path: eight minutes of recording, and a skill that will corrupt one month's numbers before the year is out. The 34 minutes on the contract isn't overhead. It's the actual engineering.

What I no longer bother contract-checking

  • Ordering of columns — the contract keys by name, not position
  • Whitespace in string fields — trim aggressively, this is never ambiguous
  • Empty optional fields — treat blank and missing as identical
  • Row count — no minimum, no maximum, let the caller decide

The pattern in one line

Record the skill, then immediately open the markdown, find the input description, and rewrite it as a contract: encoding, separators, formats, required fields, refusal clause. The recording captures the transform. The contract captures the interface. Skills without contracts are demos. Skills with contracts survive their creators — mine outlived the specific client project it was built for and got reused, unchanged, on two other reconciliation jobs.

If you already have a recorded skill running in production, don't wait for the silent failure. Open the markdown today, count the lines describing the input. If it's fewer than 15, you have a demo, not a skill.

Where this fits in real client work

bizflowai.io builds these input-contract-first skills for small businesses across bookkeeping, invoicing, lead intake, and support triage — anywhere a client hands over a spreadsheet or export that will drift from month to month. The pattern in this post is what we ship: record for speed, contract for durability, refusal clause instead of silent fallback. Most of the reconciliation, matching, and file-drop workflows we've deployed run untouched for 6-12 months because the contract catches the drift, not the pipeline. If you want to see the invoice reconciliation skill template with a working contract, that's on bizflowai.io.


Want more like this?

I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.

Subscribe to bizflowai.io on YouTube — never miss a new tutorial.

Planning an AI automation project or need a second opinion on your architecture?

Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.

Visit bizflowai.io for our services, case studies, and AI consulting.

Frequently asked questions

What is Record a Skill in Claude?

Record a Skill is a Claude feature that lets you record yourself performing a task once, after which Claude generates a reusable skill markdown file that can repeat the workflow. For example, you can record an eight-minute bank statement reconciliation, and Claude writes a skill that reproduces the steps on future files, replacing hours of manual copy-paste work.

Why do recorded Claude skills fail on new input files?

Recorded skills fail because Claude's auto-generated markdown describes the input file rather than defining a contract. It captures what the file looked like once, but not what to do when columns rename, encodings shift, or date formats vary. The skill then silently produces wrong output on drifted files, with no error or warning, since it has no rules for rejecting mismatches.

How do I write an input contract for a Claude skill?

Open the generated skill markdown and replace the input description with explicit rules: exact case-sensitive column names, file encoding (e.g. UTF-8 or Windows-1251), decimal separator, accepted date formats, and required versus optional fields. Add a refusal clause instructing the skill to stop and report which field failed if the contract is violated, rather than guessing or proceeding.

Why does a refusal clause matter in a skill contract?

A refusal clause forces the skill to stop and report mismatches instead of guessing. Without it, a renamed column or shifted format causes the skill to grab whatever data is in the expected position and produce numbers that look right but are wrong. For accounting workflows, that silent corruption may only surface during a tax audit months later.

How long does it take to build a reliable recorded skill?

Roughly forty-two minutes total: about eight minutes to record the workflow and around thirty-four minutes to rewrite the auto-generated input description as an explicit contract. The recording is only ten percent of the work; the remaining ninety percent is defining encodings, separators, date formats, required fields, and refusal behavior so the skill survives file drift over many months.