feat(mcp): books.mark_sent, and the send in the read tools (P4b S5)

`books.mark_sent` records the send the human or a delegate made: the
invoice (`invoice:7` or its id), the day, who sent it (the token's name
unless given), how (email by default, or mail, portal, hand, another
channel), to whom (the bill-to email, else the customer's), whether it
was the original or a reminder (original on the first send, reminder
after), and the channel's own id. It answers with every send on the
invoice, newest last. `books.documents` rows gain `last_sent` and
`books.document` shows the sends, so "did we send it, and when" is one
read away, while a bill (which we receive) has none.

The schema lint refuses a parameter named `to` (a path-name rule), so
the recipients parameter is `recipients`; the journal event and the
result keep the plan's `to`. The tool needs `books:write`: it changes
the record, never a figure.

The handbook gains the invoices-and-bills section (S4.5 had not landed
yet) with the send procedure: the books never send, the PDF goes to the
human, and each send is recorded afterwards.

Over the wire, mark_sent_records_who_and_how: two sends (the second a
reminder with a message id), the list's `last_sent`, the document's
`sends`, and the invoice still owing its total. The P2 cutover refuses
a send before consent and takes it after.
This commit is contained in:
swaits committed 2026-10-27 13:24:10 -06:00
1 parent d1fb512e4b
commit 75d8c23d9b
10 files changed
+416 -14

No files matched your search

+3 -1
View File
@@ -56,7 +56,7 @@ const C: Needs = PostWhenConfirmed;
const K: Needs = PostForApprovalRules;
/// Every tool and what it needs (INVARIANT 1).
pub const TABLE: [(&str, Needs); 122] = [
pub const TABLE: [(&str, Needs); 123] = [
("books.handbook", R),
("books.start", R),
// Settings are committed at once: the settings commit of the plan.
@@ -166,6 +166,8 @@ pub const TABLE: [(&str, Needs); 122] = [
("books.receive_payment", P),
("books.pay_bill", P),
("books.void_document", P),
// Records a send: changes the books' record, never a figure.
("books.mark_sent", W),
("books.control_check", R),
("books.documents", R),
("books.document", R),