docs(arap): B-41, the bookspec examples, and the four registers (P4b S7)

rules.md B-41 states the write-off: the direct method the owner chose
(A-5), Dr 6990 / Cr 1200 with the receivable tagged, at most what is
owed and at most the least owed from that day on (a partial write-off as
§1.166-3(a) allows), refused on cash-basis books (§1.166-1(e)), its
evidence filed in the same call (§1.166-2(a)); bills are never written
off (ASC 405-20-40-1); and a recovery is not a new movement (D-49): the
same year, reverse the write-off and record the payment; a later year, a
plain entry to a bad-debt-recoveries revenue account, the invoice staying
written off (IRC §111).

bookspec states B-41 for the first time and gives B-38 its example: two
new examples, bad-debt-direct-write-off (two partial write-offs of one
invoice, the balances and the trial balance worked by hand) and
arap-hand-control-posting-refused (a hand line an earlier version wrote
replays, the same line written today is refused). The example schema
gains the dated write_off event, lets a refused entry say so (an entry
posts whole or not at all; older and refused never combine), and its
refusal vocabulary gains untagged_control_line, B-38's control guard.

The registers: docs/architecture.md names the new tool group;
docs/receipt-store.md lists invoice.written_off and what replay does with
it; docs/parity.md records the slice (no row changes status, the floor
stands); docs/testing.md records every test and what P12 must mutate;
docs/verification.md the two Lean theorems and what they cover.

No figure moves: no golden file changed.
This commit is contained in:
swaits committed 2026-10-27 12:44:45 -06:00
1 parent b731d7ed04
commit 0322778683
13 files changed
+547 -25

No files matched your search

+77
View File
@@ -3050,3 +3050,80 @@ and `BooksServer::new`'s copy (a dropped field must fail
`isolation_label_is_enforced_under_the_unit`); the host's `server_flags`
extension and `parse_args`' passthrough of `--isolation` and its optional
`--http` value.
## P4b S7: bad debts (write-offs), 2026-10-27
The write-off (plan 8.2 S7; rules.md B-41): an invoice that will not be
collected leaves the receivables as a deduction, with its evidence filed
in the same call.
- **ledger-core, `tests/spec_documents.rs`:**
`half_paid_rest_written_off_is_written_off` (B-38 SCENARIO 7: 50 paid,
50 written off, one balanced entry Dr 6990 / Cr 1200, the receivable
line tagged `WriteOff`, receivables down by exactly the write-off);
`write_off_refused_on_cash_basis` (B-41 INVARIANT 3: refused, the
snapshot unchanged, no draft left); `write_off_above_outstanding_refused`
(the bound at the day, a zero and a negative amount, then the exact
amount owed taken); `write_off_then_recovery_same_year` (D-49: the
reversal re-instates the debt, the payment settles it, the invoice reads
Paid and the write-off entry no longer stands). The first test's book
is also the identity's: `control_check` agrees after the write-off.
- **ledger-store, `tests/arap_standing.rs`:**
`write_off_waits_for_documents_v2` (R-A: refused with
`FormatUpgradePending`, journal and head unchanged, in the old-books
fixture; taken after the upgrade; the evidence filed on the entry and
linked to the invoice; replay equals live);
`write_off_replays_and_projects` (the journal holds one
`invoice.written_off` with its five facts, `receipt.attached` then
`attachment.linked`; 6990 carries the expense, 1200 is zero, the
control check agrees, `verify` passes and a reopened store has the same
books and the same figures); `write_off_without_evidence_is_refused`
(no evidence, and an attachment that isn't there, journal nothing; one
already filed is linked to the entry and the invoice).
`tests/arap_props.rs` gains `Op::WriteOff`: random write-offs (partial
and over-large, refused ones included) run through the store, which is
then reopened and read by a `Snapshot`; `status_is_a_function_of_journal_and_date`
still holds at every date.
- **Over the wire, `crates/ledger-cli/tests/documents.rs`:**
`write_off_then_recovery` (an upload as the evidence, `confirmed`, the
invoice reading written_off with its figures and its timeline, the
receipt on the entry, the reversal and the payment, the control check
agreeing) and `write_off_needs_evidence` (neither or both evidence
arguments refused; an already-filed attachment linked to the entry and
the invoice).
- **The viewer, `crates/ledger-gui/tests/invoices.rs`:**
`a_write_off_shows_on_the_timeline` (the shipped screenshot
`invoice-written-off`: status Written off, still owed $0.00, the
timeline's "Written off" row opening its entry).
`crates/ledger-gui/tests/view_model.rs`: `a_write_off_is_flagged_for_the_cpa`
(one item, "Write-off awaiting the CPA's note", the same through the
server's read of the store and the viewer's snapshot).
- **Lean, `Wanderbooks/Documents.lean`:** `Step.writeOff` (a tagged
control line and an untagged expense line: a `Step.document` in shape),
`Step.writeOff_unattached` (it moves nothing unattached, so
`control_agrees` and `Reachable.control_is_documents` still hold),
`through_append`, `docSum_writeOffLines`, and `writeOff_within_bounds`
(a write-off of at most what is owed leaves nothing owed below zero over
the date-sum). Both theorems are in `Audit.lean`. No kernel function
was added, so no Kani harness.
- **bookspec:** `bad-debt-direct-write-off` (B-41, CHOICE: invoice 100,
two partial write-offs, the balances and the trial balance) and
`arap-hand-control-posting-refused` (B-38: an entry an earlier version
wrote on receivables replays, attempts the control guard now refuses,
and the books still tie out). The example schema gained the `write_off`
event and lets a refused `lines` event say so (an entry posts whole or
not at all); the refusal vocabulary gained `untagged_control_line`,
which belongs to B-38.
**For P12 (mutation targets, none run, D-33):** `Ledger::write_off_steps`'
cash-basis comparison (the `eq_ignore_ascii_case`, the early return), its
order of checks (amount before the day, the day before the room), the
`amount > room` bound and its `room < owed` message arm, the `WriteOff`
tag, `least_owed_from`'s `max` and its `min` over later days (the refactor
`payment_room` now shares), `replay_invoice_write_off`'s movement, and
`doc_state`'s `written_off` arm; `Store::write_off_invoice`'s evidence
arms (the `check_linkable` call, the already-linked-to-the-invoice check,
the blob-before-journal order, the `File`/`Existing` link steps) and the
feature gate; the MCP handler's amount default, the exactly-one evidence
check, and the preview's clone; attention's year comparison and its
`stands_on` filter; the bookspec loader's `may_refuse` arm.