P3: rename accounts, one set of period defaults, tax categories warn or refuse (defects 15, 16, 8)
check / check (push) Canceled after 0s

books.rename_account; account names trimmed, bounded, refused when a
sibling's or TIN-shaped; books.show refuses what its target ignores. A
report asked for without dates covers this fiscal year so far, everywhere
(one rule for tools, descriptions and the viewer; the IIF and CPA package
keep their own dates). policy.require_tax_category warns or refuses; a
Form 4797 gain is shown apart on every return.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 10:41:24 -06:00
1 parent 8255ac2103
commit 9ab003d0d1
69 files changed
+8063 -570

No files matched your search

+205 -7
View File
@@ -1080,12 +1080,19 @@ lowers the format in force. Opening the books checks every complete line
before it changes anything: a cut-off last line is set aside only when the
lines before it pass, so an older version never alters newer books.
A feature (`ledger_store::format::KNOWN_FEATURES`) is a field or value on
an event type an older version knows but would misread; the writer declares
it with the format in force (at least 2) just before the first line that
needs it, so books that never use it stay readable by versions before it:
`members_cumulative` (P3, B-13: a members close counted from its count's
first close, `"rounding": "cumulative_since"`).
A line an earlier version can't read is announced by a journal feature
(`ledger_store::format::KNOWN_FEATURES`): a new event type, or a field or
value on an event type an earlier version knows but would misread. The
writer declares it once, in the `journal.format_set` just before the
first line that needs it, with the format in force (at least 2) and the
features already in force (`Store::ensure_feature`): an earlier reader
stops at the declaration, and books that never use the feature stay
readable by it. P3's: `members_cumulative` (B-13: a members close counted
from its count's first close, `"rounding": "cumulative_since"`),
`account_renamed` (before the first `account.renamed`, B-31) and
`require_tax_category` (before the first `settings.set` naming
`policy.require_tax_category`, B-32). A later piece asks the owner's
go-ahead before the first such declaration (plan D-39).
**Enforced:** `ledger_store::format` (`JournalFormat::admit`, `declared`,
`gate`, `settle`, `declaration`), `journal::ENVELOPE_KEYS`,
@@ -1281,6 +1288,196 @@ checked to change no figure or entry; `approval_rules_off_by_default`,
`tests/spec_kept.rs`; Lean `Step.record_changes_no_figure`,
`Reachable.records`; fuzz `fuzz_settings_replay`.
## B-30 A report asked for without dates covers this fiscal year so far
**Status:** AUTHORITY for the period (IRS Pub. 538,
`sources/irs-periods-and-methods.md`: "A tax year is an annual accounting
period for keeping records and reporting income and expenses"; 26 U.S.C.
§6041(a), "in any calendar year", and §6041A(a)(2), "during such calendar
year", `sources/irs-1099-thresholds.md`, for the calendar-year counts);
DECISION for using it as the default (no authority says what a tool's
omitted dates mean; the owner, 2026-10-20, `plan/served.md` P3 "Settled in
design": "a report asked for without dates covers this fiscal year so
far, everywhere, and an account's register opens on it with 'Show all
dates' one click away").
The books' annual period is the fiscal year (`company.fiscal_year_start`,
B-29). Every place a date may be omitted resolves it by one rule
(`ledger_core::calendar::PeriodDefaults`), so the agent's report tools,
`books.show`, the viewer and the tool descriptions can't say different
things:
| Kind | Used for | Both dates omitted | Only `end` given | Only `start` given |
|---|---|---|---|---|
| A period | profit and loss (and by class), cash flow, tax summary, members' equity, general ledger, general journal (csv, json, xlsx), an account's register, `books.show` of an account or a report | the first day of the fiscal year that holds today, through today | the first day of the fiscal year that holds `end`, through `end` | `start` through today |
| An instant | balance sheet, trial balance, A/R and A/P aging, fixed asset register, the IIF's `as_of` (every posted entry from the first through it, B-34), the CPA package's `as_of` | today | | |
| A calendar year | party totals (`books.parties`, `books.party`, the viewer's party pages), the viewer's 1099 worksheet | today's calendar year | | |
- A resolved start after the resolved end is refused, whether the caller
gave both dates (`InvalidDateRange`) or only a start after today, the
end an omitted end means (`StartAfterToday`, whose message says the end
was defaulted: "pass end"). It is never swapped or clamped to fit.
- A derived start before 0001-01-01 is the calendar's first day (only an
end early in year 1 under a non-January fiscal year, B-28).
- The first year: when the company was formed after its fiscal year's
first day, the default start comes before formation. The amounts are
the same, and the start is not moved to `company.formed`: start-up and
organizational costs (IRC §195, §709) can be dated before formation.
- "Today" is the company's (`company.timezone`, B-29).
- The CPA package's period and the viewer's "Year to date" preset are
the fiscal year that holds their day (`calendar::fiscal_year_start_on`),
the fact itself, never "what an omitted date means": they don't follow
this default if it ever changes.
- Not covered (no default period): search filters, the activity digest
(its `since` is required; the `books://digest` resource reads this
month so far), the `through` of `books.run_schedules` and
`books.run_recurring` (today: an operation, not a period), and the
viewer's drill-downs from a balance, which run from the books' first
entry because a balance is cumulative. The IIF is never a period (B-34):
it refuses `start` and `end`, so the general journal's default period
never shortens it, nor anything in the CPA package, which gives its
statements and ledgers its own fiscal year to date.
**Enforced:** `ledger_core::calendar` (`period_defaults`,
`PeriodDefaults::statement`, `statement_range`, `as_of`,
`calendar_year`, `fiscal_year_start_on`, `PeriodKind::describe`,
`START_DOC`, `END_DOC`, `AS_OF_DOC`, `YEAR_DOC`); `ledger_package`
`statement_range`, `as_of_date`, `pnl_statement`, `bs_statement`,
`cash_flow_statement`, `build_package`; `ledger-mcp` `BooksServer::periods`,
`statement_range`, `as_of`, every report tool, `books.show`, the
generated descriptions (`described`) and parameter docs; `ledger-gui`
`Books::periods`, `default_period`, `fiscal_ytd`, `App::show`,
`RegisterRange`. **Checked:** `ledger-core` `calendar.rs` unit tests and
`tests/properties.rs` `prop_period_defaults_resolve`; `ledger-mcp`
`server.rs` `period_tools_describe_the_one_default`, `tests/mcp_stdio.rs`
`one_default_period_everywhere_defect_16` and
`the_default_period_never_shortens_the_iif_or_the_package`,
`tests/oracle_mcp.rs`
`show_sends_the_dates_it_names_defect_16`,
`show_refuses_start_after_end_defect_15`,
`show_refuses_a_derived_inverted_range_defect_15`; `ledger-gui`
`tests/show.rs`; bookspec `examples/default-period-fiscal-year-to-date.toml`.
## B-31 Renaming an account changes its label, nothing else
**Status:** DECISION (the owner, `plan/served.md` 4.9 and P3 "Settled in
design", 2026-10-20: "a new account named like a sibling (ignoring
capitals), or holding a nine-digit number, is refused"); B-8 (posted is
forever); AUTHORITY for keeping the old name (Rev. Proc. 98-25 §5.01,
`sources/irs-records.md`: "the relationship (i.e., audit trail): (a)
between the total of the amounts in the taxpayer's machine-sensible
records by account and the account totals in the taxpayer's books");
Intuit IIF (`sources/intuit-iif.md`: "Accounts are identified by NAME")
for refusing a sibling's name.
Entries name accounts by id, so a rename changes no entry, line, hash,
balance or statement amount: every report, past periods included, prints
the new name. The journal keeps the old one (`account.renamed {account_id,
code, name, previous, reason}`), so a report printed under the old name
can be tied to the new. A rename is refused when the account is unknown,
the name is blank, the name (trimmed) is the one it has, another account
under the same parent (top-level accounts are siblings too) has it
ignoring capitals and surrounding space (QuickBooks would merge the two),
it holds a run of nine digits (a full SSN, EIN or account number, B-26:
every report prints a name), or it is not one line of at most 200
characters (`account::MAX_ACCOUNT_NAME`, the bound of the company's own
name: no line break, tab or other control character, which no report, CSV
or IIF line can hold). The name is kept trimmed. The same name rules hold
for a new account (`books.add_account`, `books.import_chart_csv`), which
is journaled with its name trimmed too; accounts the engine
creates for a control role keep their fixed names and are never refused
for one. Approval rules, roles, settings and closed periods name accounts
by code or id, and a name is no posting, so none of them blocks a rename.
Existing charts are untouched: the rules are the commands', never replay's
(B-28).
A rename can still change what the books pick by name (no amount): with
`books.cash_accounts` unset, the cash accounts are the asset accounts
whose names say they hold cash, and the card accounts are the
liabilities named "card" (`ledger_store::reconcilable`); the result says
when the new name moves the account into or out of either. It also lists
conventions whose text names the old name. QuickBooks matches accounts
by name, so a CPA who imported an earlier IIF is told.
**Enforced:** `Ledger::rename_account` / `check_rename_account`,
`check_account_name` (also in `add_account`), `account::MAX_ACCOUNT_NAME`,
`check_rename_reason`,
`replay_rename_account` (only that the account exists),
`ChartOfAccounts::set_name`, `Ledger::add_control_account`;
`Store::rename_account` (journal feature `account_renamed`, declared
before the first rename, B-27); `books.rename_account` (scope
`books:post`, like renumbering). **Checked:** `ledger-core`
`tests/spec_ledger.rs` (`rename_account_*`), `tests/model.rs`
(`Cmd::Rename`); `ledger-store` `tests/store.rs`
(`rename_account_changes_only_the_name_defect_15`,
`prop_rename_changes_no_figure`, `a_journaled_rename_replays_past_the_command_rules`),
`tests/format.rs`; `ledger-mcp` `tests/mcp_stdio.rs`
(`rename_account_end_to_end_defect_15`), `tests/hostile_args.rs`;
`ledger-core` `tests/spec_ledger.rs`
(`a_new_name_is_one_trimmed_line_of_bounded_length`), `ledger-store`
`tests/store.rs` (`a_new_accounts_name_is_journaled_trimmed`); Lean
`Step.record` (a name is outside the model: `docs/verification.md` 13a);
bookspec `examples/rename-changes-no-amount.toml`.
## B-32 Every revenue and expense account maps to a line of the return
**Status:** AUTHORITY for why it matters (IRS Pub. 583,
`sources/irs-records.md`: "Your books must show your gross income, as
well as your deductions and credits"; IRS Pub. 538: "you must maintain
records that will enable you to file a correct return"; Rev. Proc. 98-25
§5.01: the relationship "between the total of the amounts in the
taxpayer's machine-sensible records by account and the taxpayer's
return"); DECISION for warn versus refuse (Pub. 583: "the law does not
require any specific kind of records"; the owner, 2026-10-20: warn by
default, header accounts exempt, the first company set to refuse at the
P3 cutover).
A tax category is the account's link to the return (`ledger_core::tax`,
`sources/irs-tax-lines.md`). An account needs one when it is a revenue or
expense account and not a header (a header takes no postings, so nothing
of its own reaches the return: no entry posts to one, a replayed one
included, and an account's header flag never changes).
- Needs attention and the digest (`uncategorized_accounts`) list every
account that needs a category and has none, with activity or not.
- `policy.require_tax_category`: `warn` (unset) or `refuse`. Under `warn`,
`books.add_account` and `books.import_chart_csv` create the account and
their results say so. Under `refuse`, they refuse it (the file whole),
naming the account and where the categories are listed; both take the
category at creation, so the refusal is always met in one call; and
`books.set_tax_category` won't clear a category an account needs.
- The accounts invoices, bills and payroll create for a control role
(Revenue 4000, General Expenses 6999, Salary & Wage Expense 6100,
Payroll Tax Expense 6110) are never refused, so an invoice is never
refused over a chart label: their results warn, and Needs attention
lists them. The policy is "no uncategorized account made through the
chart tools", not "never any".
- Turning the policy to `refuse` changes no existing account: it applies
to new commands (B-28). Needs attention keeps listing the gaps.
- Every account a tool can require has a category that fits it on every
return: the gain or loss account of a disposal (`form_4797_gain_loss`,
Form 4797), tax-exempt income (`tax_exempt_income`, Schedule K), and
each control role's. A gain or loss on business property is figured on
Form 4797 from the tax basis, and its split (ordinary gain, net section
1231 gain) decides the line, never Schedule C: the tax summary lists it
apart (`TaxSummary::form_4797`), at its book amount, with no tax amount
and in no total, on every return.
**Enforced:** `tax::needs_category`, `account_needs_category`,
`uncategorized_accounts`, `CATEGORIES`; `Ledger::check_tax_category_required`,
`check_tax_category_kept` (commands only, never `set_account_tax`, which
replay calls); `settings::TaxCategoryPolicy`, `Settings::require_tax_category`;
`Store::add_account_with_tax`, `add_account`, `set_account_tax`
(journal feature `require_tax_category`, declared before the first
`settings.set` naming the key); `ledger-mcp` `books.add_account`,
`books.import_chart_csv`, `books.digest`; `ledger-gui` Needs attention
(Info under warn, Todo under refuse). **Checked:** `ledger-core`
`tests/tax.rs`; `ledger-store` `tests/store.rs`
(`prop_tax_policy_refuses_exactly`, `a_journaled_clear_replays_under_refuse`);
`ledger-mcp` `tests/spec_tax_policy.rs`; `ledger-gui` `tests/view_model.rs`
(`needs_attention_flags_every_uncategorized_pl_account_defect_8`);
bookspec `examples/tax-category-required.toml`.
## B-33 A package as of a day shows the books through that day
**Status:** CONVENTION (a report as of a day reports the books on that
@@ -1447,4 +1644,5 @@ appendix: the file line by line and its changes, derived by hand);
`ledger-mcp` `oracle_mcp.rs`
(`iif_declares_accounts_by_role_and_balances_every_transaction`,
`iif_without_as_of_is_as_of_today_and_ties_to_the_trial_balance`),
`mcp_stdio.rs` (`mcp_import_export_over_stdio`).
`mcp_stdio.rs` (`mcp_import_export_over_stdio`,
`the_default_period_never_shortens_the_iif_or_the_package`).
+63
View File
@@ -350,6 +350,65 @@ SHA-256 of the retrieved file (`i1120s.pdf`): `eab655cff9226dbc6d80213350bac0500
> Enter only taxable portfolio interest on this line. Taxable interest is interest from all sources except interest exempt from tax and interest on tax-free covenant bonds.
---
## Gains and losses on business property (Form 4797), and tax-exempt income
Governs `form_4797_gain_loss` and `tax_exempt_income` (added in P3, defect 8, 2026-10-20: under `policy.require_tax_category = refuse` every revenue and expense account needs a category that fits it, and the gain or loss account `books.dispose_asset` takes had none). Each file below was retrieved again on 2026-10-20 and is byte for byte the one quoted above (its SHA-256 is the same); only the passages are new. Captions as above.
**Form 1065 (2025), Schedule K** (Source: <https://www.irs.gov/pub/irs-pdf/f1065.pdf>; Retrieved: 2026-10-20; SHA-256 `0f19f556e12ef53c41ba27e5930b4373103f3abcf64693c4ea686451b2a8f56f`)
> 10 Net section 1231 gain (loss) (attach Form 4797)
>
> 18a Tax-exempt interest income
>
> b Other tax-exempt income
>
> c Nondeductible expenses
**Form 1120-S (2025), Schedule K** (Source: <https://www.irs.gov/pub/irs-pdf/f1120s.pdf>; Retrieved: 2026-10-20; SHA-256 `c4b7cae7f2283a2e3225523ea001a710109fee34aad909461421025b287cd18d`)
> 9 Net section 1231 gain (loss) (attach Form 4797)
>
> 16a Tax-exempt interest income
>
> b Other tax-exempt income
>
> c Nondeductible expenses
**Form 1120 (2025), Schedule K item 9 and Schedule M-1 line 7** (Source: <https://www.irs.gov/pub/irs-pdf/f1120.pdf>; Retrieved: 2026-10-20; SHA-256 `016c7fb4042a565c23a98014b17019af7ddc89e3260dd3f926b84d6688b7be6e`)
> 9 Enter the amount of tax-exempt interest received or accrued during this tax year
>
> 7 Income recorded on books this year not included on this return (itemize): Tax-exempt interest $
**Instructions for Form 1065 (2025), Line 6 and Tax-exempt income, in part** (Source: <https://www.irs.gov/pub/irs-pdf/i1065.pdf>; Retrieved: 2026-10-20; SHA-256 `f4a35a24c7abac486908d3610754397a66b2130f0eb425dda897d78a9d91b786`)
> Line 6. Net Gain (Loss) From Form 4797
>
> Caution: Include only ordinary gains or losses from the sale, exchange, or involuntary conversion of assets used in a trade or business activity.
> Tax-exempt income. Don’t include any tax-exempt income on lines 1a through 8. A partnership that receives any tax-exempt income other than interest, or holds any property or engages in any activity that produces tax-exempt income, reports this income on Schedule K, line 18b, and in box 18 of Schedule K-1 using code B.
>
> Report tax-exempt interest income, including exempt-interest dividends received as a shareholder in a mutual fund or other RIC, on Schedule K, line 18a, and in box 18 of Schedule K-1 using code A.
**Instructions for Form 1120-S (2025), Line 4 and Tax-exempt income, in part** (Source: <https://www.irs.gov/pub/irs-pdf/i1120s.pdf>; Retrieved: 2026-10-20; SHA-256 `eab655cff9226dbc6d80213350bac050009ba90daf621c182d31c541bc62c3f2`)
> Line 4. Net Gain (Loss) From Form 4797
>
> Caution: Include only ordinary gains or losses from the sale, exchange, or involuntary conversion of assets used in a trade or business activity.
> Tax-exempt income. Don’t include any tax-exempt income on lines 1a through 5. A corporation that receives any tax-exempt income other than interest or holds any property or engages in any activity that produces tax-exempt income reports this income on Schedule K, line 16b, and in box 16 of Schedule K-1 using code B.
>
> Report tax-exempt interest income, including exempt-interest dividends received as a shareholder in a mutual fund or other regulated investment company, on Schedule K, line 16a, and in box 16 of Schedule K-1 using code A.
**Instructions for Form 1120 (2025), Line 5, in part** (Source: <https://www.irs.gov/pub/irs-pdf/i1120.pdf>; Retrieved: 2026-10-20; SHA-256 `49476a6f69a38a0fdb96fea1d66ddc936404e9b2eebb249da8e20830b0743358`)
> Report tax-exempt interest on Schedule K, item 9. Also, if required, include the same amount on Schedule M-1, line 7, or Schedule M-3 (Form 1120), Part II, line 13, if applicable.
**Instructions for Schedule C (2025), Other Schedules and Forms You May Have To File, in part** (Source: <https://www.irs.gov/pub/irs-pdf/i1040sc.pdf>; Retrieved: 2026-10-20; SHA-256 `b5536db91498a450572e37078d7d5ee8fdc400e385d4fee3a26c230a33e399b6`)
> • Form 4797 to report sales, exchanges, and involuntary conversions (not from a casualty or theft) of trade or business property.
## Mapping (wanderbooks, derived)
**This section is derived, not quoted.** It is wanderbooks' reading of the 2025 forms and instructions above, written 2026-10-13, for the categories in `crates/ledger-core/src/tax.rs`. It is not tax advice; the CPA decides the return. "—" means the category does not apply to that form (per `TaxCategory::forms`). "Not page 1" means the quoted page-1 lines have no place for it; where it goes instead (for example Schedule K) is not quoted in this file and is left to the CPA.
@@ -359,7 +418,9 @@ SHA-256 of the retrieved file (`i1120s.pdf`): `eab655cff9226dbc6d80213350bac0500
| `gross_receipts` | 1 | 1a | 1a | 1a |
| `returns_allowances` | 2 | 1b | 1b | 1b |
| `interest_income` | 6 for interest on notes and accounts receivable (instructions, line 6); other interest not determined here | Not page 1: Schedule K line 5, portfolio interest (quoted above) | Not page 1: Schedule K line 4, portfolio interest (quoted above) | 5 |
| `form_4797_gain_loss` | Not on Schedule C: Form 4797 (instructions, quoted above) | 6 (the ordinary part, from Form 4797); net section 1231 gain on Schedule K line 10 (quoted above) | 4 (the ordinary part); net section 1231 gain on Schedule K line 9 (quoted above) | 9 |
| `other_income` | 6 | 7 | 5 | 10 |
| `tax_exempt_income` | — | Not page 1: Schedule K line 18a (interest) or 18b (other) (quoted above) | Not page 1: Schedule K line 16a or 16b (quoted above) | Not page 1: Schedule K item 9 and Schedule M-1 line 7, tax-exempt interest (quoted above) |
| `cost_of_goods_sold` | 4 (from Part III, line 42) | 2 | 2 | 2 |
| `advertising` | 8 | 21 (no advertising line) | 16 | 22 |
| `bad_debts` | 27b via Part V (no line in Part II; instructions list bad debts under Part V) | 12 | 10 | 15 |
@@ -395,3 +456,5 @@ Notes on the derivation:
- Form 1065 line 13 "Rent" and Forms 1120-S line 11 / 1120 line 16 "Rents" do not split equipment from property; Schedule C line 20 does.
- Schedule C has no line for bad debts or for organizational and start-up costs; the Part V instructions quoted above put them in Other expenses (line 48 to line 27b).
- Schedule C line 30 (business use of home) and Form 1065 line 20 / 1120-S line 19 / 1120 line 25 (energy efficient commercial buildings) have no wanderbooks category.
- `form_4797_gain_loss` (2026-10-20) is the book gain or loss on business property. Form 4797 figures the tax gain from the tax basis (book and tax depreciation differ), and splits ordinary gain (recapture) from section 1231 gain; the CPA does both, and the split decides the line (the row above). So, unlike `depreciation` (on page 1 of every return, only its amount the CPA's), the tax summary can't place it: it lists it apart (`form_4797`), at its book amount, with no tax amount and outside income and both nets, on every return alike. A Form 4797 gain or loss never goes on Schedule C (the Schedule C instructions send sales of business property to Form 4797).
- `tax_exempt_income` (2026-10-20) is income at 0%: none of it is taxable, so it appears in the summary's book amounts and not in its tax amounts, the income twin of `nondeductible`. It is for the entity returns; a sole proprietor's tax-exempt interest is not business income.