Tests and docs: no figures from the first company's books
check / check (push) Canceled after 0s

The odd-cent regression's net loss and its splits, the website's example
Apple charge and the GUI plan's example receipt now use made-up amounts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 09:38:17 -06:00
1 parent eb4329bb2a
commit 8255ac2103
6 files changed
+51 -51

No files matched your search

@@ -6,18 +6,18 @@ sources = ["26 U.S.C. §704(a)", "Treas. Reg. §1.704-1(b)(2)(iv)(b)"]
tags = ["partnership", "close", "rounding", "book", "us"] tags = ["partnership", "close", "rounding", "book", "us"]
# The regression this example comes from (wanderbooks P3 defect 17, audit # The regression this example comes from (wanderbooks P3 defect 17, audit
# F52, anonymized): a net loss of 399.23 at 50/50, closed in January and # F52, anonymized): a net loss of 287.53 at 50/50, closed in January and
# again in March, with February closed to retained earnings. Restarting the # again in March, with February closed to retained earnings. Restarting the
# count at March gave A the tied cent twice (A −399.24, B −399.22). # count at March gave A the tied cent twice (A −287.54, B −287.52).
# #
# By hand, from the rule: # By hand, from the rule:
# January: −399.23 × 0.5 = −199.615 each; rounded toward zero, −199.61 # January: −287.53 × 0.5 = −143.765 each; rounded toward zero, −143.76
# each, one cent short; the remainders tie (0.005), so the cent goes to # each, one cent short; the remainders tie (0.005), so the cent goes to
# the member listed first: A −199.62, B −199.61. # the member listed first: A −143.77, B −143.76.
# February: 10.00 to retained earnings; the members' count skips it. # February: 10.00 to retained earnings; the members' count skips it.
# March: the count's running total is −798.46, −399.23 each exactly; # March: the count's running total is −575.06, −287.53 each exactly;
# less January's −199.62 / −199.61, March gives A −199.61, B −199.62. # less January's −143.77 / −143.76, March gives A −143.76, B −143.77.
# Each member ends at −399.23, their exact share. # Each member ends at −287.53, their exact share.
[setup] [setup]
currency = "USD" currency = "USD"
@@ -65,8 +65,8 @@ share = "0.5"
date = "2026-01-15" date = "2026-01-15"
description = "January's fees" description = "January's fees"
lines = [ lines = [
{ account = "6000", side = "debit", amount = "399.23" }, { account = "6000", side = "debit", amount = "287.53" },
{ account = "1000", side = "credit", amount = "399.23" }, { account = "1000", side = "credit", amount = "287.53" },
] ]
[[events]] # event 2: the closing entry [[events]] # event 2: the closing entry
@@ -91,8 +91,8 @@ close = { start = "2026-02-01", to = "retained_earnings" }
date = "2026-03-15" date = "2026-03-15"
description = "March's fees" description = "March's fees"
lines = [ lines = [
{ account = "6000", side = "debit", amount = "399.23" }, { account = "6000", side = "debit", amount = "287.53" },
{ account = "1000", side = "credit", amount = "399.23" }, { account = "1000", side = "credit", amount = "287.53" },
] ]
[[events]] # event 6 [[events]] # event 6
@@ -105,25 +105,25 @@ query = "entry"
entry = 2 entry = 2
[expect.values] [expect.values]
"6000.credit" = "399.23" "6000.credit" = "287.53"
"3000.debit" = "199.62" "3000.debit" = "143.77"
"3001.debit" = "199.61" "3001.debit" = "143.76"
[[expect]] [[expect]]
query = "entry" query = "entry"
entry = 6 entry = 6
[expect.values] [expect.values]
"6000.credit" = "399.23" "6000.credit" = "287.53"
"3000.debit" = "199.61" "3000.debit" = "143.76"
"3001.debit" = "199.62" "3001.debit" = "143.77"
[[expect]] [[expect]]
query = "balances" query = "balances"
as_of = "2026-03-31" as_of = "2026-03-31"
[expect.values] [expect.values]
"3000" = "-399.23" "3000" = "-287.53"
"3001" = "-399.23" "3001" = "-287.53"
"3999" = "-10.00" "3999" = "-10.00"
"1000" = "-808.46" "1000" = "-585.06"
+4 -4
View File
@@ -2225,10 +2225,10 @@ impl Ledger {
/// 6. IF a journaled MembersCumulative names a start other than the /// 6. IF a journaled MembersCumulative names a start other than the
/// one the books derive: SHOULD refuse (InvalidMembers) /// one the books derive: SHOULD refuse (InvalidMembers)
/// BECAUSE replay would split differently from the journal. /// BECAUSE replay would split differently from the journal.
/// 7. IF 50/50 members closed −399.23 in January (A −199.62, B /// 7. IF 50/50 members closed −287.53 in January (A −143.77, B
/// −199.61), February closed to retained earnings, and March closes /// −143.76), February closed to retained earnings, and March closes
/// −399.23 to the members: SHOULD give A −199.61, B −199.62, so /// −287.53 to the members: SHOULD give A −143.76, B −143.77, so
/// each has −399.23 (the count skips February's close). /// each has −287.53 (the count skips February's close).
/// ``` /// ```
pub fn close_period_to( pub fn close_period_to(
&mut self, &mut self,
+22 -22
View File
@@ -205,50 +205,50 @@ const THIRDS: [&str; 3] = ["0.3333", "0.3333", "0.3334"];
// The regression (audit F52, 2026-10-16, anonymized) // The regression (audit F52, 2026-10-16, anonymized)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// Regression (P3 defect 17; audit F52's −399.23, anonymized): members A /// Regression (P3 defect 17; audit F52's −287.53, anonymized): members A
/// and B at half each. January's net loss of 399.23 closes to them: exact /// and B at half each. January's net loss of 287.53 closes to them: exact
/// −199.615 each, so A −199.62, B −199.61 (the tie to the member listed /// −143.765 each, so A −143.77, B −143.76 (the tie to the member listed
/// first). February's 10.00 loss closes to retained earnings. March's /// first). February's 10.00 loss closes to retained earnings. March's
/// 399.23 loss closes to them again. The run rule (2026-10-13 to P3) /// 287.53 loss closes to them again. The run rule (2026-10-13 to P3)
/// started over at March, handing A the tied cent a second time: A /// started over at March, handing A the tied cent a second time: A
/// −399.24, B −399.22 for an exact −399.23 each. Now February's close is /// −287.54, B −287.52 for an exact −287.53 each. Now February's close is
/// skipped and March continues January's count: the running −798.46 splits /// skipped and March continues January's count: the running −575.06 splits
/// −399.23 each, less January's −199.62 / −199.61, so March gives A /// −287.53 each, less January's −143.77 / −143.76, so March gives A
/// −199.61 and B −199.62. The March entry: Cr 6000 399.23, Dr 3000 199.61, /// −143.76 and B −143.77. The March entry: Cr 6000 287.53, Dr 3000 143.76,
/// Dr 3001 199.62. /// Dr 3001 143.77.
#[test] #[test]
fn the_audit_odd_cent_never_piles_up_across_closes() { fn the_audit_odd_cent_never_piles_up_across_closes() {
type Rule = fn(Vec<MemberClose>) -> CloseTo; type Rule = fn(Vec<MemberClose>) -> CloseTo;
let (run, cumulative): (Rule, Rule) = (CloseTo::MembersRun, CloseTo::Members); let (run, cumulative): (Rule, Rule) = (CloseTo::MembersRun, CloseTo::Members);
for (rule, a, b) in [ for (rule, a, b) in [
(run, "-399.24", "-399.22"), (run, "-287.54", "-287.52"),
(cumulative, "-399.23", "-399.23"), (cumulative, "-287.53", "-287.53"),
] { ] {
let mut co = company(Currency::USD, 2); let mut co = company(Currency::USD, 2);
let to = rule(co.members(&[0, 1], &HALF)); let to = rule(co.members(&[0, 1], &HALF));
let jan = co.month(1, "-399.23", &to); let jan = co.month(1, "-287.53", &to);
let jan_entry = co.l.get_period(jan).unwrap().closing_entry.unwrap(); let jan_entry = co.l.get_period(jan).unwrap().closing_entry.unwrap();
assert_eq!( assert_eq!(
co.lines(jan_entry), co.lines(jan_entry),
[ [
line("6000", Side::Credit, "399.23"), line("6000", Side::Credit, "287.53"),
line("3000", Side::Debit, "199.62"), line("3000", Side::Debit, "143.77"),
line("3001", Side::Debit, "199.61"), line("3001", Side::Debit, "143.76"),
] ]
); );
co.month(2, "-10.00", &CloseTo::RetainedEarnings { sweep: vec![] }); co.month(2, "-10.00", &CloseTo::RetainedEarnings { sweep: vec![] });
let mar = co.month(3, "-399.23", &to); let mar = co.month(3, "-287.53", &to);
assert_eq!(co.capitals(), decs(&[a, b])); assert_eq!(co.capitals(), decs(&[a, b]));
let retained = co.l.chart().find_by_code("3999").unwrap().id; let retained = co.l.chart().find_by_code("3999").unwrap().id;
assert_eq!(co.l.account_balance(retained).unwrap(), dec("-10.00")); assert_eq!(co.l.account_balance(retained).unwrap(), dec("-10.00"));
if a == "-399.23" { if a == "-287.53" {
let mar_entry = co.l.get_period(mar).unwrap().closing_entry.unwrap(); let mar_entry = co.l.get_period(mar).unwrap().closing_entry.unwrap();
assert_eq!( assert_eq!(
co.lines(mar_entry), co.lines(mar_entry),
[ [
line("6000", Side::Credit, "399.23"), line("6000", Side::Credit, "287.53"),
line("3000", Side::Debit, "199.61"), line("3000", Side::Debit, "143.76"),
line("3001", Side::Debit, "199.62"), line("3001", Side::Debit, "143.77"),
] ]
); );
let (since, split) = co.l.member_close_split(mar).unwrap(); let (since, split) = co.l.member_close_split(mar).unwrap();
@@ -257,8 +257,8 @@ fn the_audit_odd_cent_never_piles_up_across_closes() {
assert_eq!( assert_eq!(
rows, rows,
[ [
(dec("-199.61"), dec("-399.23"), dec("-399.23")), (dec("-143.76"), dec("-287.53"), dec("-287.53")),
(dec("-199.62"), dec("-399.23"), dec("-399.23")), (dec("-143.77"), dec("-287.53"), dec("-287.53")),
] ]
); );
} }
+3 -3
View File
@@ -602,10 +602,10 @@ Defect 17 (rules.md B-13; D-10, D-38), built without its mutation run
worked by hand in the test's comment. worked by hand in the test's comment.
**The named regression.** `the_audit_odd_cent_never_piles_up_across_closes` **The named regression.** `the_audit_odd_cent_never_piles_up_across_closes`
(`ledger-core` `tests/odd_cent.rs`): audit F52's −399.23 at 50/50, (`ledger-core` `tests/odd_cent.rs`): an audit finding's −287.53 at 50/50,
anonymized, with a close to retained earnings between two members closes. anonymized, with a close to retained earnings between two members closes.
The run rule left A −399.24 / B −399.22; the rule since P3 gives −399.23 The run rule left A −287.54 / B −287.52; the rule since P3 gives −287.53
each (March: Dr 3000 199.61, Dr 3001 199.62). Its bookspec example is each (March: Dr 3000 143.76, Dr 3001 143.77). Its bookspec example is
`bookspec/examples/members-close.odd-cent-across-closes.toml`; it and the `bookspec/examples/members-close.odd-cent-across-closes.toml`; it and the
other `members-close.*` examples are checked with every example by other `members-close.*` examples are checked with every example by
`ledger-core` `tests/bookspec.rs` (`every_bookspec_example_holds`). `ledger-core` `tests/bookspec.rs` (`every_bookspec_example_holds`).
+1 -1
View File
@@ -37,7 +37,7 @@ journal with its verification status.
hash-chained journal and the blob store and builds its own in-memory hash-chained journal and the blob store and builds its own in-memory
view (`ledger_store::Snapshot`). Changes happen through the agent (MCP). view (`ledger_store::Snapshot`). Changes happen through the agent (MCP).
Where the GUI finds something to fix, it offers a ready-to-paste prompt Where the GUI finds something to fix, it offers a ready-to-paste prompt
for the agent ("Attach the receipt for entry 12 — Anthropic, $5.82"). for the agent ("Attach the receipt for entry 12 — Anthropic, $7.40").
5. **Live.** It follows the journal: a change made by the agent appears 5. **Live.** It follows the journal: a change made by the agent appears
within a second, highlighted as new. within a second, highlighted as new.
6. **Numbers are typography.** Tabular figures everywhere (Inter with 6. **Numbers are typography.** Tabular figures everywhere (Inter with
+1 -1
View File
@@ -193,7 +193,7 @@ footer { padding: 72px 0 48px; color: var(--faint); font-size: 14px; display: fl
<h2>Just ask.</h2> <h2>Just ask.</h2>
<p class="sub">Your bookkeeper is the AI you already use, working through an accounting engine that won't let it be wrong. Talk to it the way you'd talk to a good bookkeeper. It does the work, shows you the result, and waits for your OK before anything is posted.</p> <p class="sub">Your bookkeeper is the AI you already use, working through an accounting engine that won't let it be wrong. Talk to it the way you'd talk to a good bookkeeper. It does the work, shows you the result, and waits for your OK before anything is posted.</p>
<div class="grid"> <div class="grid">
<div class="card ask"><div class="say">Find the receipt for the $106.38 Apple charge in my email and record it.</div><p>Searches your inbox, files the receipt as a PDF, drafts the entry with the right accounts, and shows you the preview.</p></div> <div class="card ask"><div class="say">Find the receipt for the $84.17 Apple charge in my email and record it.</div><p>Searches your inbox, files the receipt as a PDF, drafts the entry with the right accounts, and shows you the preview.</p></div>
<div class="card ask"><div class="say">Here's last month's bank export. Categorize everything.</div><p>Imports the transactions as drafts, proposes an account for each from how you've booked them before, and asks about the ones it isn't sure of.</p></div> <div class="card ask"><div class="say">Here's last month's bank export. Categorize everything.</div><p>Imports the transactions as drafts, proposes an account for each from how you've booked them before, and asks about the ones it isn't sure of.</p></div>
<div class="card ask"><div class="say">The domain renewal went to the wrong account. Fix it.</div><p>Reverses the entry and records it correctly. Nothing is ever erased; the history shows exactly what changed and why.</p></div> <div class="card ask"><div class="say">The domain renewal went to the wrong account. Fix it.</div><p>Reverses the entry and records it correctly. Nothing is ever erased; the history shows exactly what changed and why.</p></div>
<div class="card ask"><div class="say">Show me every Adobe charge this year.</div><p>Opens the search in the viewer beside you, every charge listed, each one a click from its receipt.</p></div> <div class="card ask"><div class="say">Show me every Adobe charge this year.</div><p>Opens the search in the viewer beside you, every charge listed, each one a click from its receipt.</p></div>