Reconcile one beancount account against a bank or broker statement. Use this skill whenever the user wants to check that their ledger matches a statement — "reconcile my checking account", "does my ledger match my May Chase statement", "find the missing transactions for last month", "why is my balance off", or when they paste/point to a statement (CSV export or PDF text) and ask to compare it to the ledger. The skill reports a diff (missing, duplicate, amount-mismatch, date-drift) and, only after explicit confirmation, appends the missing transactions plus a period-end balance assertion, then runs bean-check. SKIP when the user wants to bulk-import a statement as the primary source of new transactions (that is an import workflow, not reconciliation), record a single specific trade or transaction, ask analytics/reporting questions ("top expenses"), or edit existing entries. The core trigger is "check my ledger against this statement and fix what's missing".
يبقى الأمر في سطر واحد. مرّر أفقيًا لمراجعته كاملًا قبل النسخ.
تفضّل نسخة محلية؟ نزّل الملفات المتاحة حاليًا لدى SkillsMP.
مستكشف الملفات
20 ملفات
عرض SKILL.md
SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
beancount-reconcile
description
Reconcile one beancount account against a bank or broker statement. Use this skill whenever the user wants to check that their ledger matches a statement — "reconcile my checking account", "does my ledger match my May Chase statement", "find the missing transactions for last month", "why is my balance off", or when they paste/point to a statement (CSV export or PDF text) and ask to compare it to the ledger. The skill reports a diff (missing, duplicate, amount-mismatch, date-drift) and, only after explicit confirmation, appends the missing transactions plus a period-end balance assertion, then runs bean-check. SKIP when the user wants to bulk-import a statement as the primary source of new transactions (that is an import workflow, not reconciliation), record a single specific trade or transaction, ask analytics/reporting questions ("top expenses"), or edit existing entries. The core trigger is "check my ledger against this statement and fix what's missing".
beancount-reconcile
Reconcile one account against one statement period: find every discrepancy between the ledger and the statement, and — only after the user confirms — append the missing transactions and a period-end balance assertion that proves the account ties out.
This skill exists because reconciliation is the deterministic trust check for a ledger, and doing it by hand is tedious and error-prone: statement sign conventions differ from ledger conventions, pending-vs-settled timing shifts dates, and the only real proof of correctness is a balance assertion that beancount itself verifies. The skill normalizes the statement, diffs it against the ledger, classifies each discrepancy, and lands a balance assertion whose success (via bean-check) is the reconciliation's definition of done.
Scope — what this skill does and does not touch
Does: compare one account to one statement; append missing transactions and one period-end balance assertion; report everything else it finds.
Does not: edit or delete existing entries. Duplicates, suspect entries (in the ledger but not on the statement), and amount mismatches are — this skill only appends. It never touches , , or directives, and never reconciles more than one account per run.
reported for the user to fix by hand
option
plugin
include
Read references/statement-formats.md before parsing any statement and references/matching.md before classifying discrepancies. Both encode edge cases that are easy to get wrong.
Workflow
Five phases, in order: Discover → Normalize → Match → Propose → Verify.
1. Discover
Learn the ledger and the reconciliation target before reading the statement.
The "main" file is the one with option/plugin/include directives at the top, or the largest with open directives.
Then establish, in order:
Target account — the single account being reconciled (e.g. Assets:Bank:Checking, Liabilities:CreditCard:Amex). If the user named an account or a bank, map it to the open account. If ambiguous, ask — do not guess which account a statement belongs to.
Account type — is the target an Assets or a Liabilities account? This determines the statement sign convention (see Normalize). Read it from the account name.
Config block — a comment block at the top of the main file starting with ;; beancount-reconcile config. If present, it records the statement's date format and sign convention per account so re-runs don't re-detect. Re-read it rather than re-detecting.
Prior reconciliation point — the most recent balance assertion for the target account. Its date and amount are the trusted starting point; you only need to reconcile forward from there.
Append target — the file where the target account's transactions live (the main file, or an included sub-file, possibly year-bucketed).
Report the target account, its type, and the prior balance assertion (if any) to the user before continuing, so a misdetection is caught early.
Persist the contract as a config block on the main file the first time:
Turn the statement into normalized lines. Read references/statement-formats.md first. In brief:
Accept a CSV export or pasted PDF text. Extract three things: the statement period (start and end dates), the ending balance, and the transaction lines (date, amount, description).
Map every amount to the ledger's sign convention for the target account:
Asset account: money in = +, money out = −.
Liability account: a charge = − (you owe more), a payment = + (you owe less).
Statements vary (separate debit/credit columns, signed amounts, MDY vs DMY dates, running-balance columns). When the sign or date convention is ambiguous, ask — never guess; a flipped sign silently corrupts the reconciliation.
If the statement's ending balance or period bounds can't be found, ask for them. They are required — the balance assertion depends on them.
3. Match
Diff the normalized statement lines against the ledger's postings to the target account within the period. Read references/matching.md first for the full algorithm and tolerances. The classes:
Class
Meaning
Action
matched
statement line ↔ ledger posting agree
none
missing-in-ledger
on the statement, not in the ledger
propose a new transaction (Propose phase)
missing-on-statement
in the ledger, not on the statement (a suspect)
report for manual review
duplicate
same real transaction recorded twice in the ledger
report for manual review
amount-mismatch
matched payee/date but amounts differ
report for manual review
date-drift
same transaction, dates differ (pending vs settled)
treat as matched; note the drift
Matching order: exact (date + amount) first, then a windowed pass (amount equal within ±N days, description similar). Anything unmatched on either side falls into one of the classes above. When a match is ambiguous, ask rather than force it.
4. Propose
Show the user the reconciliation, then ask before writing. Never write before an explicit yes — this is the user's financial source of truth.
Present, in this exact order:
Target file — one explicit path.
Diff report — a section per class with counts, listing each line and the proposed action.
New open directives — only if a proposed entry needs an account that doesn't exist yet.
Proposed transactions — for each missing-in-ledger line, one transaction, formatted exactly as it will appear. The target-account posting is the statement amount (in ledger sign); the other leg is categorized from the ledger's own payee history (see below). Format the entry, don't just describe it.
Proposed balance assertion — but only when the account will actually tie out. Before proposing it, compute the tie-out: prior asserted balance + sum of every matched statement movement + the missing-in-ledger entries you're about to add. Compare it to the statement's ending balance.
Ties out (no unresolved suspects, mismatches, or duplicates): propose the balance assertion at the statement's ending balance, dated the day after the statement's last day (see "Balance-assertion date" below).
Does not tie out: do not propose a passing-looking assertion, and never append a failing one — a permanently-failing balance directive would break the user's bean-check on every future run. Instead show the residual (the difference and its sign) and tie it to the unresolved items: the residual equals the net of the reported suspects/mismatches/duplicates. Tell the user to resolve those (by hand — this skill won't edit existing entries) and re-run. Offer, only if they explicitly ask, to append the assertion as a commented-out ; 2026-06-01 balance … tripwire.
A clear yes/no prompt.
Categorizing the other leg of a missing entry:
Look at how the same or a similar payee was categorized before in this ledger. If there's a confident prior, reuse that account.
If there's no confident match, post the other leg to Expenses:Uncategorized (open it if needed) and say so — flag it for the user to refine. Never invent a plausible-looking account name.
Keep the target-account posting exactly equal to the statement amount; the reconciliation depends on it.
Example proposal:
Target file: ./ledger.beancount
Reconciling: Assets:Bank:Checking — May 2026 statement (2026-05-01 … 2026-05-31)
Prior assertion: 2026-05-01 balance … 1,000.00 USD ✓
Diff:
matched: 18
missing-in-ledger: 2 → propose transactions below
missing-on-statement: 1 → REVIEW: 2026-05-14 "Refund ACME" 25.00 — in ledger, not on statement
amount-mismatch: 1 → REVIEW: 2026-05-09 "Gas" ledger 40.00 vs statement 42.00
duplicate: 0
New account opens (if any):
2026-05-01 open Expenses:Uncategorized
Proposed transactions:
2026-05-07 * "TRADER JOES #123"
Assets:Bank:Checking -54.20 USD
Expenses:Food:Groceries 54.20 USD ; categorized from prior "TRADER JOES" entries
2026-05-22 * "CITY PARKING AUTH"
Assets:Bank:Checking -12.00 USD
Expenses:Uncategorized 12.00 USD ; no prior match — please refine
Proposed balance assertion (statement ending balance 1,203.80 USD, as of end of 2026-05-31):
2026-06-01 balance Assets:Bank:Checking 1203.80 USD
Append these to ./ledger.beancount? (yes/no)
On yes: append. On no: ask what to change and re-propose. When appending:
Insert in date order; most ledgers are date-sorted.
Place any new open directives after existing opens near the top.
Place the balance assertion in date order (it will sort after the last May transaction).
Preserve trailing newlines and blank-line separators.
Do not append the suspect / duplicate / amount-mismatch items — those are reported only. They are exactly what an amount-off assertion will flag next.
5. Verify
After appending, run bean-check on the modified file:
bean-check ./ledger.beancount
In the clean case, you appended the missing entries plus a passing balance assertion — that assertion is the reconciliation's proof, and bean-check verifies it:
bean-check passes → the account ties out to the statement's ending balance. Report success, and note any items still left for manual review (suspects, mismatches) if the reconcile was partial.
bean-check fails on the assertion you just wrote (e.g. Balance failed for 'Assets:Bank:Checking': expected 2865.80 USD != accumulated 2863.80 USD (2.00 too little)) → your tie-out math was wrong, or a proposed entry had the wrong amount. Do not report success. Surface the exact residual, and re-examine the missing entries you added — never leave a failing assertion in place claiming the account reconciled.
Any other bean-check error (undeclared account from a categorized leg, transaction doesn't balance) → surface the exact output, propose a fix, never silently revert.
Recall from Propose that when the account does not tie out (unresolved suspect / mismatch / duplicate), you never appended an assertion at all — you reported the residual instead. So a well-run partial reconcile leaves bean-check green (no failing assertion), with the residual and its causes reported in prose for the user to fix.
If bean-check is unavailable, tell the user (pip install beancount) and, at minimum, sum the target account's postings over the period by hand and compare to the statement ending balance.
Balance-assertion date — the one subtlety to get right
A beancount balance assertion checks the account balance at the start of its date — it includes every transaction before that date and excludes transactions dated on that date. So to assert a statement's ending balance as of the end of the last statement day, date the assertion the day after the statement's last day.
Statement period ends 2026-05-31 with ending balance 1203.80 USD → write 2026-06-01 balance Assets:Bank:Checking 1203.80 USD.
Dating it 2026-05-31 would wrongly exclude every transaction that actually happened on 2026-05-31.
State this reasoning to the user when proposing the assertion, so the off-by-one-day choice is visible and reviewable.
What NOT to do
Don't write anything without an explicit yes.
Don't edit or delete existing entries — append only. Report suspects, duplicates, and amount mismatches for manual fixing.
Don't reconcile more than one account per run.
Don't guess a statement's sign or date convention, or a payee's category — ask or use Expenses:Uncategorized and flag it.
Don't append an assertion that won't tie out, and don't claim success on any bean-check failure — surface the residual instead.
Don't touch option, plugin, or include directives.