Donation Receipt Generator — FINAL ship/no-ship verification (Fable, 2026-09-25)
Reviewer: Claude Fable 5.1 (independent; did not author the sources, the three reviews or the resolutions document). Nothing under sources/ was edited.
Files verified: sources/donation-receipts/{rules.yaml, test-cases.yaml, receipt-templates.md, open-questions.md, validate.py} against reviews/donation-receipts-2026-09-25-{opus,fable,codex,resolutions}.md.
VERDICT: PASS (minor nits only — none changes a printed legal element, a threshold, or a gate)
1. Resolutions confirmed against the current text
| Item | Where it now lives | Confirmed |
|---|---|---|
| Signature (R-01, DR-008) | CA-11 quotes Reg. 3501(1)(i), (2), (3), (3.1) verbatim; says (3.1) is for "another recipient of the gift" and is not an e-signature route; CRA computer-generated guidance quoted as "should"; F-CA-23 signer name + title required (validator + CA-TC-30); F-CA-24 banner "Not valid until signed by an authorized person" on screen, download control and PDF; "keep a copy of the SIGNED receipt"; no "read-only / flattened / encrypted" claim anywhere (grep of all four files: none). | Yes |
| Registration gate (R-02) | CA-00: attestation text fixed; regex ^[0-9]{9}RR[0-9]{4}$ enforced by validator (REG_RE); List-of-charities link in the refusal; unchecked → CA-20 letter only (CA-TC-28); wrong format → invalid (CA-TC-29). s.188.1(7)/(8)/(9) now verbatim, no longer HYPOTHESIS. US mirror US-00 / F-US-19 / US-TC-31. | Yes |
| Serial Preview / Issue (R-03, DR-006) | CA-03 two states; preview consumes nothing (CA-TC-31: watermark, serials_consumed: 0, log_rows: []); Issue increments numeric suffix (CA-TC-32), asserts batch uniqueness (CA-TC-26), rejects self-replacement (CA-TC-27); CSV log F-CA-26; post-issue message. | Yes |
| Retention (R-04) | CA-14 / F-CA-22 built from the CRA's own sentences; retention_notice_contains asserted in CA-TC-01 and emitted by the validator. | Yes |
| US timing (R-05) | US-08: "when the donor OBTAINS it"; four tiers keyed to Jan 31 / Apr 15 / Oct 15; tests US-TC-01/04 (on_time), 25 (Feb → late_for_early_filers), 17 (extension_only), 26 (too_late); template §2a wording identical to validator TIERS. | Yes |
| Removed token path (C-1) | US-09 is refer-only; rule text lists all three Pub 1771 conditions and says test 2 is on COST; the only path offered is "re-enter as goods provided"; US-TC-15 (refer) + US-TC-29 (re-entered, $92 ceiling, disclosure). Grep for "safe path" / "choose goods_status 'none'": gone. | Yes |
| $250 net test (M-1) | US-01 net_c = amount − GFE; cwa = net >= 25000; US-TC-18 cwa_required: false; US-TC-21 ($350/$100 → CWA) and US-TC-33 ($349.99/$100 → none) bracket the boundary; Pub 526 sentence verbatim. | Yes |
| FMV == P (R-12, DR-001) | 0 < FMV < P in US-04 formula, US-14, schema constraint and validator (if FMV >= P: invalid); US-TC-20 invalid; US-TC-23 covers FMV == 0 with "provided". | Yes |
| No backdating (R-06) | CA-04 / US-08 / US-14: date_issued = clock.today, not editable; forbidden_inputs in schema; validator rejects any date_issued key (CA-TC-37, US-TC-30). The ">= last gift date" phrasing survives only as an ASSERT (gift date > today → invalid, CA-TC-38). | Yes |
Other resolutions spot-checked and present: donor.kind (CA-TC-33), free-text address (CA-TC-34), intermediary (CA-TC-35), CAD-only refer (CA-TC-36), integer-cent formulas with the discriminating case CA-TC-24 ($300.01), exact-10% pair CA-TC-22/23, $0 gifts (CA-TC-25, US-TC-22), canonical schema + shared fixtures, empty satisfies impossible (validator structural check), goods_status required for every gift (US-TC-27), mixed goods + IRB (US-TC-24), ceiling wording "limited to" (US-06, F-US-13), gift-card routing (US-12(d)), postmark date printed (CA-TC-16). The one REFER (m-10 residue: s.248(30)–(32) verbatim, current-year token figures) is honestly carried in open-questions items 1 and 10.
2. Rendered documents (from receipt-templates.md + fixtures + validator output)
CA single receipt (CA-TC-03 inputs). Reg. 3501(1) element map: chapeau statement → title F-CA-01; (a) name + address → F-CA-02/03; (b) 987654321RR0001 → F-CA-04; (c) B-0031 → F-CA-05; (d) "Toronto, ON" → F-CA-06; (e) 2026-05-02 → F-CA-07; (f) 2026-05-04 = clock.today → F-CA-08; (g) "Grace Lim" + address → F-CA-09..13; (h) $200.00 CAD; (h.1) "Dinner at the spring fundraising banquet" / $60.00; (h.2) $140.00; (i) signature line + "Dorothy Kwan, Treasurer" → F-CA-18/23; (j) "Canada Revenue Agency: canada.ca/charities-giving" → F-CA-19. Every element present. Labels match the CRA sample page exactly (re-fetched: "Place issued", "Date gift received", "Date receipt issued", "Donor name", "Amount of advantage to donor", "Description of advantage to donor", "Eligible amount of gift", "Authorized signature", "Name and website of the Canada Revenue Agency"). Validity wording: the banner says "Not valid until signed"; nothing calls the unsigned PDF valid, read-only or encrypted. As a donor: clear. As the CRA: complete once signed.
CA cumulative with advantage (CA-TC-20). "Date gift received: 2026" (permitted by (e) "the year during which"); Amount $1,250.00; Description "Banquet dinner (2026-05-02): $60.00; A pen (2026-09-13): nominal, disregarded"; Amount of advantage $60.00; Eligible $1,190.00; itemized table with per-gift date of donation. Totals reconcile to the three amount lines. Nominal applied per gift (labelled product assumption). All (1) elements present.
US year-end statement (US-TC-08). Org name, optional address/EIN, date 2027-01-20, donor, "Contribution statement for tax year 2026", one row (2026-11-07, $500.00, seat at gala ($150.00)), total, goods line with GFE, disclosure block in body type ending "The amount you may deduct for this payment is limited to $350.00." Pub 1771 elements 1–6 present; no SSN; "USD" labelled; timing tier on_time. Ceiling wording never says "your deductible contribution is". Nothing implies a determination letter or registration beyond the s.170(c) attestation the treasurer checked.
CA thank-you letter (CA-TC-28, unregistered). Title "Thank-you letter — not an official receipt for income tax purposes"; church name/address; date; donor; date/amount rows; the sentence "This letter is not an official donation receipt and cannot be used to claim a charitable tax credit." No title F-CA-01, no Registration No., no serial, no advantage/eligible lines, no CRA line. Nothing implies registration. Read as a donor: unmistakably not a receipt. Read as the CRA: nothing here is a "receipt" for s.188.1(9) purposes.
Template artifacts found: see nits 1–3 and 7 below. None prints a wrong legal element.
3. Primary-source spot verification (8 WebFetch + 2 IRS PDFs extracted locally with pypdf)
| Quote in rules.yaml | Source fetched | Result |
|---|---|---|
| Reg. 3501(1) chapeau, (a)–(j), (2), (3), (3.1), (4), (5), (6) | laws-lois s.3501, current to 2026-09-03 | Verbatim match; (2) "Except as provided in subsection (3) or (3.1), every official receipt shall be signed personally…" exact |
| CRA computer-generated receipts: "should be in a read-only or non-editable format"; "should be encrypted and signed with an electronic signature"; "secure electronic signature should be kept under the control…"; "copies of email-issued receipts must be retained" | canada.ca, modified 2020-02-24 | Verbatim; every signature/format sentence is "should", the retention sentence is "must" — exactly as CA-11/CA-14 state |
| CRA "What information must be on an official donation receipt": statement, "a unique serial number", signature, "canada.ca/charities-giving" | canada.ca, modified 2018-11-02 | Verbatim ("Issuing receipts" page family) |
| CRA Split receipting: "not more than $75 or 10% … whichever is less"; "does not have to subtract"; $6 mug + $2 pen; near-cash exclusion; "more than 80%" | canada.ca, modified 2018-05-22 | Verbatim |
| CRA Books and records: two years / six years / Canadian address / electronically readable / backup | canada.ca, modified 2025-09-08 | Verbatim, all five |
| CRA Sample receipts: "Only the following organizations have registration numbers…"; 80% sentence; all labels | canada.ca, modified 2021-12-01 | Verbatim; label set matches the template |
| ITA s.188.1(7) 5%, (8) 10% within five years, (9) 125% "false statement … on a receipt" | laws-lois, current to 2026-09-03 | Verbatim |
| §170(f)(8)(A)–(C), (f)(12)(C) 30 days, (f)(17) | law.cornell.edu | Verbatim, incl. "obtains the acknowledgment on or before the earlier of" |
| Pub 1771 (Rev. 11-2023): "There are no IRS forms… Letters, postcards, or computer-generated forms… are acceptable"; token exception three conditions, $125 / $62.50 / $12.50 (2023, inflation-adjusted), COST; "no donative element"; "may not exceed $60"; "no later than January 31"; "gift card redeemable for cash"; SSN sentence; "$75 or less" membership; "need only mention the $20 value of the poster"; "contribution that is valued at $250 or more"; "incurs no penalty" | irs.gov PDF, text extracted | All verbatim. Word "signat*" occurs 0 times in Pub 1771 — US-13's "says nothing about a signature" is literally true |
| Pub 526 (2025, 5-Feb-2026 build): "your contribution is the amount of the payment that is more than the value of the goods and services"; "lists each contribution and the date of each contribution and shows your total contributions"; "don't combine separate contributions"; "You must get it on or before the earlier of: a. The date you file your return…; or b. The due date, including extensions"; "If the acknowledgment doesn't show the date…" | irs.gov PDF, text extracted (hyphenation normalised) | All verbatim |
Not re-fetched this pass (out of budget; carried honestly as open in open-questions.md): ITA s.248(30)–(32), Reg. 1.170A-13(f)(2) verbatim, s.6714, current-year token figures, Folio S7-F1-C1, P113.
4. Validator
$ python validate.py
rules: 38 ({'computed': 28, 'refer': 7, 'product': 3}); fields: 47; test cases: 73 {...}
RESULT: PASS (0 problem(s)) exit 0
$ python validate.py --selftest
FAIL CA-TC-01
- eligible_amount: got 10000 expected 9999
RESULT: FAIL (1 problem(s)) exit 1
The self-test genuinely corrupts an expectation: cases[0]["expected_output"]["eligible_amount"] = 99.99 on a deep copy of CA-TC-01, and the engine's recomputed 10000 cents is compared against 9999 — a real mismatch caught by the real comparison path, not a hard-coded failure. Exit codes are 0 / 1 as required. I also read the engine: thresholds are integer-cent (A <= 7500 and 10*A <= G, 5*A > 4*G, net >= 25000, P > 7500 and FMV > 0), money is Decimal(str(x))*100 with a whole-cent assertion, dates use date.fromisoformat, and the required-field audit runs on every document-generating case.
5. Nits (minor; fix in the next editorial pass, none blocks shipping)
- Template §2 goods line drifts from the rule and tests. F-US-11 reads "In exchange for your contribution of ${P} on {date}, we gave you…"; US-04 rule text and every expected
goods_line(US-TC-05/06/08/21/24/29) read "In exchange for your contribution, we gave you…". Pick one (the template's longer form is better for multi-gift statements) and align rule + tests. - Thank-you letter prints the signer while CA-20 says no signature block. §1b ends "With gratitude, {signer.name}, {signer.title}" — that is F-CA-23's content, yet CA-20 and CA-TC-28 list F-CA-23 absent. Also the validator's attestation branch runs before the signer check, so a blank signer would render ", ". Either drop the sign-off or label it an ordinary letter closing (not F-CA-23) and guard blanks.
- §0 bullet 3 is self-contradictory as written: it says the word "official" never appears on the letter path, but the letter's title and its mandatory sentence both contain "not an official …". Say "never appear except in the negation".
- CA-18 "excluded and listed separately as 'not receiptable'" is specified but neither templated nor tested. The validator sets
excluded = Trueand emits nothing for it; the template has no such row. Either define the row (and add a cumulative test with one gift failing the 80% test) or refer the whole cumulative receipt when any gift fails CA-10. Legal totals are unaffected either way. - Validator skips the message comparison when the engine returns
message: None(CA-TC-10/11/12/17, US-TC-10/11/12/14 — eight refer messages are UI copy the checker does not verify), andfields_present/fields_absentare checked only for id existence, not computed. The resolutions document's "including refusal messages" is therefore slightly generous. No legal element depends on these. - Banner wording after signing. "Not valid until signed by an authorized person" stays printed on the PDF after the treasurer signs it, so the donor's signed receipt still says "not valid until…". Consider "Valid only when signed by an authorized person", which reads truthfully both before and after.
- Middle initial: template prints "{M.}" (with period); validator/tests print "Ruth A Vanderberg" (no period). Pick one.
- CA-03 serial fallback for a
serial_startwith no numeric suffix ({start}-{i+1}) exists only in the validator, not in the rule text. - Cross-reference drift: CA-03 notes cite "open-questions Q17" for the browser-local reminder; that item is now 19 (17 is the IRS filing-season note). The resolutions table already says Q19.
6. What I did not find
- No wording anywhere calls the unsigned PDF valid, read-only, flattened, encrypted or "official" on the unregistered path.
- No wording implies CRA registration or an IRS determination beyond the treasurer's own attestation.
- No threshold, formula, gate, or printed legal element disagrees between rules.yaml, test-cases.yaml, receipt-templates.md and validate.py (the only textual drift is nit 1, a sentence pattern).
- No Reg. 3501(1) element is missing from the CA receipt template; no Pub 1771 element is missing from the US template.