docs: the assumed list, checked against the standards; mutation score
check / check (push) Canceled after 0s

Each formerly ASSUMED behavior is now CONFIRMED, CONVENTION, DESIGN, or
CHANGE (pending), with its source (Reg. S-X, ASC 230 via Deloitte DART,
IRS K-1 instructions, OpenStax). Two need engine work: members' equity
should allocate cumulatively so each member's beginning equals the prior
ending (#7), and the cash flow should show financing gross and classify
investing by account role (#8). Two questions for the CPA are listed.
The test doc comments that relied on them now cite the verdicts.

Core mutation run: 1,011 caught + 13 timeouts of 1,024 viable, 0 missed.
No benchmark budget, by decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-13 18:18:30 -06:00
1 parent 0dcf865ff8
commit 08d1a9c230
6 files changed
+40 -35

No files matched your search

+1 -1
View File
@@ -458,7 +458,7 @@ fn statement_lines(
/// SCENARIOS: /// SCENARIOS:
/// 1. IF a month was closed: SHOULD still report that month's earnings. /// 1. IF a month was closed: SHOULD still report that month's earnings.
/// BECAUSE closing doesn't make a period break even (R-9). /// BECAUSE closing doesn't make a period break even (R-9).
/// 2. IF `start` is after `end`: SHOULD refuse. (ASSUMED) /// 2. IF `start` is after `end`: SHOULD refuse. (DESIGN: no standard governs a reversed range; docs/testing.md #1)
/// ``` /// ```
pub fn profit_and_loss_for_period( pub fn profit_and_loss_for_period(
ledger: &Ledger, ledger: &Ledger,
+1 -1
View File
@@ -538,7 +538,7 @@ fn create_invoice_refuses_due_before_date_books_unchanged() {
/// Closes January 2026 on `l`. /// Closes January 2026 on `l`.
/// ///
/// ASSUMED: closing a period with nothing in it still marks it closed /// CONVENTION (docs/testing.md #4): closing a period with nothing in it still marks it closed
/// (close_period returns Ok(None)); the precondition is asserted. /// (close_period returns Ok(None)); the precondition is asserted.
fn close_january(l: &mut Ledger) { fn close_january(l: &mut Ledger) {
let p = l let p = l
@@ -316,7 +316,7 @@ fn schedule_clamps_to_end_of_life() {
/// life ends 2026-03-15 so Mar holds 1..14 = 14/31 → 140.00. 17/31 + 14/31 /// life ends 2026-03-15 so Mar holds 1..14 = 14/31 → 140.00. 17/31 + 14/31
/// = one full month, and the total is exactly 620.00. /// = one full month, and the total is exactly 620.00.
/// ///
/// ASSUMED: life ends on purchase_date + useful_life_months and the end day /// CONVENTION (docs/testing.md #9): life ends on purchase_date + useful_life_months and the end day
/// itself is not held (matches the doc's "17/31 + 14/30" pairing for a /// itself is not held (matches the doc's "17/31 + 14/30" pairing for a
/// purchase on the 15th). /// purchase on the 15th).
#[test] #[test]
@@ -343,7 +343,7 @@ fn partial_first_and_last_months_make_one_full_month_b15() {
/// Jan 17/31 → 170.00, Feb 310.00, Mar 310.00 (acc 790.00), Apr (30 days) /// Jan 17/31 → 170.00, Feb 310.00, Mar 310.00 (acc 790.00), Apr (30 days)
/// trues up to 930 − 790 = 140.00, not 14/30 × 310 = 144.67. /// trues up to 930 − 790 = 140.00, not 14/30 × 310 = 144.67.
/// ///
/// ASSUMED: same end-of-life convention as the test above. /// CONVENTION (#9): same end-of-life convention as the test above.
#[test] #[test]
fn last_month_trues_up_unequal_month_lengths_b15() { fn last_month_trues_up_unequal_month_lengths_b15() {
let a = asset("1030.00", "100.00", 3, date(2026, 1, 15)); let a = asset("1030.00", "100.00", 3, date(2026, 1, 15));
@@ -867,7 +867,7 @@ fn run_payroll_posts_hand_worked_entry() {
/// 750.00, state 250.00, SS 620 + 248 = 868.00, Medicare 145 + 58 = 203.00, /// 750.00, state 250.00, SS 620 + 248 = 868.00, Medicare 145 + 58 = 203.00,
/// other 200.00, net 3567.50 + 1697.00 = 5264.50. /// other 200.00, net 3567.50 + 1697.00 = 5264.50.
/// ///
/// ASSUMED: lines are aggregated per account ("zero totals are skipped" /// CONFIRMED (docs/testing.md #10): lines are aggregated per account ("zero totals are skipped"
/// implies totals), not one line per employee. /// implies totals), not one line per employee.
#[test] #[test]
fn run_payroll_aggregates_employees() { fn run_payroll_aggregates_employees() {
+1 -1
View File
@@ -1235,7 +1235,7 @@ fn a_loss_closes_as_a_debit_to_equity() {
} }
/// Oracle: close INVARIANT 5: nothing to close posts nothing and returns /// Oracle: close INVARIANT 5: nothing to close posts nothing and returns
/// None. ASSUMED: the period is still marked closed (step 3 of the doc). /// None. CONVENTION (docs/testing.md #4): the period is still marked closed (step 3 of the doc).
#[test] #[test]
fn a_close_with_nothing_to_close_posts_no_entry() { fn a_close_with_nothing_to_close_posts_no_entry() {
let mut b = books(); let mut b = books();
+4 -4
View File
@@ -531,7 +531,7 @@ fn pnl_closed_month_still_reports_its_earnings() {
assert_eq!(year.net_income(), c(7000)); assert_eq!(year.net_income(), c(7000));
} }
/// Oracle: P&L SCENARIO 2 (ASSUMED in the spec itself) — start after end /// Oracle: P&L SCENARIO 2 (DESIGN, docs/testing.md #1) — start after end
/// refuses. /// refuses.
#[test] #[test]
fn pnl_period_start_after_end_refuses() { fn pnl_period_start_after_end_refuses() {
@@ -1009,7 +1009,7 @@ fn me_refuses_bad_shares() {
/// earnings 100.01 -> A 50.01, B 50.00; period income 201.01 -> A 100.51, /// earnings 100.01 -> A 50.01, B 50.00; period income 201.01 -> A 100.51,
/// B 100.50. Total ending 2251.02 = BS equity. /// B 100.50. Total ending 2251.02 = BS equity.
/// ///
/// ASSUMED: the beginning (unclosed prior earnings) and the period's income /// CHANGE PENDING (docs/testing.md #7): the beginning (unclosed prior earnings) and the period's income
/// are each split separately (INVARIANT 3 names them separately), not /// are each split separately (INVARIANT 3 names them separately), not
/// cumulative-then-differenced. /// cumulative-then-differenced.
#[test] #[test]
@@ -1407,7 +1407,7 @@ fn sorted_amounts(lines: &[ledger_core::CashFlowLine]) -> Vec<Decimal> {
/// 400; AR up 300 (-300), AP up 80 (+80); CFO 180; financing loan 300 + /// 400; AR up 300 (-300), AP up 80 (+80); CFO 180; financing loan 300 +
/// contribution 100 = 400; net change 580 = cash 1000 -> 1580. /// contribution 100 = 400; net change 580 = cash 1000 -> 1580.
/// ///
/// ASSUMED: one working-capital / financing line per account that changed. /// CHANGE PENDING (docs/testing.md #8): one working-capital / financing line per account that changed.
#[test] #[test]
fn cf_hand_computed_with_designated_debt() { fn cf_hand_computed_with_designated_debt() {
let co = cf_example_books(); let co = cf_example_books();
@@ -2418,7 +2418,7 @@ proptest! {
/// NI + depreciation + working capital; with or without a close and a /// NI + depreciation + working capital; with or without a close and a
/// designated loan. /// designated loan.
/// ///
/// ASSUMED: with no registered fixed assets, depreciation and investing /// CHANGE PENDING (docs/testing.md #8): with no registered fixed assets, depreciation and investing
/// are zero (receivables are current, so working capital). /// are zero (receivables are current, so working capital).
#[test] #[test]
fn prop_cf_sections_sum_to_change_in_cash(gens in arb_books(), (a, _m, e) in three_days(), month in opt_month(), designate in any::<bool>()) { fn prop_cf_sections_sum_to_change_in_cash(gens in arb_books(), (a, _m, e) in three_days(), month in opt_month(), designate in any::<bool>()) {
+30 -25
View File
@@ -25,7 +25,7 @@ the store's journal safety; the other crates come next (gap list).
| Miri | undefined behavior in our use of dependencies (4) | the core's unit, bridge, invariant tests | `just miri` | ~2 min | on demand | | Miri | undefined behavior in our use of dependencies (4) | the core's unit, bridge, invariant tests | `just miri` | ~2 min | on demand |
| Golden | `tests/golden/fixture_company.json` (5) | `invariants.rs` | `just check` | <1s | every change | | Golden | `tests/golden/fixture_company.json` (5) | `invariants.rs` | `just check` | <1s | every change |
| Benchmarks | a baseline (6) | `benches/books.rs` | `just bench` | ~1 min | none (no budget, by decision) | | Benchmarks | a baseline (6) | `benches/books.rs` | `just bench` | ~1 min | none (no budget, by decision) |
| Mutation | can the tests fail? | `ledger-core` | `just mutants` | ~12 min | weekly | | Mutation | can the tests fail? | `ledger-core` (2026-10-13: 1,011 caught + 13 timeouts of 1,024 viable, 0 missed) | `just mutants` | ~3 h | weekly |
CI: `.github/workflows/check.yml` runs `just check` on every push; CI: `.github/workflows/check.yml` runs `just check` on every push;
`scheduled.yml` runs proofs, the oracle, fuzzing, and mutation weekly. `scheduled.yml` runs proofs, the oracle, fuzzing, and mutation weekly.
@@ -56,32 +56,37 @@ laptop:
| balance sheet | 47 µs | 1.04 ms | | balance sheet | 47 µs | 1.04 ms |
| one month's P&L | 25 µs | 248 µs | | one month's P&L | 25 µs | 248 µs |
## Assumed, not confirmed (reject any) ## Formerly assumed: checked against the standards (2026-10-13)
Each was inferred, not decided by a human; the tests that rely on them Stephen isn't an accountant and won't adjudicate these; each was checked
say so in their doc comments. against published sources instead (rank: FASB ASC via the Deloitte DART
reproduction, SEC, IRS; then textbooks; software practice is evidence of
convention only). Verdicts: CONFIRMED (a source supports it), CONVENTION
(no standard governs; matches mainstream practice), DESIGN (not an
accounting question), CHANGE (a source says otherwise; work pending).
- ASSUMED: a P&L for a period whose start is after its end is refused. | # | Behavior | Verdict | Source / note |
- ASSUMED: a balance sheet omits accounts with a zero balance. |---|---|---|---|
- ASSUMED: the lifetime `profit_and_loss` counts closing entries | 1 | A P&L whose start is after its end is refused | DESIGN | No standard governs a reversed range; refusing beats silently swapping a typo |
(only period statements exclude them). | 2 | A balance sheet omits zero-balance accounts | CONFIRMED | Reg. S-X 210.4-03(a): "No caption should be shown ... as to which the items and conditions are not present." A comparative statement must still show a zero beside a non-zero prior column |
- ASSUMED: closing a period with nothing to close still marks it closed. | 3 | The lifetime `profit_and_loss` counts closing entries | CONFIRMED | Used as earnings not yet closed on the balance sheet; closed income is already in capital (OpenStax Principles of Financial Accounting 5.1). Its name misleads: rename to unclosed earnings (pending) |
- ASSUMED: a sweep account (draws, distributions) is zeroed as of the | 4 | Closing a period with nothing to close still locks it | CONVENTION | A close is a lock (B-12); a dormant month must still lock and count for close-in-order |
period end, like income. | 5 | Draws and distributions are closed into capital at period end | CONFIRMED | OpenStax 5.1: dividends "are closed to the permanent account"; drawing accounts close to owner's capital. Monthly closes mean the year's K-1 Item L withdrawals sum across months |
- ASSUMED: reconciliation balances are debits minus credits (tested on | 6 | Reconciliation balances | CONFIRMED | Signed by the account's normal side (a card reads as owed), as statements show it; `reconciling_a_card_reads_its_normal_side_...` |
cash; liability accounts unspecified). | 7 | A member's ending on one equity statement may differ by a cent from the next statement's beginning | CHANGE (pending) | A capital account is one running balance (Form 1065 K-1 instructions, Item L: beginning and ending capital "based on the partnership's books and records"). Allocate cumulatively so beginning equals the prior ending per member. CPA question below |
- ASSUMED: a members' equity statement allocates prior unclosed earnings | 8 | Cash flow nets one line per account; investing comes only from registered assets | CHANGE (pending) | ASC 230-10-45-7: gross receipts and payments "more relevant" than net; 45-8 limits netting to quick-turnover items. Financing: contributions apart from distributions, borrowings apart from repayments. ASC 230-10-20/45-13: loans made and investments bought are investing, so classify by account role |
and the period's income separately; one statement's ending for a member | 9 | An asset's life ends at purchase + life months, that day not held; day-count proration | CONVENTION | Exactly N months of service; day proration and the mid-month convention are both accepted book policies (ASC 360-10-35-4 not read: login) |
may differ by a cent from the next statement's beginning (only totals | 10 | A payroll entry has one line per account total | CONFIRMED | The per-employee detail the IRS requires ("Amounts and dates of all wage ... payments", Employment Tax Recordkeeping) is kept in the run; a summary entry is standard |
are guaranteed to chain). | 11 | A period with exactly the dates of an existing one is allowed | DESIGN | Nothing governs duplicate periods; harmless (the second close locks only). Refusing exact duplicates is an option |
- ASSUMED: cash flow lists one working-capital or financing line per
account that changed; with no registered assets, investing is zero. Questions for Krista Baker (CPA):
- ASSUMED: an asset's life ends on purchase date + life months, that day
not held. 1. Interim members' equity: is a per-member allocation of unclosed
- ASSUMED: a payroll entry has one line per account total, not per earnings acceptable on interim statements, or should the books close
employee. monthly so the capital accounts carry it?
- ASSUMED: a period with exactly the dates of an existing one nests in it 2. How should 2100 Due to Shareholders, and expenses the owners paid
(is allowed). personally, appear on the cash flow statement: operating, financing,
or as a non-cash disclosure (ASC 230-10-50-3, not yet read)?
## Gap list: what isn't covered, and what would bring it in ## Gap list: what isn't covered, and what would bring it in