fix(core,store): child accounts share their parent's type; periods nest; lines checked when added

Three spec decisions from Stephen (2026-10-13), each test-first:

- A child account has its parent's type (ParentTypeMismatch): a child
  subdivides its parent. Test: a_child_account_has_its_parents_type.
- Periods nest (a month in a quarter in a year) but never partially
  overlap (PeriodsOverlap; a refusal uses no id). Test:
  periods_nest_but_never_partially_overlap.
- A line's account must exist and take postings when the line is added,
  not only at post (UnknownAccount / PlaceholderAccount from add_line
  and add_line_fx); posting still re-checks. Two older tests that pinned
  the post-time refusal now check both (the post-time check is reached
  through the unchecked replay path, as old journals can).

Compatibility: replay reads history through new Ledger::replay_*
commands (add_account, add_line, add_line_fx, create_period) that skip
checks added later, so books written before these rules still open.
The contract test books_written_under_older_rules_still_open writes such
a journal directly and found, on its first run, that the SQLite
cross-check rebuild used the checked commands and would have failed
verification ("sqlite projection can't be read back"); it now replays
too. Documented in docs/spec/README.md; decisions recorded in rules.md
(B-1, B-4, B-12).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-13 14:16:51 -06:00
1 parent df38dcccf9
commit 925c3ba523
7 files changed
+362 -12

No files matched your search

+8
View File
@@ -31,6 +31,14 @@ SOURCE: CONVENTION (QuickBooks aging); nearest authority ASC 326-20-55-38 (illus
A rule whose only source is CONVENTION or DECISION is a candidate for a
human to confirm or overturn; nothing else is.
## Rules added later never lock out older books
When a rule is tightened (a check added to a command), books written
before it must still open. Journal replay and the SQLite cross-check read
history through the engine's `replay_*` commands, which skip checks added
later; new commands go through the checked ones. Pinned by
`books_written_under_older_rules_still_open` (ledger-store).
## Vendoring rules
- Public-domain texts are copied **verbatim** (whitespace normalized), each
+11 -3
View File
@@ -27,7 +27,10 @@ Spec blocks in the code cite these as `SOURCE: rules.md B-n`.
total credits after you post the journal entries to the ledger accounts."
**Enforced:** `kernel::check_lines` via `JournalEntry::validate` (two or
more lines, debits = credits exactly). **Checked:** Kani
more lines, debits = credits exactly). A line's account must exist and
take postings when the line is added, not only at post: DECISION
(Stephen, 2026-10-13), `Ledger::add_line`
(`a_line_on_an_unknown_or_placeholder_account_is_refused_when_added`). **Checked:** Kani
`check_lines_is_exact`, `accepted_entry_nets_to_zero`; Lean
`Accepted.net_zero`; property `prop_posted_entries_balance`.
@@ -74,7 +77,9 @@ its liabilities … from delivering or producing goods, rendering services,
or carrying out other activities." E81 expenses "are outflows or other
using up of assets … or incurrences of its liabilities …".
**Enforced:** `AccountType` (five variants). **Checked:** by type.
**Enforced:** `AccountType` (five variants). A child account has its
parent's type: DECISION (Stephen, 2026-10-13), `Ledger::add_account`
(`ParentTypeMismatch`). **Checked:** `a_child_account_has_its_parents_type`.
## B-5 Normal balances
@@ -178,7 +183,10 @@ the entity
Periods themselves: IRS Pub. 538 (`sources/irs-periods-and-methods.md`)
defines the tax year, calendar and fiscal. Locking a closed period and
closing in order are practice (and follow from B-8: a closed period's
book is not rewritten). **Enforced:** `closed_period_containing`,
book is not rewritten). Periods nest (a month in a quarter in a year)
and never partially overlap: DECISION (Stephen, 2026-10-13),
`Ledger::create_period` (`PeriodsOverlap`;
`periods_nest_but_never_partially_overlap`). **Enforced:** `closed_period_containing`,
`CloseOutOfOrder`, `Store::rehearse`. **Checked:** Lean `Steps.frozen`;
store property `refused_commands_never_reach_the_journal` (no draft in a
closed period).