longbook llms.txt — contract revision 45. Hello. If you are an AI reading this, you are probably helping someone with longbook. This is the contract, plainly stated. Read it once; the working method is short and the examples are there so you never have to guess. If you are writing about longbook rather than working in a vault, the media kit has what you need: facts, descriptions, logos, screenshots and founder details, in Markdown at https://longbook.app/media-kit/index.md For a directory or listing form, every fact about longbook, one per line, is at https://longbook.app/facts/ and in Markdown at https://longbook.app/facts.md ## What longbook is longbook is a financial memory for your AI. It remembers every commitment — subscriptions, insurance, EMIs, rent, school fees, salaries, memberships, domains, warranties, one-time spends — so the owner and their AI always know what is due next and what the year has really cost. It is paid: $38 one-time, bought before first use with a 7-day refund, and one purchase unlocks every platform. longbook Cloud, $4 a month, covers what uses longbook's server — the hosted copy an AI reads, statement reading, shared reports — and the purchase includes its first 12 months. Without Cloud, an AI on the owner's Mac (Claude Desktop, Claude Code) still reads and edits the longbook folder in iCloud directly, through the local server or the files, at no charge. longbook never reads what is in this memory. Card numbers, CVVs, expiries and credentials can never be stored anywhere; the file refuses them. The owner's financial details stay on their own devices unless they switch the hosted copy on, and switching it off deletes that copy. longbook keeps only tag structures — each tag's kind, name and icon, never a name, amount, date, or note — to make suggestions better for the owner and the library better for everyone. ## The data-minimization rule — read this before you write anything longbook is designed so this file **cannot** hold a card number, CVV, expiry, PIN, or any credential. Those fields do not exist in the schema, on purpose. For an account, write only: a display name (e.g. "Visa 9292"), the institution, the last 4 digits, a nickname, a tag (credit card / bank account / UPI / wallet / cash…), and its notes. If you are tempted to write more than that anywhere in this file — including details values, notes, report text and insights — stop. It does not belong here, and a save containing it will be refused, naming the row. A statement you read is full of account numbers. Read them; never copy them. The last four digits of a card are the most that ever leaves a statement for this file. ## The three doors — and how they work together **If you have longbook's tools** (an MCP server called "longbook"), use them. They are the same arithmetic the app uses, and every write goes through the same validator and lands in the owner's Proposals screen as one reviewable batch, which they can keep or undo whole. The tools: `next_payment`, `upcoming`, `charges`, `cashflow`, `spending_by_category`, `spending_by_account`, `spending_by_person`, `commitments`, `search`, `accounts`, `people`, `tags` and `whoami` to read; `list_documents` and `read_document` for the statements in the documents folder, and `reports` for the reports longbook has drawn; `add_commitments`, `update_commitment`, `mark_paid`, `add_charges`, `remove_charges`, `update_charge`, `set_planned_count`, `cancel_commitment`, `delete_commitment`, `promote_to_recurring`, `add_account`, `update_account`, `add_person`, `update_person`, `rename_tag`, `delete_tag`, `leave_insight` to write; `create_report` to ask longbook to draw a report, which shows on its Reports screen rather than in Proposals; `read_guide` to pull any chapter of this text back when you need it. The servers also offer the owner's prompts — "Set up my longbook", "Add my statement" and the rest in `Prompts` below — each with its procedure. There are three doors, and `whoami` says which one you are standing at: 1. **This machine.** The server built into the longbook app, running on the owner's own computer (Set up Claude Desktop or Copy configuration, on the Mac). It reads the folder and the documents inside it, and its writes go into the file at once, standing in Proposals for review. `write_mode: "direct"`. 2. **Signed in on the web.** claude.ai, or any client the owner signed into with their own account. It reads the hosted copy the app pushes. Its writes are suggestions, and only after the owner ticks "Let signed-in AIs suggest changes" on the app's Automations screen (More on iPhone, Settings on the Mac) — until then the write tools refuse and say exactly that. 3. **A key.** For schedulers and clients that send headers: an `lbk_` key made on the app's Automations screen. "Read only" reads; "Read and suggest changes" may also file suggestions. Named, revocable at once, with an optional expiry `whoami` warns about. **One memory, one review.** All three doors end at the same Proposals screen, every batch signed with its author — the client's name on door 1, "web session" on door 2, the key's name on door 3 — so the owner always knows whose hand did what. The two hosted doors share ONE inbox and one idempotency space: a suggestion filed through the key shows in the signed-in door's `whoami` as `pending_for_review`, and the same `idempotency_key` never files twice, whichever door carries it — for ninety days: a suggestion is deleted ninety days after it was filed, and its key goes with it. **The hosted copy is a snapshot**, pushed by the app when the owner saves. A charge the owner adds through Siri or a Shortcut reaches the copy only at the app's next save. Your own suggestion is NOT in the copy you read until the owner's app applies it — never re-file because the memory looks unchanged. `pending_for_review` and `failed_to_apply` in `whoami` are your outbox: what is still waiting, and what the app could not carry out. The app refreshes the copy daily while it is in use; a copy it has not refreshed for 90 days is deleted, and so is a suggestion that old. When `whoami` reports no hosted copy, say so to the owner: their next save in the app, with the server switched on, brings it back — the daily refresh only touches a copy that exists, and never makes one. Documents are read on door 1 only — the Mac door, where the app's own server reads the folder. Through the hosted doors the owner attaches the statement in the chat, and it is already in front of you. Several AIs can serve one memory this way — a desktop AI importing statements, a web session answering questions, a night agent filing receipts — without stepping on each other: read first, file with idempotency keys, and let provenance tell the owner apart whose work is whose. **If you only have the folder** — no tools, just files — edit `financial_memory.json` by the schema beside it. Every rule below applies the same way; you are simply holding the pen yourself: preserve ids, sign `_provenance`, keep the JSON valid, write the whole file atomically. longbook notices the change, shows the owner a diff, and lets them keep or undo it. That is the design, not a concession: the folder is the product. **Which door, when you have a choice.** On the owner's own computer, work on their folder: door 1 when the longbook server is there, the file itself when it is not. It is the owner's own file in their own iCloud, nothing passes through longbook's server, and the statements in the documents folder are readable. The hosted doors are for when the folder is out of reach — a phone, a browser, a scheduler in the cloud. The guide, the schema and the tag library in the folder are kept current by the app, which rewrites them whenever it opens: read them there, and never edit them. A statement pasted or attached into the chat is already in front of you — the client extracted it. `read_document` is for files in the vault's documents folder; do not call it for a file that is not there. ## The first run — when the folder is empty An empty `commitments` array and one sentence like "Read my 12-month bank statement and help me set up my longbook" — the prompt `Set up my longbook` — means you are setting someone up. The app is holding a waiting screen until this folder holds real commitments. Until then they are looking at a door, so do the whole job in one sitting rather than handing back a half-filled file. The order below is the order; each step is the ground the next stands on. **0. Read `to_fill_in` first.** Setup may have asked the owner what they pay for, and the names they ticked — "Home rent", "Cook salary", "Wife health insurance" — wait there without an amount or a day. Both doors show the same list three ways: appended to the server's instructions at connect, in `whoami`, and in every `commitments` answer beside its `to_fill_in_note` (the Mac door reads the list into its instructions when the server starts; `whoami` is always current). Those names come first, before anything else the statements hold: - **Search every statement for each one by name, loosely.** "Cook salary" is a UPI to a person on the 1st of every month; "Wife health insurance" is one premium line to an insurer in one month. When you find one, write it with `add_commitments` under that same name — paid rows at their real dates, then promote what repeats — and the app drops it from the list. Never write a stranger beside a name that is waiting. - **Ask for the rest in one table**, one row per name still open, and say a spoken answer is fine — most clients take voice, and a rough answer beats none: | commitment | your question | your best guess | |---|---|---| | Wife health insurance | Which insurer, how much, and which month? | HDFC Ergo, about ₹18,000, March — the one line on 15/03 fits | - **If the statements do not reach far enough** — a yearly premium outside the window, a name with no line at all — ask for older or longer statements before you guess. A guess written as a fact is the one mistake the owner cannot see in the Proposals screen. **Setup's hints come with them.** Setup also asked which accounts the owner pays from and who they pay, and kept the answers in `hints` — accounts by their kind, "Savings account", "Credit card", and people by their role, "Maid", "Cook". Hints are not accounts or people: nothing is billed to one. Read each statement with them in mind — a monthly payment to Sunita may be the cook, the statement's account may be the savings account — and add the account or person under the name the statement or the owner gives (`add_account`, `add_person`, "HDFC savings 4521", "Sunita"), never under the hint's own word. Both doors show them beside `to_fill_in`: in the instructions at connect, in `whoami`, and in every `commitments` answer with its `hints_note`. An older setup wrote accounts and people by kind and role as real rows instead: put each statement on the account of its kind and rename that account to the statement's own first (`update_account`); then write its charges under the new name, since a charge written to a name the memory does not hold makes a new account. Rename a role's person with `update_person` once the owner gives the name. "Self" is the owner. Never add either twice, and never add the owner again. **1. Say where you are in time.** Before reading a line, name what the statement covers and where today is: "HDFC savings, 1–31 July 2026; today is 4 September, so this is two months back." Everything you write from it is dated inside that window, and a report about it is about July, not about now. **2. Write every charge as a fact.** A statement is evidence of the past: every line on it proves a charge happened, on a date, for an amount, from an account. Write each one that is a charge as a one-time commitment at its real date and amount, `paid: true`, `basis: "statement"`, on the statement's account, with your best-guess category — and do not ask permission for a charge that already cleared. Guess by what a wrong guess would cost: copy the amount, the date, the account and the currency as printed, and never guess them; guess the name, the category and which commitment a line belongs to, write them, and show them in the table; ask before writing who a payment is for. Money in — salary, a refund, interest — is written too, as a one-time row with `direction: "in"`; it never counts as spending. Skip movements between the owner's own accounts; one the owner wants remembered is a row with `direction: "transfer"`. The method and the worked lines are in `Importing statements` below. **3. Set what clearly repeats.** A payee the statements show at least three times, a steady month, quarter or year apart, and still going at their end, repeats: `promote_to_recurring` it now. It keeps every charge exactly as it was and plans ahead as far as the owner says, 24 months if they do not. A repeat that stopped is promoted too, with `ends_on` at its last charge. List each one in the table, so the owner can undo it. Where writes are proposals — a sign-in or a key — the rows are not in the memory until the owner's app applies them, so there are no ids to promote yet: list these repeats as ones you will set, and promote them after the owner answers, once `commitments` shows the rows. A charge seen once or twice that may repeat — a yearly premium — is a question, never a promotion. **4. Ask about Members, and say what they are.** Most owners have never met the idea, so say it in a sentence: longbook can remember who a payment is for — a Member is a person in their life, a spouse, a child, a parent, someone they employ — and once a charge is tied to one, longbook answers "how much do I spend on her?" in one place: health insurance for a wife, school fees for a son, a helper's salary. Then ask whether they would like to add anyone; first names are enough. Name a member only when they do. **5. Show the table.** Call `tags` — it carries every category with its icon — and lay out every category you chose with how many charges and the total in each, the names and categories you guessed, and the repeats you set; list every charge you could not place, with its date and amount, at the bottom. Ask which are wrong, what the unplaced ones were, and how often each charge seen once or twice comes round, with your guess. Say it can be answered out loud: most clients take voice, and a rough answer beats a considered one. Every import, not only the first. **6. Correct and complete.** Apply what they said — categories, members, and each commitment's notes: its three details that matter, as far as the source or the owner states them (see `Notes` below) — in one batched `update_commitment` pass. Undo a repeat the owner says is not one. Invent nothing they did not say. **7. Promote the rest on a yes.** For each further repeat the owner confirms, `promote_to_recurring` it, planned as far as they say, 24 months if they do not. Say how far you planned. **Then the reports draw themselves.** longbook writes one per calendar month the rows now cover as fact — twelve months of statements is twelve reports — the next time the owner opens it. You write none; `reports` reads them back. The modules each holds, and what to do with them, are in `Reading reports` below. **Then say you are done**, and name what they can look at: how many commitments, how many months the reports will cover, how far the plans reach, and the two or three things you noticed that they may want to change. ## The working method 1. **Read first.** `commitments` (status "all") and `search` before any write — or the file itself. Match by name, loosely: "Netflix", "NETFLIX.COM" and "Netflix India" are one commitment. Members and accounts match by `name_key` (trimmed, lowercased name). **Name a commitment as the owner would say it** — "Netflix", "Car EMI", "Term insurance" — never as the statement prints it; a detail worth keeping goes in its notes. 2. **Update over add.** When a row exists, `update_commitment` it; never create a twin. A new card is an update to `account`; a new price is an update to `amount`, which reaches only the planned charges from today on — the past is never repriced. An account's own name, type, notes (its card name and any other detail, one per line), balance with its day, the person it belongs to and its linked account change with `update_account`; a person's name, email and tag with `update_person`. `accounts` and `people` read them back. A statement's closing balance is its account's `balance`, dated the statement's last day: set it with `update_account` after the statement's charges are written — the app's own importer does the same — and skip it only when the statement prints none. 3. **Every charge is a row.** History is exactly the rows before today; the plan is exactly the rows from today on; nothing is computed from a rule. A charge you cannot prove repeats is a `once` row at its real date and amount. Doubt is not a reason to skip; it is a reason to write `once`. 4. **Write in one batch, then stop.** `add_commitments` takes up to 200 rows in one call; a whole year of statements is one or two calls. Then say: "Review the change in the app's Proposals screen — keep it or undo it there." Do not re-read and rewrite to "check your work"; the owner reviews, not you. 5. **Facts first, then what clearly repeats, then the question.** Write what the statement proves; `promote_to_recurring` a payee it shows at least three times, a steady month, quarter or year apart, still going, and show it; ask about the rest. Where writes are proposals, promote once `commitments` shows the rows the app applied. A promotion keeps every fact. What comes back unanswered stays `once`, and goes in the summary for the owner to promote later, in the app or through you. 6. **Never invent a fact.** No amounts the source did not show, no dates it did not carry, no members or accounts the owner did not name. A guess at a name, a category or a clear repeat is fine when you show it; a guess at a fact is not. 7. **Nothing is written unasked, and nothing runs out silently.** The app keeps no plan ahead on its own: what is planned is what someone wrote. Plan as far as the owner asks (24 months when they do not say), say how far you planned, and when `commitments` or `upcoming` names a plan running short, offer to extend it with `set_planned_count`, or `add_charges` for named days. ## Prompts — what the owner asks, and the procedure for each The owner gives their AI one of these, word for word or in their own words. Each has a procedure, step by step: `read_guide` with the prompt's name or the owner's words returns it with the chapter it builds on, Claude lists the prompts in its menu, and `prompts.json`, beside this file, holds them all. Follow the procedure with every rule in this guide. In the procedures, "the Mac connection" is the first door, the server inside the longbook app; "a sign-in" is the second, and "a key" the third. The owner sees each prompt's result in the Automations section of the app and the website, and copies the prompt from there. **Setting up** - **Set up my longbook** — "Read my 12-month bank statement and help me set up my longbook", or "Set up my longbook". Your longbook filled from your statements: every charge on record, the names you picked in setup filled in where your statements or your answers show them, each account's closing balance set, the charges that clearly repeat planned ahead, and the rest set to repeat once you confirm them. It waits in the Proposals screen for you to keep. Builds on "first-run". - **Add my statement** — "Add my statement in my longbook", or "Update my longbook from my statements", or "Add my statement". Every charge on the statement recorded as paid, money in kept apart, the account's balance updated, the charges that clearly repeat set to continue, and the rest once you confirm them. Builds on "importing-statements". - **Categorise my longbook** — "Categorise my longbook". Every commitment without a category gets one. Your AI shows you the ones it guessed, so you can correct any. Builds on "categorise-my-longbook". - **Add a month report in my longbook** — "Add September month report in my longbook". The month's statements recorded, the charges they prove confirmed, and each account's balance set. longbook draws that month's report, with "Not yet confirmed" beside the total until every charge in it is confirmed. Builds on "month-report". **Keeping it current** - **Add email invoices in my longbook** — "Add email invoices in my longbook". This month's bank alerts, receipts, bills and money in from your mail, each recorded once, with nothing doubled. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Set up a daily longbook email round** — "Go to longbook.app/mcp and set up a daily email round for my longbook, as longbook recommends". Your AI reads your mail every morning at 6 and records the money that moved, so your longbook stays current without you asking. Needs the owner's mail, connected to their AI, and scheduled tasks in their AI's app. Builds on "daily-email-round". - **Add document invoices in my longbook** — "Add document invoices in my longbook". The invoices, receipts and bills you attach, or keep in the longbook documents folder, recorded as charges with the file's name in their notes, without doubling what your statements or mail already recorded. Builds on "document-invoices". - **Add a commitment or an expense** — "Add a commitment in my longbook", or "Add an expense in my longbook", or "Add my new gym membership to my longbook". A new commitment planned ahead, or a one-time expense recorded, on the account and for the person you name. Builds on "working-method". **Reading it** - **What do I pay next?** — "What's my next commitment in my longbook", or "What do I pay next?". The next day you owe something, and what is due that day. Nothing in your longbook changes. - **What is coming in the next 30 days?** — "What is coming in my longbook in the next 30 days?", or "What is coming in the next 30 days?", or "What do I owe this month?". Everything due in the next 30 days, or the period you name, by day, with the total per currency. Nothing in your longbook changes. - **What did I spend in a month?** — "What did I spend in July in my longbook?", or "What did I spend in July?". What you spent in that month, and what is not yet confirmed. Through the Mac connection this comes from longbook's report; otherwise from your charges, including ones nobody has confirmed yet. Nothing in your longbook changes. Builds on "reading-reports". - **What renews this year?** — "What renews this year in my longbook?", or "What renews this year?", or "Which premiums are due before March?". Your yearly, half-yearly and quarterly payments due in the next 12 months, or before the day you name, in date order. Nothing in your longbook changes. - **Make my weekly longbook page** — "Make my weekly longbook page", or "Give me my longbook week". One page for the week: what is due in the next 7 days, what you spent last week, what renews in the next 30 days, and anything waiting for you. Nothing in your longbook changes. **Reports** - **Create a report in my longbook** — "Create a report in my longbook". A report over the days, accounts, people and categories you choose, on the Reports screen the next time the app is open. It counts confirmed charges only. Builds on "create-a-report". - **Make a report of my trip** — "Make a report of my Goa trip in my longbook", or "Make a report of my Goa trip". A report of your trip's spending on the Reports screen, filling in as the trip's charges are confirmed. Builds on "create-a-report". **Housekeeping** - **Tidy my longbook** — "Tidy my longbook". Doubled charges, bank-style names and odd categories found, and fixed once you approve each change. Builds on "working-method". - **Reorganise my longbook** — "Reorganise my longbook". Your whole longbook, once over: names as you would say them, every row categorised, people and accounts linked, doubles merged, nothing lost. The whole pass waits in Proposals. Builds on "reorganise-my-longbook". - **Set up a morning brief** — "Set up a morning brief for my longbook", or "Set yourself up for a longbook morning brief". A short summary every morning of what is due today and in the next 30 days. Nothing in your longbook changes. Needs scheduled tasks in their AI's app. Builds on "unattended-runs". **From your email** - **Find subscriptions from my email** — "Find subscriptions from my email and add them in my longbook". Your subscriptions found in the last 12 months of your mail: each one with its receipts as charges, the ones that clearly repeat planned ahead, and trials and price changes as the mails state them. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find expenses from my email** — "Find expenses from my email and add them in my longbook". This month's spending from your mail's bank alerts and receipts, each recorded once as a paid charge. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find invoices from my email** — "Find invoices from my email and add them in my longbook". The invoices and receipts in the last 12 months of your mail, each recorded once as a charge, paid or due. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find insurances from my email** — "Find insurances from my email and add them in my longbook". Your insurance policies found in your mail: each with its premiums as charges, its next premium planned, and its policy details in its notes. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find purchases from my email** — "Find purchases from my email and add them in my longbook". Your orders from the last 3 months of your mail, each recorded once as a paid charge named for what you bought. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find bills from my email** — "Find bills from my email and add them in my longbook". Your bills from the last 3 months of your mail: what you paid recorded, what is due planned, and each card's monthly bill kept apart from spending. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find loans and EMIs from my email** — "Find loans and EMIs from my email and add them in my longbook". Your loans and EMIs found in your mail: each loan with its EMIs as charges, planned ahead to its end when the mails state it. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find travel bookings from my email** — "Find travel bookings from my email and add them in my longbook". Your bookings from the last 12 months of your mail: flights, trains, hotels and cabs, each recorded as a paid charge with where and when. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". - **Find money in from my email** — "Find money in from my email and add them in my longbook". Your salary, refunds, interest and other money in from the last 12 months of your mail, kept apart from spending. Needs the owner's mail, connected to their AI. Builds on "daily-email-round". ## Importing statements — the method, with examples The owner drops bank and card statements into the vault's `documents/` folder (PDF with text, `.xlsx`, or `.csv` — nothing else is read; a scanned PDF is refused with the reason) and says something like "Update my longbook from my statements" or "Add September month report in my longbook". That is the Mac door; through the hosted doors the statement arrives attached in the chat, already in front of you. **A statement is evidence of the past.** Every line on it proves a charge happened, on a date, for an amount. Record what it proves; do not ask permission for it. "Shall I add this?" about a charge that already cleared has one answer, and a year of statements asked that way is an interrogation, not an import. **Ask what the paper cannot say.** A statement carries a payee string, not what the charge was, who it was for, or whether it repeats — and those are the questions worth a person's attention. Collect them while you classify, ask them in one batch when you have finished reading, short enough to answer out loud, and write once. A good one names the evidence and asks the unknown: "₹5,400 to a name I cannot place, twice since March — what is it, and is it for anyone in particular?" **Whether it repeats is the question that pays — settled by the evidence where it is clear.** Write the lines first, each as a one-time row; `promote_to_recurring` a payee the statement shows at least three times, a steady month, quarter or year apart, still going at its end, and show it in the table; ask about the rest, and promote each yes. Where writes are proposals, the rows reach the memory only when the owner's app applies them: list the clear repeats, and promote them with the yeses once `commitments` shows the rows. A promotion keeps every charge the statement proved, exactly as written, and plans ahead from the last of them. With nobody in the conversation to answer, do not wait: leave the rows `once` and propose the promotions in your note, per `Unattended runs`. This is the whole flow: 1. `list_documents` → `read_document` each (long files page through `next_offset`; keep asking until it is null). Credit-card statements hold the card's real charges; the bank statement's "card bill payment" line is a transfer, not a spend — skip it, or you count everything twice — unless the memory holds that bill as a transfer row: then the line settles it, `add_charges` on that row, paid, basis "statement". 2. `commitments` with status "all", so what is already there is known — a line that belongs to a commitment that exists is `add_charges` on it, paid, basis "statement", not a twin. 3. Classify every line. Every line that survives the tree is written the same way — a one-time row at its real date and amount, `paid: true`, `basis: "statement"` — and the tree decides what you will say about it, and what you will promote afterwards: - **A credit, refund, salary, interest, dividend, or cashback** → a one-time row with `direction: "in"`, paid, basis "statement". Money in is remembered beside what goes out and never added to it; a transfer in from the owner's own account is a movement. - **A movement between the owner's own accounts** — ATM withdrawal, card bill payment, transfer to their own savings, wallet top-up → skip. Money moved, nothing was bought, and a ledger of what was bought has no use for it. A card bill payment is skipped unless the memory holds that bill as a transfer row — the daily email round writes one, "Coral card bill, October 2026" — and then the line is `add_charges` on it, paid, basis "statement", at the row's own date with `paid_on` the day the money moved: `search` the card's name before you skip. A movement the owner wants remembered — "keep my transfers to savings" — is written with `direction: "transfer"`: shown on the timeline, never in spending, never in money in. - **The same payee at the same amount ~30 days apart, still going at the end of the statement** → a monthly rhythm. Seen three times or more, promote it and show it; fewer, ask: `monthly`, and the plan continues from the last charge seen. Streaming, phone plans, broadband, rent, society maintenance, gym, SIP-like standing instructions, EMIs, a cook or cleaner paid on the 1st, a cloud-storage plan. - **The same payee at the same amount ~365 days apart, or one charge whose name says "premium", "annual", "renewal", "yearly"** → a yearly rhythm: `yearly` on promotion. Insurance premiums, domain renewals, annual memberships, software licences, school annual fees, a streaming plan paid by the year. - **A steady rhythm that is neither** → `custom` with `custom_cycle_days` on promotion: a half-yearly premium (183), a quarterly fee (91), water cans every 10 days (10), a domain bought for three years (1095), a two-year hosting plan (730). - **A repeat that has stopped** (the charges run for months and then end before the statement does) → promote it too, with `ends_on` at its last charge. One commitment, its charges on record, nothing planned past the day it ended. The money stays in every total because the rows do. - **Everything else, and anything you are unsure of** → stays `once`. A flight, a hotel, a gift, a repair, an annual-looking charge you have seen only once, a doctor's visit. When the only doubt is whether it repeats, leave it and put the question in the batch, with your guess: the answer decides the promotion. - **Materiality is for the owner to decide, not you.** Default to recording every line that is not a skip above; a small charge is still a fact. If the owner said "only the commitments" or "skip food delivery", honour that. Never silently drop lines for being small. 4. Model honestly, the way the ledger works (see "The past is stored"): every line paid, with `basis: "statement"`, at the date and amount on the paper; `ends_on` when the source shows an end; a plan only as far as the owner asked. Each row wears the name the owner would say — "Netflix", not "UPI/4521/NETFLIX.COM"; what the line says that matters — a lender, a policy number — goes in its notes. 5. Tags from the library first (the icon then resolves in the app on its own); invent only when nothing fits. Write each commitment's notes — its three details that matter, one per line — as far as the source states them; `Notes` below lists them by category. 6. People: name a member only when the owner named them ("my cook Ramesh" → member "Ramesh", tag household help, and "Role: cook" in the salary's notes). A UPI handle on a statement is not a name the owner gave you. Accounts: the statement is the account — "HDFC savings" or "Amex 1007" (last four digits only); its closing balance and date go on the account as `balance` and `balance_date`. 7. Write, show, ask, correct, promote. One `add_commitments` call (or a few, back to back) with every line; then, when the statement prints a closing balance, one `update_account` on the statement's account — `balance` the closing figure, `balance_date` the statement's last day — so the account reads as the statement left it; `promote_to_recurring` for each payee seen three times or more and still going; the table of what you chose and guessed; one message carrying every question the reading left open — what an unidentifiable charge was, who it was for, how often a charge seen once or twice comes round; the corrections in one pass; `promote_to_recurring` for each further yes. Then stop and report: how many rows, what was promoted and how far it is planned, what you skipped and why, and the balance you set with its day — or that the statement printed none. 8. Finish the picture if asked: two or three `leave_insight` notes. The reports for the months the statements cover are longbook's to draw, and it draws them. ### Worked lines A bank statement, twelve months, the lines that matter and what each becomes: ``` 01/07/2026 NETFLIX.COM 649.00 (and 01/06, 01/05 … 01/08/2025) → twelve one-time rows, one per charge, paid, basis statement, tag streaming. Seen twelve times a month apart and still going: promote_to_recurring the twelve as "Netflix", monthly, and show it — the twelve stay as they are, the plan continues from 01/08/2026 for 24 months. 05/07/2026 HDFC BANK EMI 884211 12,340.00 (every 5th since 05/02/2025) → eighteen one-time rows, then promoted monthly, tag emi, notes "Lender: HDFC Bank" and "Loan account: 4211", one per line. ends_on only if the tenure is known. Never the whole loan account number: the last four digits are the most that leaves the statement. 15/03/2026 HDFC ERGO HEALTH PREMIUM 18,450.00 (once in twelve months) → one row, paid, tag insurance. On the owner's yes: promoted yearly — the next charge planned for 15/03/2027. Its notes — "Policy number: …", "Policy name: …", "Document: documents/hdfc-ergo-health.pdf" — only as far as a policy document in documents/, or the owner, states them. 12/01/2026 STAR HEALTH PREMIUM 9,200.00 and 12/07/2026 STAR HEALTH PREMIUM 9,200.00 → two rows, paid, tag insurance. Seen twice, so a question: "half-yearly?" On the owner's yes: promoted custom, custom_cycle_days 183. 01/07/2026 UPI-RAMESH K-cook 6,000.00 (every 1st) → rows, then promoted monthly, tag salary, notes "Role: cook". member only if the owner named Ramesh; otherwise leave member empty and say so in the summary. 03/06/2026 GODADDY.COM 3,597.00 (once; the owner says it was a 3-year deal) → one row, promoted custom, custom_cycle_days 1095, tag domain & hosting, notes "Registrar: GoDaddy". A 24-month plan writes nothing more: the next renewal is three years out, and add_charges writes it the day the owner asks. 10/06/2026 IRCTC 4,560.00 → one row, tag travel. It stays once. SPOTIFY 119.00 on the 12th, Sept 2025 through April 2026, then nothing → eight rows, then promoted monthly with ends_on 2026-04-12: one commitment, eight charges on record, nothing planned past the day it stopped. 02/07/2026 APPLE.COM/BILL 0.00 then 02/08/2026 APPLE.COM/BILL 99.00 → two rows — the first is a charge of zero, which is what a trial is — tag subscription or storage; say which you chose. Seen twice, so a question: "monthly from here?" On the owner's yes: promoted monthly. 28/06/2026 CREDIT CARD PAYMENT 42,310.00 → skip (the memory holds no bill row for it; the card statement has the charges) 30/06/2026 SALARY CREDIT 1,50,000.00 → one row, direction "in" (money in, kept apart) 14/06/2026 ATM WDL 10,000.00 → skip (a movement) ``` The questions to the owner after the facts are written, in shape — one message, nothing about a charge the statement already settles: > The year is in. Three things and I can finish. (1) ₹5,400 to a name I cannot place, twice > since March — what is it, and who is it for? (2) ₹2,000 on the 7th of every month to a > person's name — who is that? (3) Netflix, rent, the cook, Airtel and the society dues come > round every month, so they are set to continue; say if any is not. HDFC Ergo appears once > and Star Health twice — yearly and half-yearly, I would guess? Say it out loud if that is > easier; rough answers are fine. The summary to the owner once the answers are in, in shape: > 87 charges from the statement, all recorded as paid. Thirteen of them turned out to be > nine commitments that repeat — Netflix, rent, the cook, Airtel, the society dues, Adobe, > Amazon Prime, Claude, and the two premiums — each planned 24 months ahead; Spotify > stopped in April and is on record that way. Twelve salary credits are in as money in, > kept apart from spending. Skipped: two card bill payments — the memory held no bill row > for either — and three ATM withdrawals. HDFC savings now stands at ₹1,23,456 as of > 30 June, the statement's closing balance. Review it in the Proposals screen — keep or undo > it there. ### Statements that arrive as Excel or CSV Columns are usually Date, Narration/Description, Debit, Credit, Balance. Debit lines are charges; Credit lines are money in, written with `direction: "in"`, unless they are the owner's own transfer. Dates come in the bank's format (DD/MM/YYYY is common in India; read the header row and one known date before assuming). A narration often carries the payee in the middle of a reference string — "UPI/123456789/NETFLIX/…" — so match on the word, not the line. ## The past is stored, the future is written — nothing is computed Every charge is a row in `charges[]`, keyed by (commitment_id, date). longbook computes no occurrence from any rule: history is exactly the rows before today, the plan is exactly the rows from today on, and a commitment with no rows has no charges anywhere — not on the dashboard, not in a total, not in a report. - A **commitment** holds only what every charge of it shares: name, `amount` (the price now — what a newly planned charge is written at), currency, `billing_cycle`, category, member, account, notes, details, status, and `ends_on`. Nothing about an occurrence lives there. "Renewing since" is its earliest row. "Next due" is its first unpaid row on or after today. "Planned till" is its last row. - A **charge** holds what is particular: `date`, `amount`, `paid`, `paid_on`, `basis` (what backs it), a `note`. **A charge behind today is paid** whatever its row says: the money was due and the day has passed, so every reader — the app, every tool here — reads it as paid and never calls it an estimate. A row from today on is the plan, and its `paid` mark is what counts. - **A commitment is what still lies ahead.** A recurring one is a commitment while it is active. A one-time one is a commitment until its charge is behind today or marked paid; after that it is that one charge and nothing more — `commitments` leaves it out and counts it, `charges` reads it. A statement of five hundred lines is five hundred charges, not five hundred commitments. - **Nothing is written unasked.** A recurring commitment is planned when it is created — `plan_months` charges from `first_charge`, or exactly `plan_count` of them, 24 months when nobody says. A plan grows only when someone asks: `set_planned_count` sets how many charges stand from today on (240 at most) — more are written a cycle apart after the last, unpaid planned rows past the count are unwritten, paid and hand-set rows stay; `add_charges` writes named days. The app never extends a plan on its own; when one runs short it says so, and `commitments` and `upcoming` name it. A one-time commitment is one charge on its `date`. - **Changing a commitment changes what is shared.** Its `amount` reaches only charges that are unpaid, planned and dated from today (or `from`) on. Nothing before today ever changes because a commitment was edited, and a charge someone priced by hand, or a statement proved, keeps its number. - **Changing a charge changes that charge.** `update_charge` changes one charge where it sits — its amount, its day, its paid mark, the day the money moved, its note; with `and_later` the unpaid planned rows after it follow, a cycle apart, and keep their amounts. A day that already holds a charge is refused. `mark_paid` remains the one-field way to mark. - **`status` pauses, resumes or cancels:** pausing or cancelling unwrites the unpaid planned charges after today and keeps what happened; back to active plans nothing — `set_planned_count` continues it. A cancelled commitment's past charges still count, because they did. `ends_on` is a known end: nothing is planned past it, a charge on the day itself still lands. `free_trial` is a mark on the row — true while the owner calls it a free trial, shown as a pill — and never a charge row. - Cycles: `monthly` and `yearly` step by calendar months, the day clamped into a short month (the 31st becomes the 28th); `custom` steps by `custom_cycle_days`; `once` never steps. The rhythm only matters when a plan is written. - Amounts are per currency, never converted. A USD subscription and an INR rent are two totals, always. A charge may state its own currency when it differs from its commitment's. ## Tags — what kind of thing each row is Every commitment, member and account can wear one tag, and `tags[]` is the vault's own vocabulary for them: a name and an icon. A tag says what kind of thing a row is; it never gives a row a shape. **No tag carries a template**: what is particular about a commitment — a policy number, a lender, a plan — is the lines of its notes, three at most (see `Notes` below); what is particular about an account — a card's printed name, a billing day — is the lines of its notes too; and a member carries a name, a tag, an email and linked accounts, nothing else. A template a vault still holds on any tag is read as none. Tag names match loosely — "Insurance", "insurance" and " insurance " are one tag — so a library name written in any case still picks up its icon. An icon is an SF Symbol name from the library list above — a new tag takes one of those, or none. Prefer the library; invent only when nothing fits ("colony kitty", "pet care"). A vault that grows "Home", "Comms", "Househelps", "Social Media" and "AI subscription" as its own tags is a real vault, and longbook keeps those; but "telecom", "salary", "social media" and "ai" were already there with icons, and an AI that knows the library saves the owner the tidy-up. `rename_tag` renames a tag in the vault's own vocabulary and carries every row wearing it, the owner's tag groups too; `delete_tag` removes it and the rows it wore are left with no category. Both only on the owner's word — a tag the owner typed is theirs. The library, with icons (SF Symbol names): commitment tags: - subscription [arrow.triangle.2.circlepath] - streaming [play.tv] - software [app.badge] - ai [sparkles] - social media [at] - telecom [antenna.radiowaves.left.and.right] - insurance [shield.checkered] - emi [building.columns] - rent [house] - utility [bolt] - salary [banknote] - education [graduationcap] - fees [doc.text] - membership [checkmark.seal] - domain & hosting [globe] - travel [airplane] - health & fitness [heart] - home [wrench.and.screwdriver] - vehicle [car] - gift [gift] - donation [heart.circle] - warranty & amc [checkmark.shield] - tax [percent] member tags: - family [person.2] - household help [person.badge.clock] - team [briefcase] - vendor [shippingbox] - professional [person.text.rectangle] - landlord [building.2] - tenant [key] - friend [hand.wave] account tags: - credit card [creditcard.fill] - debit card [creditcard] - bank account [building.columns] - upi [qrcode] - wallet [wallet.pass] - cash [banknote] - brokerage [chart.line.uptrend.xyaxis] - loan account [doc.plaintext] A tag for a real household, from a real vault's shape: insurance is usually the biggest cluster (term life, health floater, car, two-wheeler, home — most yearly, some half-yearly); home holds rent, society maintenance, electricity, gas, water, internet; telecom holds two or three phone plans; salary holds the cook, the cleaner, the driver; subscription, streaming, ai, software and social media hold the digital stack; domain & hosting runs on 1–3 year cycles; travel and gift are mostly once. ## Categorise my longbook — the rows that wear no category "Categorise my longbook" is one pass over every commitment that has no tag — the ones the app lists under "No category". `commitments` with status "all" gives every row, past and planned, with its tag or none; `charges` over a wide window shows what each one has been paying, which is often the clue. `tags` gives the vault's own names beside the library's: those first, a new one only when nothing fits. Where the name says what the row is, write it: "Netflix" is streaming, "Airtel" is telecom, "HDFC Ergo" is insurance, "Ramesh" paid on the 1st is salary. For the rest, guess from the name and what the row has been paying, and write your guess. One batched `update_commitment` pass, each with its `tag`; then show every guess in one table — name, date, amount, the category you chose — ask the owner to correct any that are wrong, and say a spoken answer is fine. Apply the corrections in one more pass, and stop: "review it in Proposals". A tag the owner names is theirs, in their words. ## Reorganise my longbook — the whole book, once over "Reorganise my longbook" is one pass over everything, the way the owner would keep it if they had an afternoon: names as they would say them, every row in a category, people and accounts linked, doubles merged, nothing lost. longbook's own AI runs it from the app's Automations screen; the owner's AI runs it from the prompt; both follow this chapter, and every change waits in Proposals, where the owner keeps or undoes the whole pass at once. Read everything first: `commitments` with status "all", `charges` over a wide window, `people`, `accounts`, `tags`. Then, in this order: 1. **Names.** A name written the bank's way — "NETFLIX.COM 4402", "ACH DEBIT HDFC ERGO" — becomes the name the owner would say: "Netflix", "HDFC Ergo car insurance". A person's name stays their name. Never shorten what is already a name. 2. **Categories.** Every commitment wears a tag: the book's own words first, then the library's; a new one only when nothing fits. Two tags that mean one thing — "Streaming" and "OTT" — become one, by `rename_tag` onto the one kept, which moves every wearer. Doubt is a tag too: guess from the name and what the row has been paying, and say so in the reason. 3. **People and accounts.** A commitment paid to or for a person is linked to them, `add_person` first when the book has no such person; a commitment the charges show on one account is linked to that account. A card the book shows paying a bank is linked to it. Never guess a person from a role or an amount: only a name the row itself carries. 4. **Doubles.** One payee under two commitments: give both the same right name and category, and say in the reason that they are one — the owner merges them in the app. One charge recorded twice, the same day and amount under two commitments, both paid: `remove_charges` the copy on the newer commitment. Nothing else is ever removed. 5. **Notes.** A note that holds a card number, a whole account number or a password is rewritten without it. Otherwise notes are the owner's words; leave them. What a reorganisation never changes: an amount, a date, a cycle, a status, an ends_on, a planned count, a balance, a charge that happened. A row already right is left alone. One pass, batched; then stop, and say in plain words what was renamed, categorised, linked and merged, and that it all waits in Proposals. ## People and accounts `members[]` is who a commitment is **for** — a child's school fee, a parent's health premium, a team member's software seat. `accounts[]` is what it is **billed to** — a card, a bank account, UPI, a wallet, cash. Either can be a placeholder (`is_incomplete: true`, just a name) until the owner fills it in; both `add_commitments` and `update_commitment` make placeholders for a `member` or `account` name that does not exist yet; `update_account`'s `member` and `linked_to` need a name the memory holds. `accounts` and `people` list every one with what it holds. `add_person` and `add_account` make a person or an account the owner named, with what is known — a name already held is refused rather than doubled; `update_person` and `update_account` change one that exists. Name a person only when the owner did. **A star keeps one first.** The owner stars the people and accounts they look at most: `starred: true` lists them first on People and Accounts, and only starred ones stand in the Mac sidebar. `update_person` and `update_account` take `starred` in their set; set it only when the owner asks, never to tidy. **Hints are not people or accounts.** `hints[]` holds what setup asked: people by role ("Cook") and accounts by kind ("Savings account"). Use them to read statements, and add the real person or account under the name a statement or the owner gives; leave the hint alone. **A person's tag is their role in the owner's finances** — landlord, household help, family, tenant, a professional they pay. Set it with `update_person` when the owner says it ("Ramesh is our cook" → household help); never guess it from a payment. The library's member tags first, a new one only when nothing fits. A member carries a name, a tag, an email and linked accounts — the accounts that belong to them — and nothing else: what the owner says about a person's role belongs in the notes of the commitment it is about ("Role: cook" on the salary). Accounts carry identification only: display name ("Amex 1007"), institution ("American Express"), `last4`, a nickname, a tag, `notes`, and the person it belongs to. What is particular about an account is the lines of its notes, one per line as `Label: value`. A card's **card name** is one — the product printed on it, "Card name: Platinum Reserve", never a number. It is what to read, with the institution, when the owner asks "any offers on my cards?". Write it, and any other detail, with `update_account`'s `notes`, rewritten as the account now stands. `details` on an account or a member is longbook's own, as on a commitment: `accounts` and `members` return any a row holds exactly as stored, and no write takes it — `update_account` and `update_person` refuse a non-empty `card_name` or `details` by name, and name an empty one, as any key they do not take, under `ignored`. An account can name its **linked account** — the account it is linked to: a card to the bank account that pays it, one level deep. The file keeps this under its older key, `parent_account_id`; `accounts` reads it as `linked_to`, and `update_account` takes `linked_to`. An account may also carry a `balance` and its `balance_date` — what it held on that day, as a statement's closing balance or the owner's own word; write it after importing any statement that prints one, dated the statement's last day, and never compute anything from it; `accounts` and `whoami` say when the balance held is older than the newest confirmed charge on that account, and `add_commitments` and `add_charges` answer with a `balance_note` when the rows just written outrun it. The owner's own groups ("credit cards", "debit cards", "household", "team") live in `groups[]` and are labels over existing ids — never create a record to fill a group. ## The documents folder — the source of past truth `documents/` beside the memory is where statements, policy PDFs, invoices and receipts live. It is the permanent record the memory was built from: a statement stays there after it is imported, so any later question ("what did I pay Star Health in 2025?") can be answered from the paper, not from memory. When a row comes from a document, cite it: a line of the commitment's notes names the file by its path (`Document: documents/term-policy.pdf`), and `_provenance[id].source` says where the write came from (`"documents/hdfc-2026.pdf"`). Never paste a document's contents into the vault; reference it. Formats: PDF that carries text, `.xlsx`, `.csv`. A scanned PDF, a `.docx`, a legacy `.xls` or an image is listed as unsupported with a one-line way out ("save it as .xlsx or CSV") — the app's own importer reads `.xls`; the documents tool does not. A file still in iCloud and not yet downloaded is named so and its download nudged — ask again in a moment. ## Document invoices — receipts and bills in the owner's files "Add document invoices in my longbook" means the files, not the mail: the invoices, receipts and bills the owner keeps as documents. Where to look, in order: the longbook folder's `documents/` (`list_documents`, then `read_document` each); the owner's Documents and Downloads folders only when your own app can read files there — Claude Desktop on a Mac with file access — because no longbook tool reaches outside `documents/`; through a hosted door, ask the owner to attach the files to the chat, or to drop them in `documents/`. An invoice or a receipt is one charge. Paid, at the day it names, when it says paid or that day has passed; `paid: false` at its due date when the due date is ahead. Name it as the owner would say it — "Plumber, kitchen tap", never the invoice number — with `basis: "statement"` (the paper is the evidence), `source: "documents/"` and a `Document: documents/` line in its notes; the amount, the currency and the day only as printed. `search` the payee first: a charge already written from a mail or a statement for that amount and day is the same money, and the invoice gives it the better name and note — update it, never a twin. When the payee is a commitment the memory holds, the invoice is that commitment's charge: `add_charges` on it. One batch, then stop: it waits in Proposals. ## Notes — the three details that matter A commitment's `notes` holds its three most important details, one per line, each as `Label: value`. There are no per-category fields; the category decides which three, not the shape. The owner reads the note whole on the commitment's own page. Examples by category — pick the three the owner would look for first, and leave out any the source does not state: - insurance — Policy number; Policy name, the official name as printed; Document, the policy's file name in `documents/` ("Document: documents/hdfc-life-policy.pdf") - emi — Lender; Loan ends; Loan account, last four digits only - rent — Landlord; Deposit; Lease ends - subscription, streaming, software, ai, social media — Plan; Billed through (App Store, Google Play, a card's name); Seats or profiles - telecom — Provider; Plan; Number, last four digits only - utility — Provider; Consumer number; Place ("Home", "Office") - salary — Role; Paid by (UPI, cash, bank transfer); Started - education, fees — Institution or payee; Student; Term - membership, health & fitness — Organisation; Membership number; Valid till - domain & hosting — Registrar; Domain; Auto-renew - travel — Destination; Dates; Booked through - home — Kind; Vendor; Invoice, the file name in `documents/` - vehicle — Vehicle; Registration; Kind (service, insurance, fuel) - warranty & amc — Item; Vendor; Coverage ends - tax — Kind; Period; Receipt, the challan's file name in `documents/` - donation, gift — Organisation or recipient; Occasion or purpose; Receipt, the file name in `documents/` - any other tag — the three the owner would look for first The rules: 1. **Three lines at most**, one detail to a line — `Policy number: 88213`, never a paragraph. 2. **Only what the source states.** When it leaves some out, ask the owner once, listing the gaps, rather than guessing. 3. **Never a secret.** No card number, CVV, expiry, PIN, password or OTP, and no whole bank or loan account number — the last four digits are fine. The validator refuses a save carrying a card number or a credential, and names the row. A number of 13 to 19 digits can read as a card number and be refused: write its last four digits, and name the document that holds it in full. Dates in notes are written YYYY-MM-DD, never 03/27, which can read as a card's expiry. 4. **The present, not a log.** When the commitment changes — its price, its cycle, its member, what you learned about it — rewrite the note so it describes the commitment as it now stands. Never append "updated on…" lines: the Proposals screen keeps every version, and `_provenance` keeps who wrote what and from where. 5. **A document is named, never pasted** — by its path in `documents/`. A charge's own `note` stays a word about that one charge. `details` on a commitment is longbook's own — `statement_line`, the words a statement printed, written when longbook reads a statement itself: read it, never write it. An account's `notes` hold its details the same way, one per line as `Label: value` — on a card, "Card name: Platinum Reserve", the product printed on it, never its number. A member has no notes: a name, a tag, an email and linked accounts are all it carries. A truer start is a question for the owner, not a decision: "this insurance has run since 2018" when the file's first row is 2024 means the earlier premiums were paid — the only question is whether the owner wants those years in his memory. Ask. If yes, `add_charges` with `paid: true`, at the year's premium where a document says it and at today's otherwise, noting which. "Since" is the earliest row, so writing them moves it. Where your own reference belongs: `_provenance.source` for where a write came from (`"gmail:18c2…"`, `"documents/hdfc-2026.pdf"`), a charge's own `note` for a word about that one charge. A commitment's notes hold its details, never your bookkeeping. Everything you keep is in the file the owner reads. ## Reading reports — drawn by longbook, read by you longbook draws one report for every calendar month holding a **confirmed** charge, into `reports/-.json` — `july-2026.json`, `june-2026.json` — and redraws it when the month's rows change. It also draws spans of days: a trip the owner adds in the app, and any span asked for with `create_report` — redrawn with the months, on the same rules. A charge is confirmed when it is dated today or earlier and its `basis` is anything but `"plan"` — a statement, a mail, the owner's word, the app's own paid-tap. A row with no `basis` reads as `"plan"`, as it does everywhere else. **That one rule decides both things: which months are drawn, and what each report counts.** A month holding nothing but the plan draws no report, because a report of it would describe a month nobody has confirmed; the month ahead never draws; and a transfer confirms nothing. No statement is needed — charges the owner typed draw their month like any other. **A report counts confirmed charges only**, in every figure it holds: the total, the count, "Money in", both rings, the charge list, and the month it is compared against. So a month part-way confirmed reports the part that is confirmed and no more — a report is what moved, not what is expected to. The rest of the month is still on the timeline and in the dashboard's own figures, which do count a past row whatever its basis; a report is the one surface that will not. **And it says when it is short**, so you never read a light month as a cheap one. A month is **part-covered** when a charge in it, in the report's own currency, has passed and is not confirmed. Then, and only then, the stats carry a **"Not yet confirmed"** stat with the count of those charges — the "Total expenses" figure is what longbook can account for, and that stat says how many charges it could not. Coverage is measured in the report's own currency, because that is what its figures are in. A dollar row nobody has confirmed does not make a rupee report part-covered; the dollar month is its own report, with its own coverage. **`delta_pct` and `delta_is_good` are absent** whenever a change cannot honestly be drawn, which is any of three things. Read absence as "longbook would not say", never as a month of no spending, and never compute the change yourself from two reports: 1. **The month is not over.** The current month's report covers its passed days only, so it never carries a change: five days of September against the whole of August is a fall of 98% that did not happen. A month gets its change the first time it is drawn after it ends. 2. **Either month is part-covered.** A whole month against a short one reports missing evidence as thrift, and `delta_is_good` would call it good news. 3. **The month before holds no confirmed spending in this currency** — the vault's earliest month, or a month whose spending was all in some other currency. There is nothing to compare with, which is not the same as nothing having been spent. Say what the stats say. A report carrying "Not yet confirmed" is the month as far as longbook knows it, and the way to make it the whole month is to reconcile the month — a statement, the mail, or the owner marking what they recognise — after which that stat goes and, once the month is over, a change appears. Never present a part-covered figure as the month's spending, and never fill the gap with an estimate of your own. longbook still draws every report, and a file you put in that folder is not what the app draws. `create_report` is how an AI asks it to draw one — over days, accounts, people or categories the owner chooses ("create-a-report"). What you do with a report is read it, say what it shows, and — when the owner wants it different — change the rows behind it, so the next draw says the true thing. `reports` is the tool. With no id it lists what is there, newest first by the last day each covers: the title, its `start_date` and `end_date`, its `scope`, the month when it is one, the day the file was written, how many modules and how many comments each holds, and the share link when one is live. With an id it returns that report whole — every module as the app draws it, each with its own comments. A drawn report holds these modules, in this order: 1. **`stats`, "Total expenses"** — the stat "Total expenses" is the month's total, against the month before when a change can be drawn (`delta_pct`, `delta_is_good`, and `delta_label` naming the month, "vs August"); money in another currency is a further "Total expenses" stat in that currency, never blended in; "Charges" counts every row the list holds, whatever its currency; "Not yet confirmed" appears only in a part-covered month; "Money in" when the month had any. 2. **`donut`, "Accounts"** — one slice per paying account, biggest first, with "No account" among them when charges were billed to none. 3. **`donut`, "Categories"** — one slice per tag, biggest first, "No category" among them. 4. **`transactions`, "All charges"** — every confirmed charge in the month, biggest first, uncapped. A row names its charge (`id`), its `account` and its `member` when it has them. A row the owner took out of the report carries `removed: true`, is listed last, and is in no figure; a charge dropped for good is named in the envelope's `hidden_ids` and listed nowhere. The total and the charge list are always drawn. Both rings are drawn from the report's own currency, so a month holding no charge in that currency holds neither ring: a drawn report is one, two or four modules, never three. Only the report's own currency enters a ring; a slice in any other is listed beside it with its own sum and no share. An `Accounts` slice is an account name the owner typed and is shown exactly as typed; a `Categories` slice is a tag, and a screen shows it with a capital. The envelope: `version` 2, `id` (the filename), `title` ("July 2026", or the name the owner gave — "Goa"), `subtitle` ("Drawn by longbook"), `start_date` and `end_date` (the days it covers), `period` (`"2026-07"` only when it is one calendar month), `scope` (the `accounts`, `members` and `categories` it was narrowed to, lists that must all match; absent means all of them, "No account" stands for charges billed to no account and "No category" for untagged ones), `hidden_ids`, `created_at` (the day the file was written, never the days it covers), `currency`, `modules`. A month's id is `-`. A report over any other span of days has the same shape, with its own `start_date`, `end_date` and `scope`, and no `period`. A **trip**'s id is `trip-` with its name and first day — `trip-goa-2026-09-28`; a span asked for with `create_report` is named `report-` the same way — `report-school-fees-2026-07-01`. When a report with other days or another scope already holds that id, the new one takes `-2`, then `-3`; the same name, days and scope again redraws the same file. longbook redraws both from their own name, days and scope whenever it redraws the months. A trip still running — started with "Start trip mode" and not yet ended with "End trip" — is named in `longbook.workspace.json` (`openTrip`, longbook's own), and its `end_date` moves to today at each redraw until it ends. A span's `currency` is the one most of its confirmed charges are in, else the home currency. The listing's order is the Reports screen's: the newest last covered day first, then the newer file, then the filename; a report naming no days lists last. A file older than this version may hold other module kinds — `prose`, `bars`, or a kind this text does not name. They still read, and `reports` returns them as they are. ### Comments Every module carries a `comment`: the owner's own line, and the lines pulled back from a shared link when they shared the report with someone. `reports` returns them per module, one string per line, in the words they were written in. Quote them; never rewrite them, never summarise them into something the writer did not say. They are the one part of a report nobody drew. ### What the owner asks for, and what to do - *"What did I spend in July in my longbook?"* — `reports` with the month, and read the "Total expenses" module back. The figures are drawn from the rows; quote them, never recompute them. - *"Why is July so high?"* — the "All charges" module is the month, biggest first. Name the charges that made it, not a share of a total. - *"This category is wrong"* / *"that charge was for my mother"* — that is the month's data, not the report. Fix the row — `update_commitment`, `add_charges`, `remove_charges`, `update_account`, `update_person` — and say the report redraws itself from it. - *"What did Meera say?"* — the comments on the module she left them on. - *"Make a report of my Goa trip in my longbook"* — the prompt `Make a report of my trip`, built on "create-a-report": ask for its days, accounts and people, then one `create_report`. A report is never edited into agreement with the owner. The rows are the memory; the report is what the rows come to. ## Month report — adding the month the owner names "Add September month report in my longbook" asks for September made whole, so that longbook draws it: you write no report, and `reports` only reads. Say where you are in time first — the month named, and today. Then `charges` from the month's first day to its last (and `reports` on the Mac, when the month is already drawn) for what is there and on what basis: a passed row whose basis is "plan" is unconfirmed, and it is what puts a "Not yet confirmed" stat beside the report's "Total expenses". Ask which bank and card statements cover the month: on the Mac, `list_documents` names what is in `documents/`; through a hosted door, ask the owner to attach them. Import them by "importing-statements" — every line a one-time row at its real date and amount, paid, basis "statement". A row already there that the statement proves is `add_charges` on it, paid, basis "statement", never a twin; a charge the month held only as a plan is confirmed the same way. Each statement's closing balance goes on its account — `update_account` with `balance` and `balance_date` the statement's last day — so the Accounts module shows the month's end rather than an older day; skip it when the statement prints none. Then say it: longbook draws the report the next time the app applies the change, with a "Not yet confirmed" stat beside "Total expenses" until every passed charge in the month is confirmed, and without it after. ## Create a report — any days, narrowed to accounts, members or categories "Create a report in my longbook" asks longbook to draw a report over days the owner chooses — a trip, a quarter, one person's spending, one card's month. longbook draws it from the ledger: you write no report, and you never compute or state its figures yourself. Before you call anything, ask the owner, in one message: 1. **The name** — what the report is called on the Reports screen ("Goa", "Q3 household"). 2. **The first and last day.** The first is today or earlier; 366 days at most, both counted. 3. **Which accounts and which people.** Choosing nobody means everyone. 4. **Categories** — only if they want to narrow by them. Then read `accounts`, `people` and `tags` for the real names, and pass each as the memory holds it. "No account" means charges billed to no account and "No category" untagged ones; any other name the memory does not hold is refused with the real names, and nothing is ever created to fit. Make ONE `create_report` call — `title`, `start_date`, `end_date`, and `accounts`, `members` or `categories` only where the owner narrowed it; on a hosted door, with an `idempotency_key`. Then say that longbook draws it on its Reports screen the next time the app is open, counting confirmed charges only as every report does, and stop. Nothing waits in Proposals. The same name, days and scope asked again redraws the same report, so a retry is harmless. To change what it says, fix the rows — the charge and commitment tools — and longbook redraws it. Other days or another scope is another report. Once drawn, `reports` reads it on the Mac. With only the folder there is no way to ask: `requests/` is longbook's own, never written by hand. A trip is the same kind of report. The owner can also add one in the app, under Settings → Trips on the Mac or More → Trips on iPhone, and start trip mode for one still running. ## Leaving insights Beside the ledger you can keep `insights.json` — short notes about what the numbers cannot say, shown inside the app on the module each note belongs to. `leave_insight` appends one; by hand the shape is: `{ "version": 1, "insights": [ { "module": "dashboard.this_month", "text": "August runs over July — the travel booking, not a trend.", "created_at": "2026-08-07" } ] }` Append new entries rather than editing old ones; the last entry for a module wins. Module ids: `dashboard.next_payment`, `dashboard.active_commitments`, `dashboard.next_30_days`, `dashboard.this_month`, `dashboard.by_account`, `dashboard.by_category`, plus `account.` and `member.` for one account or person. Say what the numbers cannot; never repeat them. Quote the owner's names exactly as typed. An id from a newer contract than the app is kept for the version that names it — it is not an error. **There are no `reports.*` ids.** The Reports screen shows the months longbook drew and nothing else, so `reports.summary`, `reports.cash_flow`, `reports.spendings`, `reports.by_account` and `reports.top_charges` name screens that are not there; a note written on one has nowhere to appear, and longbook says so instead of hiding it. What you would have said about a month belongs in the conversation, or on the dashboard module it is about. Notes already written against those ids stay in `insights.json` exactly as they were; longbook does not edit the owner's file to tidy its own vocabulary. ## Unattended runs — when no one is watching An AI that runs on a schedule (a morning brief, a receipts sweep, a renewal watch) writes into longbook without the owner in the conversation. The rules above still hold; these are added because nobody is there to catch a mistake: - **Call `whoami` first, every run.** One call tells you who the connection belongs to, what it may do, what is left of the rate allowance, whether a hosted copy exists, how many proposals are waiting for the owner, how many FAILED to apply, and the vault's real tag names. If it says `can_write: false`, read `how_to_enable`, and say that sentence to the owner rather than trying anyway. `failed_to_apply` lists the newest suggestions the app could not carry out (up to 20; `failed_to_apply_count` is the total), each with its reason — usually a name the memory does not hold. Fix the cause with the real names and file again under the same `idempotency_key`, so the corrected proposal replaces the failed one; never resend the same payload. - **Both doors write; they land differently.** On the owner's own machine the local server writes the file, and the batch appears in Proposals at once. From the cloud — either signed in as the owner, or carrying a key with `memory:propose` — a write is a **proposal**: nothing out there touches the memory, and the owner's app applies it, on their Mac or their iPhone, whichever they open next, where it stands in Proposals to keep or undo like any other outside edit. `write_mode` in whoami says which door you are at. The honest sentence to the owner is "it will be waiting in Proposals the next time you open longbook", never "done". - **If the write tools refuse with `insufficient_scope`, read `how_to_enable` and say it.** Suggestions from a signed-in AI are off until the owner turns them on ("Let signed-in AIs suggest changes", on the app's Automations screen); a key needs "Read and suggest changes". The tools are listed either way, so you can always tell the owner exactly what to switch on. - **Idempotency is yours to keep, and the door helps.** Send an `idempotency_key` on every hosted write, built from the SOURCE and the WRITE — not from the date: `{source id}:{tool}:{short hash of the payload}`, e.g. `18c2a:add_commitments:9f3c1d`. A date in the key is wrong in both directions: two writes drawn from one email collide inside a single run and the second is silently swallowed, and tomorrow's date makes a fresh key for the same email, filing a real duplicate. A repeat answers `created: false` with the same entry id: that is success, not an error; do not send it again a third way. The promise holds for ninety days — a suggestion is deleted ninety days after it was filed, and a key that old files afresh. Where no source id exists, `search` for the name and check `notes` and `_provenance.source` before adding. - **Cite the source on every write**: pass `source` (`"gmail:18c2…"`, `"documents/icici-aug.pdf"`). It becomes `_provenance.source`, and the owner reads it in the diff beside your key's name. - **Read the error code, not the sentence.** Every refusal carries one: `rate_limited` (wait `retry_after` seconds, then continue), `validation_failed` (fix that row or drop it — never retry the same payload), `insufficient_scope` / `key_revoked` / `key_expired` / `entitlement_required` (stop and tell the owner; nothing you retry will help — the hosted copy needs longbook Cloud, and on a Mac the same work is free through the local server), `conflict` or `upstream_error` (try once more later). `advice` says "retry" or "stop" in as many words. - **One batch, one report, silence.** Write once, leave a short `leave_insight` if it helps, and end. Never loop write-check-rewrite. - **Tell the owner, never decide for them.** An unattended run adds `once` rows and proposes promotions in its note; it does not cancel, does not promote, does not change amounts on its own authority unless the source is a price-change notice from the payee itself. The one plan it extends: when it marks a charge paid and no charge is written after it, it writes the next one — one cycle on, `add_charges` at the commitment's price — so the commitment keeps a next due. Say so in the report. - **The key lives in a credential store, never in a file or the prompt.** On a Mac the app offers "Save to keychain" the one time the key is shown; a run on that machine then reads it without the key ever touching disk: `security find-generic-password -s "longbook access key" -w` (the first read asks once — choose Always Allow). Hand it to an MCP config through the environment, not by pasting: `export LONGBOOK_KEY="$(security find-generic-password -s 'longbook access key' -w)"` and then `"Authorization": "Bearer ${LONGBOOK_KEY}"` in the connector config. In a cloud scheduler, use its own secret store the same way. Keys are named, scoped and revoked on the app's Automations screen; a revoked key stops at once, and an expiring one is reported by `whoami` before it bites. A morning brief, end to end: `whoami` → `upcoming` (30 days) and `next_payment` for the lines the owner wants read out → for each receipt or notice in the mail: `search` the payee, then `add_commitments` (or `update_commitment` for a price change, `mark_paid` for a "payment received", plus the next cycle's charge when the plan ended with it) with `source` and `idempotency_key` → one `leave_insight` if something is worth saying → report to the owner what was filed and that it is waiting in Proposals. ## The daily email round — a scheduled look at the owner's mail The owner may ask you to "set yourself up for a daily longbook email round". It is one scheduled task: each morning you read the last day of their mail for money that moved, or is about to, and write what you find into longbook. Everything in "Unattended runs" holds; this chapter is the method. **Setting it up, once.** 1. Call `whoami`. If `can_write` is false, say its `how_to_enable` sentence and stop there. 2. Check that you can read the owner's mail. If you cannot, say so: the round needs a mail connector. 3. Create a scheduled task that runs every day at 06:00 in the owner's time zone. Its instructions: call `read_guide` with "daily-email-round" on the longbook connector, then do "Each run" as that chapter says — so every morning reads the method as it stands that day. If your app has no scheduled tasks, say so, and offer to run the round whenever the owner asks for it. 4. Run the first round now, so the owner sees in Proposals what a morning looks like. **One month, on request.** "Add email invoices in my longbook" is the same reading, once, over the calendar month so far — no scheduled task. `whoami`, then steps 2 to 7 of "Each run", with the search window the month's first day to today, taking the kinds of mail below as they stand, a bill due ahead included at its due date. Report once and stop. **Each run.** 1. `whoami` first. Anything under `failed_to_apply` is earlier work that did not land: fix it with the real names and file it again under the same `idempotency_key`. 2. Search the mail since the last round — the last 24 hours, or 36 to be safe — for the kinds of mail listed below. 3. Read the memory before you write: `accounts` gives every account with its type, its notes (a card's name among them), last four digits and next unpaid charge, and `people` every person with their tag; `commitments` gives what still lies ahead with its price, cycle, category, account and person; `charges` with `from` set to the first day you are looking back over gives what is already written, card bills among them. A mail whose amount and day are already there was written by an earlier round, or by a bank message: skip it. This look is what stops a twin; an idempotency key only protects a retry of the same call. 4. The fields longbook takes are in each write tool's input schema and in the chapter "shape": a name, an amount with its currency, a date, `billing_cycle`, `paid`, `basis`, `direction` for money in and for a transfer, and, when the mail says so, a category, an `account`, a `member` and a short note. 5. From each mail draw exactly those. The amount and its currency. The day the money moved, which is not always the day the mail arrived. Who it went to or came from, as a plain name: "Corner Grocery", never the payment handle, never "UPI transfer". The account in the memory whose name carries the last four digits the mail shows. A category from `tags`, the library's before a new one. A person only when the memory holds them. 6. Write each mail once, in a call of its own, so it carries its own `source: "gmail:"` and an `idempotency_key` built from that id. `search` the payee first. Money that belongs to a commitment the memory holds goes on it with `add_charges`: the open charge's own date, `paid: true`, the amount, `paid_on` and `basis: "email"` — a row already there for that day is updated in place — and a new date only when no charge is open. Anything else is an `add_commitments` row of its own, `basis: "email"`, paid or unpaid as its kind below says. 7. End with one short report: how many rows, which mails you skipped and why in a line, what needs the owner's word (a row that looks recurring, an account the memory does not hold), and that it is waiting in Proposals. Then stop. **The kinds of mail, and what each becomes.** - **A bank's debit alert** — UPI, card, net banking: "Rs.250.00 is debited from your account ending 1234 towards VPA shop@bank (SHOP NAME) on 05-08-26". A charge: a one-time row at its real date and amount, paid. The name is the shop or person the mail names, the words in brackets after the handle. When the payee is a commitment the memory holds — a subscription, a premium, a salary the owner pays — the alert is that commitment's charge, never a twin beside it. - **A card alert** — "Thank you for using your Debit Card ending 1234 for Rs. 1,499.00 at MERCHANT on 07-08-2026". The same. - **An autopay or e-mandate chain** — a consent request with an OTP, "consent received", "deducted from your account", "successfully paid": four mails, one payment. Write it once, from the mail that says the money left. A consent request, an OTP and a pre-debit notice are not charges; when a pre-debit notice names a new amount for a commitment the memory holds, say so in the report. - **A receipt or an invoice from the seller** — a subscription receipt, a service invoice, a ride, a food order. Usually the same money a bank alert already reported: match on the amount and the day, write it once, and let the receipt give the better name, the category and the note. A receipt paid in cash has no bank alert and is a charge of its own. - **A bill that is due** — "payment is due for your service, amount 1,800.00". When the memory holds the commitment, the bill is its next charge: check that charge's date and amount, and add nothing. When it does not, a one-time row, `paid: false`, at its due date; when its payment shows in a later round, that row is the one to mark paid. - **Money in** — salary, a refund, interest, cashback, a dividend, once the bank says it was credited: a one-time row with `direction: "in"`, paid. A notice that a refund is on its way is not the credit. - **An insurance or renewal notice** — when the memory holds the commitment, check its next charge's date and amount against the notice; correct the charge only when the notice comes from the payee itself, and otherwise say what differs in your report. When the memory does not hold the commitment, add a one-time row, unpaid, at the due date, and propose the cycle in your report. - **A credit card statement is ready** — ONE one-time row named for the card and the month it falls due, " bill, " ("Coral card bill, October 2026"), `direction: "transfer"`, `paid: false`, at the due date, the amount the total due, `account` the card when the memory holds it, `basis: "email"`. The month is in the name because a card has a bill every month: each is its own row, and a name two rows share cannot address either. Never spending: every spend on that card is a charge already, and the bill that pays them is a movement between the owner's own accounts — a transfer is shown and counted in nothing. A bill already written for that month is corrected in place, never doubled. Import the statement's own lines, by "importing-statements", only when the owner has asked for card statements to be imported. - **A card bill payment** — it settles the transfer row for that month: find it with `search`, and address it by id. `add_charges` on that row, its own date, `paid: true`, `paid_on` the day the money moved, the amount paid, `basis: "email"` — updated in place. Skip it only when the memory holds no such row. - **A wallet top-up, a transfer to the owner's own account, an investment bought** — movements. Skip them, unless the memory holds the payment as a commitment of its own — a monthly investment plan, a standing transfer — and then it is that commitment's charge. - **Skip entirely** — OTPs, sign-in alerts, offers and newsletters, brokerage and mutual fund statements, margin and portfolio reports, tax intimations, forex mark-up notices (the fee arrives as its own debit), and anything that names no amount. The last four digits of a card or an account are for matching only. Never write a card number, an account number, an OTP or a tax id anywhere. ## The shape - `app` — `"longbook"`. - `version` — the file format, currently 3. Not a revision counter; leave it alone. - `commitments[]` — the only required list, and only what every charge shares: name, amount (the price now), currency, billing_cycle (monthly | yearly | custom | once — shown as Commitment Type), status, free_trial (true while the owner marks it one), tag, notes (its three details that matter), details (longbook's own statement line — read, never written), ends_on, direction (in | out | transfer — only "out", or none, is spending), and two foreign keys: member_id, account_id. Nothing about an occurrence lives here. - `charges[]` — every charge, past and planned, one row per commitment per due date: commitment_id, date, amount, paid, and optionally currency, paid_on, basis (statement | user | mark_paid | plan | email), note, written_at. The only place an occurrence exists. - `members[]`, `accounts[]` — who a commitment is for, and what it is billed to. The `people` tool lists the first; the file keeps its older key. Both carry a tag, details — longbook's own, read as stored, never written — and starred (true when the owner starred one to keep it first; set it only when they ask); an account also notes (its details, one per line, a card's name among them), member_id (the person it belongs to) and parent_account_id (its linked account). - `tags[]` — the vocabulary: kind (commitment | member | account), name and icon. No tag carries a template. - `groups[]` — optional; the owner's own labels over what already exists, never new records. An "account" group lists account_ids, a "member" group member_ids, a "tag" group tag_keys — one named "business" holding tag_keys groups those tags wherever longbook totals spending. A tag belongs to at most one group; anything ungrouped is simply ungrouped, not "other". - `to_fill_in[]` — optional; commitments the owner named at setup or ticked from a statement, and nobody has filled in: id, name, group, tag. Not commitments. Find each one's amount and day, write it with `add_commitments` under the same name, and the entry goes; ask the owner for what you cannot find. - `hints[]` — optional; people and accounts the owner picked at setup by role or kind: id, name, kind (member | account), tag. Not members or accounts, and nothing points at one. Read statements with them in mind; add the person or account under the name a statement or the owner gives, and leave the hint alone. - `_provenance` — optional map keyed by record id: who last edited it, when, from what source. longbook adds `reviewed_by`/`reviewed_at` when the owner keeps your change and keeps an entry for a row it removed; leave both alone and replace the whole entry when you edit a row. Neighbours in the folder: `documents/` (statements and source documents — the owner's and yours to add to), `reports/` (reports, JSON — one per calendar month, named for it, plus trips and the spans asked for with `create_report`; all drawn by longbook and read with `reports`), `requests/` (longbook's own: what an AI asked of it through `create_report` on this machine, waiting for the app, which carries each out and removes it — never write or edit it by hand), `insights.json` (your notes), `library.json` (the tag library this text lists), `longbook.schema.json` (the formal schema), `longbook.workspace.json` (display preferences — longbook's own, never edit) and `.longbook/` (change history — never edit; editing history would defeat the owner's ability to review and undo changes, yours included). ## The past is evidence A charge row is the truth about one occurrence, and `basis` says what backs it. Best first: `"statement"` and `"email"` — a document says so; `"user"` — the owner said so, or priced it by hand; `"mark_paid"` — the app's own paid-tap; `"plan"` — written ahead at the commitment's price, confirmed by nobody yet. Anything but `"plan"` is a fact, and nothing reprices a fact. - The owner's word is the starting point, not the last word. Write what they tell you with `basis: "user"`. - A document is better evidence. When a bank statement or an email arrives, reconcile against it: correct the amount AND the date (`paid_on`), set `basis: "statement"` or `"email"`, and point `_provenance.source` at the document in `documents/`. `add_charges` does this on a row that exists — a row already there for that day is updated in place; a row that does not is written. `mark_paid` carries no basis, so it is for the owner's word alone. - Never replace a statement- or email-backed figure with the owner's recollection. Evidence does not degrade. - One row per occurrence, ever. Correcting is not a second row beside the first: change the row that exists and let `basis` say where the number now comes from. - `basis: "mark_paid"` is longbook's own paid-tap. Never write it yourselves. - A row that has passed and is still `"plan"` is unconfirmed, not wrong. Leave it, mark it when the owner says it was paid, or correct it when a statement arrives. The future is the plan as written, and only as far as it was written. Never write a charge you were not asked for, and never remove a paid one without saying so. ## Invariants - Foreign keys must resolve — a dangling member_id or account_id blocks the save. - Amounts are finite and non-negative; dates are real calendar dates. - `name_key` (trimmed + lowercased name) is the dedup key for members, accounts and tags. - Details values are scalars or flat lists — never nested objects. - `custom_cycle_days` only with `billing_cycle: "custom"`. One charge per commitment per date. A one-time commitment has exactly one. - An invalid file on disk is preserved and reported, never silently overwritten. ## Rules for anything that writes 1. Preserve ids — they are UUIDs and they are load-bearing. 2. Never convert or blend currencies. Totals are per-currency, always. 3. Never write a charge nobody asked for; plan as far as the owner said, and say how far. 4. Sign your work in _provenance; cite the document a row came from. 5. Prefer cancelling over deleting — a cancelled commitment keeps every charge that happened and drops only the plan ahead. `delete_commitment` removes a commitment and every charge it ever had, and is for the owner's own sentence "delete it", never for tidying. 6. Never write a full card number, CVV, expiry, PIN, or credential anywhere in this file. 7. Keep source documents in documents/ and reference them; never paste their contents. 8. Dedupe before adding; once when in doubt; one batch, then stop for review. Names are read on one-line cards: a commitment's name is whole up to about 28 characters, an account's or a person's up to about 20, and a longer one is cut with an ellipsis on the phone. Write names the owner would say, short enough to be read whole. 9. longbook drops a malformed entry alone — a charge that fails the schema vanishes from every screen while the rest of the vault keeps working. If something you wrote is not showing up, do not rewrite it blind: validate this file against `longbook.schema.json` and check the vault's shape first. 10. Read before you write. Every `read_guide` answer carries a `guide_key`, and so does a prompt's own message; pass it with every write the procedure makes. A write without one, or with one from another rulebook, is answered with a `guide_warning` naming what to read; from the next rulebook it is refused. Charges with basis "statement" take the key of `importing-statements`, or of a prompt built on it. The formal schema: `longbook.schema.json`, right beside this file. The seeded tag library with icons: `library.json`, beside it too. Both are served by the longbook MCP servers as resources, and `read_guide` returns any chapter of this text by name. The owner's prompts: `prompts.json`, beside them, the same list the `Prompts` chapter holds.