8cd2d2f80f5e8a3b3bec1cebb488f79bfbe1a2d7
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4236f03744 |
feat(store): a record's source can be corrected without touching its words (P5)
Accounting reason: a convention, a decision or an open item carries who
decided it, when and on what authority. When that is recorded wrongly the
honest fix is to say so on the record, not to restate the policy: a policy
revision bumps the convention's revision and reads as a change of the rule,
and a new decision supersedes the old one. A source correction changes only
the authority and says why, so the audit trail shows the words were never
altered.
New event types convention.source_revised, decision.source_revised and
open_item.source_revised {id, source, reason} under the journal feature
source_revisions: an older version would refuse the types, or show the old
source as the record's authority, so it stops at the declaration instead
(rules.md B-27) and books that have not had the owner's upgrade refuse the
write with format_upgrade_pending, journaling nothing. Replay sets the
source and nothing else (the text, the revision and the journal line stay);
the projection updates the rows' existing source columns, so no schema
changed. A correction must be one: other words, the same source, no reason
or an unknown record is refused.
books.set_convention takes reason (the text exactly as it stands),
books.record_decision and books.raise_item take revises with reason. Without
reason the old refusal stands and now says how to correct only the source.
8.0.6: the hand-written lines of the three shapes in
books_written_under_older_rules_still_open; fixture old-books/p5_before,
written by
|
||
|
|
28d46332e4 |
feat(store): draft states and discards are journaled, posted_seq is kept (P4)
Accounting reason: a draft that waits on its receipt, and one thrown
away, are facts about the books' working papers that must survive a
restart and replay identically, or a discarded draft would come back and
hold its period open. entry.draft_state_set and entry.draft_discarded are
drafts_v2's (an older version stops at the declaration instead of
misreading them); no figure moves. Schema 19 (taken at merge as the next
after the current last): entries.posted_seq, and a draft_states table
(drafts are not rows of entries, so the plan's entries.draft_state column
has nowhere to live). posted_seq is the journal line that posted each
entry (entry.posted, entry.reversed, the line that closed a period or
posted a schedule month), identical live and on replay.
8.0.6: a hand-written line of each new shape in
books_written_under_older_rules_still_open; fixture old-books/p4_before
written by
|
||
|
|
2ad192bbcd |
merge: P6, the evidence pipeline (branch P6, checkpoint ce366408)
The last of the paused branches, from its "not for merge" checkpoint:
extraction in ledger-import (text, pages, page images; capped, caught),
journaled as attachment.text_extracted; dedup by hash with
books.find_attachment and books.consolidate_duplicates; annotations and
books.set_entry_settles; the company record categories; the two fuzz
targets; feature attachment_text. Nineteen files conflicted with the
four P4b merges, resolved as plan 8.0.2 says: P6 retakes schema 18 (it
had taken 16, which S4.3 holds; S5 holds 17); the tool table is 129 and
the destructive count 31; both sides' appends kept with their seams
restored; one B-27 paragraph naming every feature; in the old-books
fixture P6's hand-written reimbursement is entry 7 (its ids collided
with S4.3's invoice); the replay fuzzer's list gains S7's
invoice.written_off; S4.3's issued-PDF test expects the extraction
record P6 journals right after a filing.
One integration defect between S7 and P6, found by the arap property
test and fixed here: under P6's dedup, a write-off whose evidence bytes
were filed already (the same collection letter for a second write-off of
the same invoice) was refused only after its entry had been journaled,
at the final link to the invoice ("already linked"). The write-off now
resolves the dedup before anything is journaled and keeps a link the
record already has to the invoice instead of refusing it; an explicit
existing_attachment_id already on an entry is still refused (S7's
one-receipt-once rule). P6's gate never ran before the pause; it runs on
main after this merge, and its ledger-store -> ledger-import dependency
is the open review point (plan 8.0.1).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
|
||
|
|
be58059830 |
merge: P4b S6, paying on terms (branch S6, 6c4480cd)
The third of the paused branches: Ledger::settle with discounts and the processor's fee (kernel::settle, Kani settle_is_exact, Lean settle_posts_a_document_step), books.receive_payment and books.pay_bill with applications, books.bills_due, the agings' detail and tie-out, the aging exports and the CPA package's aging CSVs, the handlers in tools/settlements.rs. Twelve files conflicted with the S4.4 and S5 merges, resolved as plan 8.0.2 says: both routers; both test appends (the shared match arm in arap_standing.rs spliced into both tests; the shared `.unwrap()` in store.rs); the scope table at 124; one invoices-and-bills handbook (S5's send step 1 with S6's steps 2, 3, 5 and procedures) registered once; docs/mcp-interface.md with S4.4's create docs and S6's payment docs; S5's law renumbered 35 and S6's 36 (S1's is 34); the parity rows and sections of both. S6's wire tests create their bills as S4.4's contract now requires (confirmed, the vendor's number, a line account, the vendor's invoice uploaded) and live in their own module beside S4.4's tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp |
||
|
|
ce36640874 | wip: pause checkpoint — P6 feature complete, gate interrupted (not for merge) | ||
|
|
57f04dbb0c |
feat(mcp): payments on terms, bills due, and the agings' tie-out (P4b S6)
books.receive_payment and books.pay_bill take applications, each document's cash with an optional discount and reason, and a customer payment may carry the processor's fee; the result names every document it settled and what reached the account. books.bills_due lists what to pay and the running total by due date. The agings take detail and return the control account's tie-out, and books.export writes ar_aging/ap_aging. The handlers live in tools/settlements.rs (the EM's split, so S4.4's documents.rs stays free); documents.rs keeps the documents, the void and the control check. docs/mcp-interface.md documents every changed tool and the new one; docs/architecture.md names the group; docs/testing.md records the tests and P12's mutation targets. Old names (document_id, cash_account) still work; the output keeps remaining/status for one document. A fee's processor is named in the description, not tagged as the entry's vendor: ledger tags are entry-wide and tagging would move the customer's party totals and 1099-NEC. The handbook's invoices-and-bills section gains the payment, discount, fee and bounce-replacement procedures (S4.5 can extend it). Over the wire: one_payment_settles_two_invoices, late_discount_needs_a_reason, a_fee_posts_gross_to_receivables_and_net_to_the_bank, card_paid_bill_is_not_1099_nec, multi_document_payment_counts_once_for_1099_and_party_totals, overpayment_refused_with_the_excess, bills_due_orders_by_due_and_discount and the_aging_export_ties_out. Terms cannot yet be journaled on a document (S4.3) and a bill line's account is not an MCP field (S4.4), so bill_on_terms_with_discount, equipment_bill_payment_is_investing_cash and the discount half of bills_due are core tests until those land; the unit's As-built block records this. |
||
|
|
d5b9e5cf99 |
feat(mcp): the create tools preview and confirm, and the party keeps its contact block (P4b S4.4)
An invoice is issued, not edited. Without `confirmed` the create tools now preview: the posting (with the accounts' codes, which the live chart may not hold yet), the number it will take, its due date, terms and early discount, warnings, and for an invoice the DRAFT copy of the PDF, handed over as a file. Nothing is written and no number is used up. With `confirmed` the invoice posts, takes the number the preview showed, and files the issued copy with itself in the same call: `receipt.attached` on its entry plus the link to the invoice, its SHA-256 in the event (A-1, A-7, B-40). The copy the customer received is therefore the copy in the books, and `books.document`'s re-created page can never be mistaken for it. The result also carries a `mailto` link for a human to send it; the books never send (A-4). A bill keeps what the plan says a bill is: the vendor's own invoice number, the vendor's invoice filed on its entry as its evidence, and, in books that have declared `documents_v2`, an account on every line, so no bill can land in General Expenses by default any more (A-9, A-10, A-13). A vendor number entered twice is refused unless the caller says why, and the reason is journaled with the second bill (B-40 item 4). Books that have not declared the feature keep recording bills exactly as before, so the owner's books keep working between units (D-39, 8.0.4). Terms are resolved in one place and in one order: the due date given, else the terms given, else the customer's or vendor's own default, else the books' `invoicing.default_terms`; a document given only a due date stores no terms, so an old-shape invoice is still written byte for byte as before (B-39, A-13). D-55: a party carries the owner's full contact block (phone, contact person, billing and shipping addresses, billing email). It rides the same `party.updated` event behind `documents_v2`, and an invoice's bill-to is snapshotted at issue with the billing address and billing email in place of the general ones when the party has them — `PartySnapshot::from_party` is the one place that rule lives, so a later edit cannot change what was sent, and the next invoice takes today's details. Tests: the wire tests are the new `crates/ledger-cli/tests/documents.rs` (preview writes nothing, the DRAFT copy, the issued copy's SHA-256 equals `books.get_receipt`'s, numbers run, a bill's file and terms, the duplicate refusal, the line-account rules, the terms fallback, the `party_id` alias and the ambiguity it refuses, the contact block's set and clear, and the bill-to at issue); `format_upgrade.rs` gains `documents_v2_refused_until_consent` and the cut-over test now refuses and then makes a document carrying a new field (8.0.6 item 3); the store tests are `the_contact_block_round_trips_and_replays` and `an_invoice_keeps_the_billing_details_it_was_issued_with`, and `books_written_under_older_rules_still_open` gains a hand-written contact-block event (8.0.6 item 1). Merge does not need the owner's go-ahead: the live books refuse a write that needs `documents_v2` until he gives it, and take every other write as before (8.0.6 item 6). No figure moved: S4 moves none. |
||
|
|
0e5aebda99 |
feat(store): journal the document fields and file the issued PDF (P4b S4.3)
S4.1 gave documents a number, terms, line accounts and snapshots, and S4.2
gave parties an email and default terms, but the store journaled none of
them: a document carrying one was refused by a temporary guard, because a
public write that replay loses would break I-3. This slice makes the
journal carry them.
- `invoice.created` and `bill.created` write a number, terms, bill-to and
seller snapshots, references, memo and a duplicate's reason, and each
line its account, quantity and unit price, every key only when set. A
document naming none is journaled byte for byte as every earlier version
wrote it (`no_new_fields_journals_byte_for_byte`), so the owner's live
books keep working.
- All of it is behind `documents_v2` (B-27): a write that names one waits
for the owner's upgrade, refused with `format_upgrade_pending` and
journaling nothing; a write that names none never waits
(`documents_v2_waits_for_the_upgrade`).
- `invoicing.first_number` is settable only while no numbered invoice
exists (A-2, B-40): the series has begun, and a number is never reused or
renumbered. Refused at `set_setting`, before anything is journaled.
- The issued copy is filed in the issuing call (A-7, B-40 item 3):
`create_invoice_issued` journals the entry, the invoice, then
`receipt.attached` on that entry (`INV-0007.pdf`, `application/pdf`,
"Issued invoice") and `attachment.linked` to the invoice. There is no
`issued_pdf` field: the attachment id is allocated after the entry exists
(CD-A6), and the link is how the copy is found and how its SHA-256 is
checked. Every refusal happens before the first journal line; the PDF's
bytes are stored before it, so a refused issue leaves an unreferenced,
deduplicated blob at worst.
- A preview runs the issue on a clone and journals nothing: the posting,
the total, the number it would take and the due date (`preview_invoice`,
`preview_bill`). Books that haven't upgraded keep the legacy shape: the
copy is filed as "Invoice {id}.pdf" and no number is taken (migration
item 5).
- Schema 16 adds the document columns; a line's new fields ride
`lines_json` as its project does. `differs_from` already compares whole
invoices, bills and parties, so it sees every new field.
8.0.6: (1) `books_written_under_older_rules_still_open` hand-writes both
new document shapes through JournalWriter and still opens; (2) a new
fixture `old-books/p4_s43_documents` was written by main
|
||
|
|
3a9aa92315
|
P3: the books' format moves only with the owner's go-ahead (D-39)
Books an update could write a newer format for open on their old format and are asked at every open; anything needing a new feature is refused until the owner agrees with the passphrase, after a verified backup that includes the keys. New books start on the current format. Each projection schema has its own database file, so an older version still opens the books until the owner agrees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj |