P3: the IIF in plain ASCII with every change listed, and the CPA package as of one day (defects 7, 21)

The CPA package's general-journal.iif now holds every posted entry through
as_of (closing entries left out, D-36), written as plain ASCII with CR LF,
DOCNUM the entry id, QuickBooks Desktop's field limits and subaccount types
honoured, and every character written differently listed in the export's
warnings and the README (grouped by substitution, TINs masked). The 1099
worksheet stops at as_of too. PDFs print CJK, Arabic, Hebrew, Thai and
Devanagari (Noto fallbacks, OFL), and so does the viewer. has_full_tin reads
Unicode separators.

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 09:35:32 -06:00
1 parent 1e8f9275b9
commit 2c29d48ab9
48 files changed
+5661 -317

No files matched your search

+195
View File
@@ -700,6 +700,201 @@ fields; `Currency::allocation_places`. P3's accept counts `ledger-core` and
`ledger-import` (R-6); the store and MCP mutants above are for the whole
pass.
## P3: the IIF and as-of (2026-10-20)
Defects 7 and 21 (plan P3; the owner's decisions D-36). Rules
`docs/spec/rules.md` B-33 (a package as of a day) and B-34 (the IIF). No
mutation run (D-33): what P12 must cover is at the end.
**Named regression tests**, each from its reproduction, on anonymized
fixtures:
- Defect 7, DOCNUM: `ledger-import` `tests/spec_import.rs`
`iif_docnum_is_the_entry_id` (a draft never posted, three entries
posted out of id and date order: DOCNUM 3, 2, 4 on the TRNS and every
SPL; the file had written 1, 2, 3).
- Defect 7, the characters (D-36, plain ASCII):
`iif_writes_plain_ascii_and_lists_every_change` (accented letters,
Chinese, emoji, a fullwidth colon and fullwidth digits, a tab in a
name, controls, a decomposed accent, private use: each written form
exact, every change listed exactly), `iif_every_line_ends_cr_lf`,
`iif_change_warnings_are_one_line_each`,
`iif_keeps_a_tin_readable_to_the_package_scan` (an en-dash or
no-break-space TIN in a memo stays a TIN to the package's TIN scan),
`iif_distinct_accounts_never_share_a_name` (review R2: duplicate names,
by case, siblings, a fullwidth colon against a hyphen, two private-use
names; a name that is another's tagged form tagged in turn; the clash
left when two codes are written alike refused),
`iif_fits_quickbooks_desktop_limits` (Intuit's limits, found in the P3
review: a level past 31 characters cut, two alike once cut tagged, a
subaccount under its cut parent; an account number past 7 characters
and two written alike left out of ACCNUM; a memo past 4,095 cut; every
line of the warnings exact; a hand-built level past 31 refused),
`iif_warnings_group_the_memos_by_what_is_written_differently` (the
lead's call: 100 depreciation memos with an em dash make one line),
`iif_warnings_never_hold_a_tin` (a TIN in a memo or an account name,
in ASCII or fullwidth digits, never in a warning),
`iif_field_transliteration_exact` and `iif_paths_and_transliteration`
(the old expectations, "Caf? meals" among them, rewritten to the rule).
- Defect 21, the package's IIF: `ledger-package` `tests/package.rs`
`as_of_package_iif_stops_at_as_of` (next year's rent was in the file;
now the four entries of the year, by id, tied to the trial balance),
`the_iif_through_as_of_at_its_edges` (an entry on as_of in, the day
after out, a reversal after as_of leaving its original, as of a day
before the first entry: the accounts and no transaction).
- Defect 21, every file: `every_file_in_an_as_of_package_stops_at_as_of`
(review R4: books the same through as_of, one set with a 1099 payment,
its receipt and an asset bought the day after, a reversal of the entry
dated as_of, the members' contributions and an earlier desk sold, build
the same package byte for byte, every file and which files there are,
PDFs included, with and without TINs and receipts, with and without a
fixed asset register, mid-year and at year end; a package days later
sees the activity). Undoing any one of the as-of
bounds (the IIF's entries, its FIXASSET typing, the 1099 worksheet's
`through`, the fixed asset register's presence) fails it; checked by
hand.
- D-36, the 1099 worksheet: `the_1099_worksheet_stops_at_as_of` (a
payment on as_of counts, the day after doesn't; a vendor under the
threshold so far is not yet reportable; a year-end package and the
tools' worksheet unchanged).
- D-36, closes: `iif_leaves_out_period_closes` (two months closed: no
close written, income and expenses equal the year's profit and loss,
balances the trial balance without the closes).
- Plan P3's accept, a bookspec example per regression test: the as-of
ones (`bookspec/examples/as-of-package-stops-at-as-of.toml`,
`1099-worksheet-through-as-of.toml`) and the IIF's
(`iif-quickbooks-journal-file.toml`, formats appendix), each expected
value derived by hand; `the_bookspec_iif_example_matches_the_engine`
reads the IIF example and checks the engine gives exactly its file and
changes.
- Review R18 and the README: `one_list_of_cash_accounts_per_package`,
`the_readme_lists_what_the_iif_writes_differently` (the memos grouped:
six depreciation memos and a lunch with an em dash, one line).
**Exhaustive and property tests.** `iif.rs`'s unit tests write every
one of the 1,112,064 Unicode scalar values (alone, after a letter, before
one) and check the field is printable ASCII without a double quote and a
fixed point, its changed runs empty exactly when it is unchanged, and a
level never empty, colon-free, trimmed and at most 31 characters (a
complete check over the finite alphabet; review R15); `with_tag`'s and
`changed_runs`' edges by hand; `the_transliteration_table_is_pinned`
samples deunicode 1.6.0, pinned exactly in `Cargo.toml` (R14).
Properties: `iif_round_trips_random_books` (memos mixing ASCII, Latin-1,
dashes and quotes, every kind of line break, controls, CJK, emoji,
combining marks, private use; DOCNUM, CR LF, the changes list exact),
`iif_names_never_clash_and_every_change_is_listed` (random charts from a
pool of clashing names, long names alike once cut, codes with colons,
past 7 characters, and one alike another once trimmed; every warning
line checked against the books and the file), `iif_ties_to_the_trial_balance_as_of` (2,000
cases: random books with reversals and closed months, random as_of; the
file's entries and balances against the trial balance without the
closes).
**End to end.** `ledger-mcp` `oracle_mcp.rs`:
`iif_declares_accounts_by_role_and_balances_every_transaction` (as_of,
DOCNUM the ids, the warnings exact: the fixture's line-broken memo and
the engine's own "Depreciation — Laptop — 2025-03" memos, the alias, a
range refused), `iif_without_as_of_is_as_of_today_and_ties_to_the_trial_balance`
(review R5), `cpa_package_holds_the_year_and_its_evidence` (the IIF from
the prior year's sale through as_of; a payment after as_of to the
contractor left out of the 1099 worksheet; the note's count);
`mcp_stdio.rs` (a Chinese account name over stdio: ASCII text, CR LF,
`warnings`); `spec_mcp.rs` (iif with as_of); `ledger-gui`
`tests/exports.rs` (the README's IIF line names the as_of).
**Names in every script, in the PDFs and the viewer.** Inter has no
Chinese, Japanese, Korean, Arabic, Hebrew, Thai or Devanagari glyphs:
every such character printed as Inter's empty box (glyph 0), and the
viewer drew boxes too. `ledger-import` `src/fonts.rs` now bundles the
fallbacks, each once: a subset of Noto Sans CJK SC
(`fonts/NotoSansCJK-LICENSE.txt` says how it was made) and Noto Sans
Arabic UI, Hebrew, Thai and Devanagari, unmodified
(`fonts/NotoSans-LICENSE.txt`: versions, upstream commits, SHA-256; 412,968
bytes for the four). Arabic is the UI form because Noto Sans Arabic 2.013
draws a letter's dots as marks of their own, which Typst writes as text
runs of their own: its PDFs' text read "شلام" for "سلام", with letters
doubled. No emoji font (about 10 MB): an emoji prints as an empty box in
the PDFs. Tests: `fonts.rs`
`every_template_names_every_fallback_in_order`; `pdf.rs`
`chinese_japanese_and_korean_print_as_written` and
`arabic_hebrew_thai_and_devanagari_print_as_written` (every glyph from
its script's font, none glyph 0); `spec_import.rs`
`pdf_keeps_chinese_japanese_and_korean_names` (the ToUnicode map) and
`pdf_prints_every_bundled_script_as_written` (the PDF read back with
hayro, the viewer's own reader: Arabic and Hebrew read right to left give
the words, the lam-alef ligature included; Thai, Devanagari and Chinese
left to right; every font embedded; failed with Noto Sans Arabic 2.013).
The viewer registers the same `FALLBACKS` after its own fonts in every
family (`ledger-gui` `theme.rs` `every_family_falls_back_to_the_noto_fonts`:
the very bytes, by pointer, and a real glyph for each script's letters in
every family) and `tests/scripts.rs`
`account_names_in_other_scripts_show_as_written` (the Accounts page paints
each name; with `SHOT_DIR` set, it saves `scripts.png`: looked at,
2026-10-20). egui shapes and joins each word, right to left for Arabic
and Hebrew, but doesn't reorder the words of a right-to-left phrase:
"مكتب الرياض" shows its two words left to right.
**TINs in any script (B-26).** `ledger_core::parties::has_full_tin`
missed "123-45-6789": the fullwidth hyphen-minus, the ideographic
and thin spaces, the Hebrew maqaf and the other Unicode dashes weren't
separators, and a zero-width space or a right-to-left mark between digits
ended a run. Past ASCII every Zs space, every Dash character and the
compatibility forms of the dot, slash and underscore now separate, and
default-ignorable characters don't count (`docs/spec/sources/unicode-tin-text.md`,
Unicode 17.0); ASCII reads as before. `spec_parties.rs`
`has_full_tin_reads_every_script_and_separator` and the property
`has_full_tin_keeps_its_ascii_reading` (2,000 cases: on ASCII the same
answer as the old reading, kept in the test; elsewhere it finds at least
what the old one did).
**Second review (2026-10-20).** QuickBooks gives a subaccount its
parent's type (Intuit): `iif_a_subaccount_of_another_type_stands_at_the_top_level`
(a bank account under a header that is no bank, an asset's cost under
"Fixed assets": each written at the top level, keeping its type, listed,
its own subaccount with it, a name it then shares told apart by code);
`check_iif` now checks every subaccount's type is its parent's and every
parent is declared before it; `iif_declares_each_parent_before_its_subaccounts`.
The file names no customer or vendor (Intuit states no need for an IIF
journal; QuickBooks Desktop asks for one on A/R or A/P):
`the_readme_names_the_entries_on_receivables_and_payables`. A memo line
whose digits are masked says so (`iif_warnings_never_hold_a_tin`, which
now also holds a name and a memo that are a TIN only once written, digits
a tab apart: with `has_full_tin` reading every script, nothing else
tested the masks' second pass). `iif_fits_quickbooks_desktop_limits` now
holds a number of exactly 7 characters (kept) and two alike but for case
(left out): undoing either bound of the 7 had failed no test. Each was
checked by undoing it and seeing its test fail.
The GnuCash differential (`oracle/`) compares the engine's figures and
has no IIF and no package, so it has nothing to re-adjudicate here.
Each of the review's fixes was checked by undoing it by hand and seeing
its test fail (2026-10-20): the 31-character cut, the clash loop's
second round, the double quote, the TIN mask, the memo cut, each reason
a number is left out, the memo grouping, the tag's colon; and, for the
builder's, the as_of `<=`, the register's presence, the 1099 `through`,
the closes filter, the FIXASSET date filter and the package's cash list.
**For P12:** mutation over `ledger-import` `iif.rs` (each `breaks` arm,
the control, quote and `None` arms of `each_piece`, the deunicode
post-mapping, `segment`'s colon, trim, cut and "?" fallback, `tag`,
`with_tag`'s room, the clash loop's depth bound, rounds and tag,
`journal_to_iif`'s clash and length refusals, the number rules, the memo
cut, each `changes` comparison and its order, `warnings`' grouping,
order and masks, `shown`, DOCNUM, `EOL`), `pdf.rs` (the fallback font),
`ledger-package` `iif.rs` (`<=` in the as_of filter, the closes filter,
the FIXASSET date filter) and `package.rs` (the cash and financing
resolution, the register's presence, the 1099 `through`, the README's
IIF line, closes sentence, warnings block and 1099 wording), and
`ledger-core` `form1099.rs` (the `through` bound), `ledger-mcp`
`server.rs` (the iif report, range and as_of checks, `warnings`, the
package note), `ledger-gui` `menu.rs` (the note). From the second
review: `iif.rs` `iif_accounts`' type filter on parents and
`apart_from`, `parents_first`, the `AtTopLevel` change, the TIN note's
condition and `receivable_payable`; `fonts.rs` and `theme.rs`
`font_definitions`; the README's A/R and A/P sentence; `ledger-core`
`parties.rs` `tin_separator`'s arms and `tin_invisible`, and the gap that
doesn't count them.
## Benchmarks (baseline 2026-10-13)
No budget, by decision (the owner, 2026-10-13: the books do little work