Three spec decisions from Stephen (2026-10-13), raised by the spec-blind test writers, each test-first: - A payment dated before its invoice or bill is refused (PaymentBeforeDocument); a real prepayment is a deposit. Test: a_payment_never_predates_its_document. Replay is unaffected (it goes through replay_invoice_payment / replay_bill_payment). - Aging as of a date shows only documents issued by then, less only the payments made by then. The ledger now records each document's payment entries (live and on replay), and aging counts those dated by as_of. Test: aging_as_of_a_date_sees_only_what_existed_then; the A/R and A/P conservation properties' aging oracles updated to the confirmed rule (they had assumed aging ignored dates). - A schedule through a mid-month date prorates that month by days; the code was right and its doc wrong. The spec-blind test that disagreed is rewritten to the confirmed spec and no longer ignored. Also found while wiring payments: a payment to an unknown cash account created the payment's draft and then failed, leaving it behind; the cash account is now checked before anything is created. The unreviewed golden van_schedule.json is gone: every row was first checked against an independent hand computation (all 61 matched), then replaced by van_schedule_is_the_hand_computed_one, which asserts that computation directly (548.39, 59 x 1,000.00, 451.61; total 60,000.00). Tests: 452. No spec-disagreements left ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
358 lines
16 KiB
Markdown
358 lines
16 KiB
Markdown
# Rule catalog
|
||
|
||
Every rule the engine enforces, the authority for it, and what checks it.
|
||
Status is **AUTHORITY** (a source states the rule), **CONVENTION** (common
|
||
practice no authority states; the best reference is given), or
|
||
**DECISION** (a human chose, and when). Vendored sources are in
|
||
`sources/`; FASB and OpenStax are quoted briefly under their copyright and
|
||
license (see `README.md`). Quotes from FASB's Codification were read in
|
||
Deloitte's DART roadmaps, which reproduce it ("FASB Accounting Standards
|
||
Codification: Copyright © 2005 - 2026 by Financial Accounting
|
||
Foundation").
|
||
|
||
Spec blocks in the code cite these as `SOURCE: rules.md B-n`.
|
||
|
||
---
|
||
|
||
## B-1 Every entry is a double entry: debits equal credits
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
- Pacioli ch. 36 (`sources/pacioli-1494.md`): "All entries made in the
|
||
ledger have to be double entries — that is, if you make one creditor,
|
||
you must make some one debtor."
|
||
- IRS Pub. 583 (`sources/irs-records.md`): "It is self-balancing because you
|
||
record every transaction as a debit entry in one account and as a credit
|
||
entry in another. Under this system, the total debits must equal the
|
||
total credits after you post the journal entries to the ledger accounts."
|
||
|
||
**Enforced:** `kernel::check_lines` via `JournalEntry::validate` (two or
|
||
more lines, debits = credits exactly). A line's account must exist and
|
||
take postings when the line is added, not only at post: DECISION
|
||
(Stephen, 2026-10-13), `Ledger::add_line`
|
||
(`a_line_on_an_unknown_or_placeholder_account_is_refused_when_added`). **Checked:** Kani
|
||
`check_lines_is_exact`, `accepted_entry_nets_to_zero`; Lean
|
||
`Accepted.net_zero`; property `prop_posted_entries_balance`.
|
||
|
||
## B-2 A line's amount is positive; its side gives the direction
|
||
|
||
**Status:** CONVENTION
|
||
|
||
Pacioli keeps amounts in a debit column and a credit column (ch. 36: "All
|
||
the creditors must appear in the Ledger at the right hand side, and all the
|
||
debtors at the left"); the side, not a sign, says which way money moves. No
|
||
authority forbids a negative amount outright; wanderbooks refuses them so
|
||
that every line reads one way (2026-10-13, found by the formal pass).
|
||
|
||
**Enforced:** `kernel::check_lines` (`NegativeAmountLine`, `ZeroAmountLine`).
|
||
**Checked:** Kani `check_lines_is_exact`; test
|
||
`negative_amount_line_is_rejected`.
|
||
|
||
## B-3 One money through the ledger; foreign amounts at the transaction-date rate
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
- Pacioli ch. 36: "the money that you reckon by at the beginning should be
|
||
the same all through the Ledger."
|
||
- ASC 830-20-30-1 (cited): "each asset, liability, revenue, expense, gain,
|
||
or loss arising from the transaction shall be measured initially in the
|
||
functional currency of the recording entity by use of the exchange rate
|
||
in effect at that date."
|
||
|
||
A foreign line is booked in the base currency's smallest unit (ISO 4217
|
||
minor units, `sources/iso-4217.md`), half away from zero; a ledger in one
|
||
money holds no fractions of its smallest unit, and its cash can then
|
||
match a bank statement: DECISION (Stephen, 2026-10-13), after the GnuCash
|
||
cross-check found wanderbooks kept $108.34567 where GnuCash booked
|
||
$108.35. Lines booked before then replay as booked.
|
||
|
||
**Enforced:** `Ledger::add_line` (base currency only),
|
||
`Ledger::add_line_fx` (base amount = foreign amount × rate, rounded to
|
||
the minor unit; original kept). **Checked:**
|
||
`a_foreign_line_is_booked_in_whole_base_units`; the GnuCash oracle.
|
||
**Checked:** `foreign_currency_line_is_rejected`, `fx_overflow_is_an_error`.
|
||
|
||
## B-4 Five elements, closed list
|
||
|
||
**Status:** AUTHORITY (definitions); the five-type chart is CONVENTION
|
||
|
||
FASB Concepts Statement No. 8, Ch. 4 (2021), which replaced Concepts
|
||
Statement 6 (cited): E16 "An asset is a present right of an entity to an
|
||
economic benefit." E37 "A liability is a present obligation of an entity to
|
||
transfer an economic benefit." E61 equity is "the residual interest in the
|
||
assets of an entity that remains after deducting its liabilities." E80
|
||
revenues "are inflows or other enhancements of assets … or settlements of
|
||
its liabilities … from delivering or producing goods, rendering services,
|
||
or carrying out other activities." E81 expenses "are outflows or other
|
||
using up of assets … or incurrences of its liabilities …".
|
||
|
||
**Enforced:** `AccountType` (five variants). A child account has its
|
||
parent's type: DECISION (Stephen, 2026-10-13), `Ledger::add_account`
|
||
(`ParentTypeMismatch`). **Checked:** `a_child_account_has_its_parents_type`.
|
||
|
||
## B-5 Normal balances
|
||
|
||
**Status:** CONVENTION
|
||
|
||
No standard defines normal balances. OpenStax, *Principles of Accounting,
|
||
Vol. 1* §3.1 (cited; "Access for free at openstax.org"): "As assets and
|
||
expenses increase on the debit side, their normal balance is a debit…
|
||
Since liabilities, equity (such as common stock), and revenues increase
|
||
with a credit, their 'normal' balance is a credit." Pacioli ch. 12: capital
|
||
"must always be placed as creditor … and the cash always debtor."
|
||
|
||
**Enforced:** `AccountType::normal_side`, `kernel::normal_balance`.
|
||
**Checked:** Kani `normal_balance_is_signed`, `normal_side_increases`; Lean
|
||
`balance_after_line`.
|
||
|
||
## B-6 The trial balance balances
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
Pacioli ch. 36: "the total of the credits — I do not say creditors —
|
||
should be equal to the total of the debits — I do not say debtors. If they
|
||
were not equal there would be a mistake in the Ledger." IRS Pub. 583 (B-1).
|
||
|
||
**Enforced:** follows from B-1. **Checked:** Kani `trial_balance_columns`;
|
||
Lean `trial_balance_iff`, `Reachable.trial_balance`,
|
||
`Reachable.trial_balance_through`; properties.
|
||
|
||
## B-7 The accounting equation
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
CON 8 Ch. 4 ¶E62 (cited): equity "is the difference between the entity's
|
||
assets and its liabilities." On a live book, income not yet closed is part
|
||
of equity (B-11).
|
||
|
||
**Checked:** Kani `accounting_equation`; Lean `accounting_equation`,
|
||
`Reachable.accounting_equation[_through]`; property
|
||
`prop_balance_sheet_balances`.
|
||
|
||
## B-8 Posted is forever: the book is written only forward
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
- Pacioli ch. 36: "You should not write backward, but always forward —
|
||
that is, go forward as the days go, which never come back. If you do
|
||
otherwise, the book would be untrue."
|
||
- German Commercial Code (HGB) §239(3) (cited; official English
|
||
translation): "An entry or a documented record may not be modified such
|
||
that it is no longer possible to establish its original content."
|
||
- Rev. Proc. 98-25 (`sources/irs-records.md`): machine-sensible records
|
||
must keep the audit trail.
|
||
|
||
**Enforced:** posted entries immutable; the journal is append-only and
|
||
hash-chained. **Checked:** Lean `Step.extends`, `Steps.extends`; property
|
||
`prop_posted_entries_immutable`; store `journal_tamper_is_detected`.
|
||
|
||
## B-9 A mistake is corrected by an opposite entry, never erased
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
Pacioli ch. 31 (the *storno*): "you shall make another entry opposite this
|
||
one in the credit for the same amount … After you have made this
|
||
correction it is just as if you had written nothing in the debit column."
|
||
Ch. 36: "Mark with a cross or with an "H" that special entry, and then make
|
||
an entry on the opposite side under the same account."
|
||
|
||
**Enforced:** `Ledger::reverse_entry` (same lines, sides flipped, linked by
|
||
`reversal_of`). **Checked:** Kani `reversal_mirrors`, `reversal_cancels`;
|
||
Lean `accepted_reversal_iff`, `movement_with_reversal`.
|
||
|
||
## B-10 An entry is reversed at most once; a closing entry never
|
||
|
||
**Status:** DECISION (Stephen, 2026-10-13)
|
||
|
||
Follows from B-9 (a correction corrects once) and B-11 (a closed period
|
||
stays closed). **Enforced:** `Ledger::check_reversal`. **Checked:**
|
||
`an_entry_is_reversed_at_most_once`, `a_closing_entry_is_never_reversed`.
|
||
|
||
## B-11 Revenue and expenses close into profit and loss, and that into capital
|
||
|
||
**Status:** AUTHORITY (mechanics); the account the net lands in depends on
|
||
the entity
|
||
|
||
- Pacioli ch. 34: "you shall close this account into the capital account";
|
||
ch. 27: profit and loss "must then be transferred for its closing (saldo)
|
||
into the capital account."
|
||
- OpenStax §5.1 (cited): "Temporary (nominal) accounts are accounts that
|
||
are closed at the end of each accounting period." Corporations close into
|
||
retained earnings; partnerships into each partner's capital (B-13).
|
||
|
||
**Enforced:** `Ledger::close_period_to`. **Checked:** Kani
|
||
`closing_line_zeroes`, `closing_entry_balances`; Lean
|
||
`closingEntry_zeroes`, `closingEntry_accepted`, `close_zeroes`,
|
||
`close_credits_net_income`; `a_close_sweeps_only_through_the_period_end`.
|
||
|
||
## B-12 A closed period takes no new postings; periods close in order
|
||
|
||
**Status:** CONVENTION
|
||
|
||
Periods themselves: IRS Pub. 538 (`sources/irs-periods-and-methods.md`)
|
||
defines the tax year, calendar and fiscal. Locking a closed period and
|
||
closing in order are practice (and follow from B-8: a closed period's
|
||
book is not rewritten). Periods nest (a month in a quarter in a year)
|
||
and never partially overlap: DECISION (Stephen, 2026-10-13),
|
||
`Ledger::create_period` (`PeriodsOverlap`;
|
||
`periods_nest_but_never_partially_overlap`). **Enforced:** `closed_period_containing`,
|
||
`CloseOutOfOrder`, `Store::rehearse`. **Checked:** Lean `Steps.frozen`;
|
||
store property `refused_commands_never_reach_the_journal` (no draft in a
|
||
closed period).
|
||
|
||
## B-13 Partners share income by the partnership agreement; capital accounts carry it
|
||
|
||
**Status:** AUTHORITY (shares); DECISION (cent rounding)
|
||
|
||
- 26 U.S.C. §704(a) (`sources/irs-partnerships.md`): "A partner's
|
||
distributive share of income, gain, loss, deduction, or credit shall,
|
||
except as otherwise provided in this chapter, be determined by the
|
||
partnership agreement."
|
||
- Form 1065 Schedule M-2: "Show what caused changes during the tax year in
|
||
the partners' tax-basis capital accounts."
|
||
- **No authority** says how to round a share to the cent. Largest
|
||
remainder, ties to the earlier member: DECISION (Stephen, 2026-10-13).
|
||
Closes made before then replay with the last member absorbing it.
|
||
|
||
**Enforced:** `CloseTo::Members`, `periods::allocate`,
|
||
`kernel::apportion`, `members_equity`. **Checked:** Kani
|
||
`apportion_is_exact`, `apportion_is_fair`; properties `prop_split_*`;
|
||
`closes_journaled_under_the_old_rule_replay_with_it`.
|
||
|
||
## B-14 Statements: P&L for a period; cash flow by the indirect method
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
ASC 230-10-45-28 (cited): an entity not using the direct method "shall
|
||
determine and report the same amount for net cash flow from operating
|
||
activities indirectly by adjusting net income … (the indirect or
|
||
reconciliation method)." ASC 230-10-45-16: "All of the following are cash
|
||
inflows from operating activities: Cash receipts from sales of goods or
|
||
services…". A closing entry is not income or spending (B-11), so period
|
||
statements exclude it.
|
||
|
||
**Enforced:** `profit_and_loss_for_period`, `cash_flow`. **Checked:** Lean
|
||
`statements_add_up`; `prop_period_pnl_partitions`;
|
||
`cash_flow_reconciles_to_change_in_cash`.
|
||
|
||
## B-15 Depreciation: straight line, salvage never depreciated, partial months by days
|
||
|
||
**Status:** CONVENTION (book method); AUTHORITY for tax conventions, which
|
||
wanderbooks does not apply
|
||
|
||
OpenStax §11.3 (cited): "Straight-line depreciation is a method of
|
||
depreciation that evenly splits the depreciable amount across the useful
|
||
life of the asset," with "(Cost – salvage value)/Useful life"; partial
|
||
periods: "A common method is to allocate depreciation expense based on the
|
||
number of months the asset is owned in a year." wanderbooks prorates
|
||
partial months by days, including a schedule through a mid-month date
|
||
(DECISION, Stephen 2026-10-13). ASC 360-10-35-4 not yet verified (see README).
|
||
Tax conventions (half-year, mid-month, mid-quarter): IRS Pub. 946
|
||
(`sources/irs-depreciation.md`), for the CPA.
|
||
|
||
**Enforced:** `assets::depreciation_schedule`. **Checked:**
|
||
`schedule_sums_exactly_to_depreciable_amount`,
|
||
`salvage_value_is_never_depreciated`, `prop_depreciation_schedule_exact`.
|
||
|
||
## B-16 Prepaid expenses and deferred revenue are recognized over the period benefited or earned
|
||
|
||
**Status:** AUTHORITY (revenue); CONVENTION (prepaids)
|
||
|
||
ASC 606-10-45-2 (cited): payment before transfer → "the entity shall
|
||
present the contract as a contract liability"; 606-10-25-23: "An entity
|
||
shall recognize revenue when (or as) the entity satisfies a performance
|
||
obligation by transferring a promised good or service." Prepaids: OpenStax
|
||
§4.2 (cited). Tax: the 12-month rule, Treas. Reg. §1.263(a)-4(f)
|
||
(`sources/irs-capitalization.md`).
|
||
|
||
**Enforced:** `schedules`. **Checked:** `a_prepaid_expense_is_expensed_month_by_month`,
|
||
`deferred_revenue_is_earned_to_the_cent`.
|
||
|
||
## B-17 Platform payouts are recorded gross when the business is the principal
|
||
|
||
**Status:** AUTHORITY
|
||
|
||
ASC 606-10-55-37 (cited): "An entity is a principal if it controls the
|
||
specified good or service before that good or service is transferred to a
|
||
customer." 606-10-55-38: an agent "recognizes revenue in the amount of any
|
||
fee or commission to which it expects to be entitled." Whether an app
|
||
developer is principal for app-store sales is a judgment under 55-39; the
|
||
CPA confirms.
|
||
|
||
**Enforced:** `payouts::payout_plan`. **Checked:**
|
||
`a_payout_is_grossed_up_to_revenue_and_fees`.
|
||
|
||
## B-18 Aging buckets: Current, 1–30, 31–60, 61–90, 90+ days past due
|
||
|
||
**Status:** CONVENTION (bucket edges); DECISION (due today is Current)
|
||
|
||
ASC 326-20-55-37/38 (cited) illustrates "one way" to age trade
|
||
receivables with buckets "current … 1–30 days past due … 31–60 … 61–90 …
|
||
more than 90 days past due"; it is illustrative, not a rule. The FFIEC
|
||
Call Report instructions (bank regulators, not binding here): "The past
|
||
due status … should be determined in accordance with its contractual
|
||
repayment terms." A document due today is not yet past due: DECISION
|
||
(Stephen, 2026-10-13). Aging as of a date shows only documents issued by
|
||
then, less only payments made by then; and a payment never predates its
|
||
invoice or bill (a real prepayment is a deposit): DECISIONS (Stephen,
|
||
2026-10-13).
|
||
|
||
**Enforced:** `arap::aging_bucket`, `Ledger::ar_aging`/`ap_aging`,
|
||
`receive_payment`/`pay_bill` (`PaymentBeforeDocument`). **Checked:**
|
||
`aging_boundaries`, `aging_is_exclusive_and_only_ages`,
|
||
`aging_as_of_a_date_sees_only_what_existed_then`,
|
||
`a_payment_never_predates_its_document`.
|
||
|
||
## B-19 1099 reporting thresholds
|
||
|
||
**Status:** AUTHORITY (`sources/irs-1099-thresholds.md`)
|
||
|
||
$600 through 2025; $2,000 for payments made after Dec. 31, 2025 (26 U.S.C.
|
||
§6041(a), §6041A(a)(2), as amended by Pub. L. 119-21 §70433); for calendar
|
||
years after 2026, indexed to inflation and rounded to the nearest $100
|
||
(§6041(h)). Card payments are reported on Form 1099-K, not 1099-NEC/MISC.
|
||
Attorneys' gross proceeds stay at $600 (not modeled).
|
||
|
||
**Enforced:** `form1099::default_threshold`, a dated rule
|
||
(`ledger_core::rules::THRESHOLD_1099`, known through 2026, review from
|
||
2026-11-01). For a later year the engine refuses with `RuleNotKnown`
|
||
rather than guess; the MCP digest and the viewer's "Needs attention" list
|
||
flag the rule once it is due (DECISION, Stephen, 2026-10-13). **Checked:**
|
||
`an_unknown_year_threshold_is_never_guessed`,
|
||
`dated_rules_go_stale_on_their_review_date`, MCP stdio test.
|
||
|
||
## B-20 Every transaction has supporting documents; records are kept
|
||
|
||
**Status:** AUTHORITY (`sources/irs-records.md`)
|
||
|
||
IRS Pub. 583: "In addition, you must keep supporting documents"; they
|
||
"support the entries in your books and on your tax return"; keep records
|
||
"until the period of limitations for that return runs out" (3 years
|
||
generally; 7 for bad debts).
|
||
|
||
**Enforced:** the MCP receipt policy (not the engine; R-13).
|
||
|
||
## B-21 Exact amounts in the books; rounding only on returns
|
||
|
||
**Status:** AUTHORITY (for where rounding happens)
|
||
|
||
Form 1065 instructions (`sources/irs-partnerships.md`): "If two or more
|
||
amounts are added to figure the amount to enter on a line, include cents
|
||
when adding the amounts and round off only the total." The books keep
|
||
cents (and FX precision) exactly (R-2).
|
||
|
||
**Enforced:** `Decimal` money, `Amount` checked arithmetic, line range.
|
||
**Checked:** Kani `sums_are_exact`; bridge tests.
|
||
|
||
## B-22 Expense or capitalize (book policy, mirrors tax)
|
||
|
||
**Status:** AUTHORITY (`sources/irs-capitalization.md`)
|
||
|
||
Treas. Reg. §1.263(a)-1(f)(1)(ii)(D) sets $500 per invoice or item for a
|
||
taxpayer without an applicable financial statement "or other amount as
|
||
identified in published guidance"; Notice 2015-82 raised it to $2,500 for
|
||
taxable years beginning on or after January 1, 2016. The 12-month rule:
|
||
§1.263(a)-4(f)(1). The election is made yearly on the return.
|
||
|
||
**Enforced:** bookkeeper policy (books' AGENTS.md), not the engine.
|