Donation Receipt Generator rules: resolution of the 2026-09-25 adversarial reviews
- Editor: Claude Fable 5.1 (rules editor; did not author any of the three reviews)
- Date: 2026-09-25
- Reviews resolved:
donation-receipts-2026-09-25-opus.md: 13 findings (C-1, M-1, M-2, m-1..m-10)donation-receipts-2026-09-25-fable.md: 20 findings (R-01..R-20)donation-receipts-2026-09-25-codex.md: 13 findings (DR-001..DR-013)
- Files rewritten:
sources/donation-receipts/rules.yaml(38 rules: 28 computed, 7 refer, 3 product; canonicalschema; sharedfixtures; 47 fields),test-cases.yaml(73 cases: CA 20 computed / 9 refer / 11 invalid; US 18 / 5 / 10),receipt-templates.md,open-questions.md. New:validate.py(spec checker, not app code).
Totals: 45 FIXED · 0 REJECTED · 1 REFER (46 findings).
Primary sources opened in this pass (24 WebFetch + Chrome + local PDF extraction)
| Source | URL | What it established |
|---|---|---|
| Income Tax Regulations s.3501 (current to 2026-09-03) | https://laws-lois.justice.gc.ca/eng/regulations/C.R.C.,_c._945/section-3501.html | Verbatim chapeau, (1)(i), (2), (3), (3.1), (4), (5), (6). (3.1) = "another recipient of the gift" only. |
| CRA — Computer-generated receipts (2020-02-24) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/issuing-receipts/computer-generated-receipts.html | Every signature/read-only/encryption sentence is "should"; "copies of email-issued receipts must be retained"; non-erasable media, off-site copies. |
| CRA — Books and records (2025-09-08) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/books-records.html | Two years / six years / Canadian address / electronically readable / backup copy — verbatim for the retention notice. |
| CRA — Correcting or replacing receipts (2017-06-22) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/issuing-receipts/correcting-replacing-official-donation-receipts.html | Full page: "lost or contained incorrect information"; replacement contents; unsent receipts "keep both copies ... cancelled". |
| CRA — Split receipting (2018-05-22) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/issuing-receipts/split-receipting.html | "not more than $75 or 10% ... whichever is less"; "does not have to subtract"; mug + pen example; near-cash exclusion; 80% rule. |
| CRA — Sample official donation receipts (2021-12-01) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/sample-official-donation-receipts.html | "Only the following organizations have registration numbers..."; field labels; title; "more than 80% of the cash gift, you cannot issue". Correct URL (the /issuing-receipts/ one is a 404). |
| CRA — What you need to know to issue a receipt (2018-11-02) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/issuing-receipts/what-you-need-know-issue-official-donation-receipt.html | True donor / intermediaries; postmark + "keep the stamped envelope"; joint accounts; anonymous box. |
| CRA — Registered charity vs non-profit (2025-09-02) | https://www.canada.ca/en/revenue-agency/services/charities-giving/giving-charity-information-donors/about-registered-charities/what-difference-between-a-registered-charity-a-non-profit-organization.html | NPO: "Cannot issue official donation receipts"; "No registration number". |
| CRA — Find another type of qualified donee (2025-10-24) | https://www.canada.ca/en/revenue-agency/services/charities-giving/list-charities/list-charities-other-qualified-donees.html | "Only Canadian registered charities and other qualified donees can issue official donation receipts..."; search-tool URL. |
| CRA List of charities — basic + advanced search (Chrome; JS-rendered; version 2026-07-31) | https://apps.cra-arc.gc.ca/ebci/hacc/srch/pub/dsplyBscSrch?request_locale=en · https://apps.cra-arc.gc.ca/ebci/hacc/srch/pub/dsplyAdvncdSrch?request_locale=en | Public can "confirm whether a Canadian charity ... is registered, revoked, annulled, penalized, or suspended". BN/Registration number = "Business number, 9 digits" + "BN Program Account Type, 2 letters" + "bn Program Account Number, 4 digits". |
| CRA — When should a charity issue a receipt (Chrome) | https://www.canada.ca/en/revenue-agency/services/charities-giving/charities/operating-a-registered-charity/issuing-receipts/when-should-a-charity-issue-a-receipt.html | February 28 suggestion; cumulative receipts for cash; separate receipt per non-cash gift — verbatim. |
| ITA s.188.1 (current to 2026-09-03) | https://laws-lois.justice.gc.ca/eng/acts/I-3.3/section-188.1.html | (7) 5%, (8) 10%, (9) 125% penalties — verbatim (was HYPOTHESIS in R-02). |
| Income Tax Folio S7-F1-C1 (2020-10-09) | https://www.canada.ca/en/revenue-agency/services/tax/technical-information/income-tax/income-tax-folios-index/series-7-charities-non-profit-organizations/series/income-tax-folio-s7-f1-c1-split-receipting-deemed-fair-market-value.html | ¶1.5, 1.9, 1.12, 1.13 quoted; singular "the gift"; no aggregation guidance. |
| 26 U.S.C. §170 | https://www.law.cornell.edu/uscode/text/26/170 | (f)(8)(A)-(C), (f)(12)(C) 30 days, (f)(17). |
| 26 CFR §1.170A-13(f) | https://www.law.cornell.edu/cfr/text/26/1.170A-13 | (f)(1), (f)(2) structure (goods other than IRB + IRB statement), (f)(3), (f)(8)(i) — summary, not verbatim. |
| IRS — Written acknowledgments (28-Jun-2026) | https://www.irs.gov/charities-non-profits/charitable-organizations/charitable-contributions-written-acknowledgments | Contents list; no form and no signature requirement stated. |
| IRS Pub 526 (2025; Feb 5, 2026 build) — PDF text extracted | https://www.irs.gov/pub/irs-pdf/p526.pdf | "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"; "earlier of" timing. |
| IRS Pub 1771 (Rev. 11-2023) — PDF text extracted | https://www.irs.gov/pub/irs-pdf/p1771.pdf | "There are no IRS forms ... Letters, postcards, or computer-generated forms ... are acceptable"; no signature sentence anywhere; token exception three conditions; $75 membership + $20 poster; "no donative element"; January 31. |
| IRS IR-2025-08 (Jan. 10, 2025) | https://www.irs.gov/newsroom/irs-announces-jan-27-start-to-2025-tax-filing-season-agency-continues-historic-improvements-to-expand-enhance-tools-and-filing-options-to-help-taxpayers | "the nation's 2025 tax season will start on Monday, Jan. 27, 2025" (late-January filing warning). |
| 404 / unreachable | CRA general BN page; T4033 (no format text); T4063; CRA search "Search tips" (HTTP 500); CRA receipt checklist | No prose page stating "RR"; structure taken from the search tool instead. |
Mechanical check (sources/donation-receipts/validate.py)
$ python validate.py
rules: 38 ({'computed': 28, 'refer': 7, 'product': 3}); fields: 47; test cases: 73 {'CA': {'computed': 20, 'refer': 9, 'invalid': 11}, 'US': {'computed': 18, 'refer': 5, 'invalid': 10}}
RESULT: PASS (0 problem(s))
$ python validate.py --selftest # positive control: one expectation deliberately corrupted
FAIL CA-TC-01
- eligible_amount: got 10000 expected 9999
RESULT: FAIL (1 problem(s))
What it proves: both YAML files parse; every base fixture, rule id, satisfies id and F-* id resolves; every case is deep-merged onto the shared fixture and recomputed in integer cents from the rules' formulas (A <= 7500 and 10A <= G; 5A > 4G; P − FMV >= 25000; P > 7500), including refusal messages and log rows; every document-generating case has every required field non-blank; and the checker fails when an expectation is wrong.
Resolutions: Opus review
| Finding | Resolution | What changed / why | Source |
|---|---|---|---|
| C-1 (critical) — US-09 "safe path (b)" omits two of three token conditions | FIXED | Path (b) deleted. US-09 lists all three conditions (fundraising campaign; donor told the deductible amount; dollar test — test 2 on COST), states the tool cannot check (1)-(2), and offers only "re-enter as goods provided". goods_status: token_item → refer (US-TC-15); the re-entered gift is US-TC-29 ($92 ceiling, disclosure). | Pub 1771 p.4 (PDF text) |
| M-1 (major) — $250 test on NET contribution; US-TC-18 wrong; Q12 answerable | FIXED | US-01 formula cwa_required = (P − FMV) >= 25000, Pub 526 quoted verbatim. US-TC-18: cwa_required: false, disclosure still true. US-TC-21 added ($350/$100 → net $250 → CWA); US-TC-33 ($349.99/$100 → $249.99 → no CWA). Q12 closed. | Pub 526 (2025) "Contributions of $250 or More" |
| M-2 (major) — CA-TC-19 cannot discriminate; float formulas | FIXED | Header + CA-09/CA-10 formulas rewritten as integer-cent comparisons with no division. CA-TC-24 added: G=333.35, A=33.34 → not nominal → $300.01 (a half-up-rounded cap gives $333.35). CA-TC-19 kept, relabelled non-discriminating. | CRA Split receipting |
| m-1 — Q5 lost receipts answered | FIXED | CA-12 excerpt now quotes "issued a receipt that was lost or contained incorrect information"; Q5 RESOLVED. | CRA Correcting/replacing |
| m-2 — Q2 partly answered | FIXED | CA-09 quotes "does not have to subtract these advantages" and the mug + pen example; Q2 labelled PRODUCT ASSUMPTION for the $0.00 printing convention. | CRA Split receipting |
| m-3 — no gate for non-individual donors | FIXED | donor.kind ∈ {individual, organization}; organization requires legal name + address only; CA-TC-33. Header scope widened. | Reg. 3501(1)(g) "in the case of an individual" |
| m-4 — (3.1)/(1.1) never distinguished | FIXED | CA-11 rule text and excerpt say (3.1) is for "another recipient of the gift" and is not an e-signature route. | Reg. 3501(3.1) verbatim |
| m-5 — disclosure overstates the deduction | FIXED | US-06 text and F-US-13: "The amount you may deduct for this payment is limited to $[P − FMV]." (deductible_ceiling in schema). | Pub 1771 p.7 "may not exceed $60" |
| m-6 — US-07 attribution; Pub 526 stronger support | FIXED | US-02 and US-07 quote Pub 526 "lists each contribution and the date of each contribution" and "If the acknowledgment doesn't show the date..."; s.170(f)(17) described as the donor-side condition; Q15 RESOLVED. | Pub 526 (2025) |
| m-7 — refer table misroutes US gift cards | FIXED | Refer table US column → US-12(d): "redeemable for cash = monetary (Pub 1771); otherwise property — accountant". US-10 note added. | Pub 1771 p.2 |
| m-8 — mixed IRB + goods unhandled (unverified) | FIXED (via R-17's alternative, not "refer") | Fourth goods_status value provided_plus_intangible_religious prints the goods line AND the IRB statement (US-02(7), US-TC-24). Reg. 1.170A-13(f)(2) fetched as a summary only → confidence medium, open-questions item 16 asks for verbatim verification before go-live. "Contradictory" error removed. | Reg. 1.170A-13(f)(2) (summary); Pub 1771 p.5 poster example |
| m-9 — ambiguous refusal text; grammar | FIXED | CA-10 message: "The advantage ($81.00) is more than 80% of the $100.00 gift (80% of $100.00 = $80.00)." (computed by the validator, CA-TC-08/21). Goods descriptions carry their own article ("a harvest dinner"). | CRA sample receipts |
| m-10 — unverified items | REFER (partly closed) | Verified this pass: CA-01 title (sample page lists it); Feb 28 tip (page read in Chrome); Q8 labelled product assumption with folio quotes. Still open: ITA s.248(30)–(32) verbatim (Q1); current-year token figures (Q10). | CRA sample receipts; CRA When should a charity issue a receipt; Folio S7-F1-C1 |
Resolutions: Fable review
| Finding | Resolution | What changed / why | Source |
|---|---|---|---|
| R-01 (critical) — typed name is not a signature; no "read-only" claim | FIXED | CA-11 rewritten: Reg. 3501(2)/(3)/(3.1) verbatim; CRA guidance quoted as "should"; signer name + title required (F-CA-23, CA-TC-30); banner "Not valid until signed by an authorized person" (F-CA-24) on screen, on the download control and on the PDF; "keep a copy of the SIGNED receipt" instruction; all "flattened / read-only" claims deleted (CA-01 note). US: Pub 1771 quoted — no form, no signature requirement; F-US-15 optional. | Reg. 3501; CRA computer-generated receipts; Pub 1771 p.3 |
| R-02 (critical) — no registered-charity gate | FIXED | New CA-00: attestation "Our church is a registered charity with the CRA." (F-CA-27); regex ^[0-9]{9}RR[0-9]{4}$ hard-validated against the CRA search tool's documented structure, with the List of charities link; s.188.1(7)-(9) penalties now verbatim (no longer HYPOTHESIS). Unchecked → CA-20 thank-you letter only (no title, no registration number). US mirror: US-00 s.170(c) attestation (F-US-19, US-TC-31). Tests CA-TC-28, CA-TC-29. | CRA advanced search; sample receipts; charity-vs-NPO page; ITA s.188.1 |
| R-03 (critical) — serial consumption undefined | FIXED | CA-03 defines Preview (watermark, nothing consumed) and Issue (treasurer enters next starting serial from their log; batch uniqueness asserted; issued-receipts CSV log F-CA-26; post-issue message). Tests CA-TC-26 (duplicate → invalid), CA-TC-27 (replacement replaces itself → invalid), CA-TC-31 (preview), CA-TC-32 (batch of three). Browser-local reminder → Phase 2 note (Q19), not a rule. | CRA Correcting/replacing ("keep both copies ... cancelled"); Reg. 3501(1)(c), (4), (5) |
| R-04 (high) — retention notice incomplete | FIXED | CA-14 / F-CA-22 notice uses the CRA's own sentences: electronic file, signed copy, Canadian address, backup, two years / six years. retention_notice_contains asserted in CA-TC-01. | CRA Books and records; computer-generated receipts |
| R-05 (high) — contemporaneous warning fires too late | FIXED | US-08: test is when the donor OBTAINS it; three-tier message (≤ Jan 31 / Feb 1–Apr 15 / Apr 16–Oct 15 / after Oct 15) computed from today; late-January filing cited to IR-2025-08. Tests US-TC-17 (extension_only), US-TC-25 (Feb → late_for_early_filers), US-TC-26 (too_late). | §170(f)(8)(C); Pub 526; Pub 1771; IR-2025-08 |
| R-06 (high) — issue-date override permits backdating | FIXED | CA-04 and US-08/US-14: date_issued = clock.today, displayed, not editable; date_issued in inputs is rejected (CA-TC-37, US-TC-30). ">= last gift date" phrasing removed; kept as an assertion. | Reg. 3501(1)(f) |
| R-07 (medium) — donor address demands Canadian parts | FIXED | CA-06: one free-text address ≥ 10 characters, any country; CA-TC-34 (Florida address issued). | Reg. 3501(1)(g) |
| R-08 (medium) — corporate donors blocked | FIXED | As m-3: donor.kind, CA-TC-33; true-donor rule quoted. | Reg. 3501(1)(g); CRA true donor |
| R-09 (medium) — double receipting via intermediary | FIXED | CA-15(f) + gift-row flag via_receipting_intermediary → refer (CA-TC-35). Platform mechanics labelled HYPOTHESIS in help text. | CRA "customers are the true donors" |
| R-10 (medium) — Q5 already answered | FIXED | As m-1. | CRA Correcting/replacing |
| R-11 (medium) — foreign currency | FIXED | New CA-19 (refer): currency fixed to CAD, "CAD"/"USD" labels on every amount, refer text; CA-TC-36. Conversion practice labelled HYPOTHESIS (confidence low; Q9 open). | Reg. 3501(1)(h) (basis only) |
| R-12 (medium) — FMV == P contradiction | FIXED | US-04 and US-14: 0 < FMV < P; US-TC-20 expects invalid; validator recomputes it. | Pub 1771 p.7 "no donative element" |
| R-13 (medium) — mailed gifts: envelope, which date to print | FIXED | CA-05 formula date_of_donation = postmark_date if received_by_mail else date_received; printed date = date of donation; tip "Keep the postmarked envelope with your records." CA-TC-16 asserts per_gift_dates_printed: [2026-11-15, 2026-12-30]. | CRA What you need to know |
| R-14 (low) — label drift | FIXED | "Place issued", "Name and website of the Canada Revenue Agency"; nominal wording "too minimal to affect the amount of the gift (CRA nominal threshold)" — "de minimis" removed. | CRA sample receipts; Split receipting |
| R-15 (low) — 80% error text | FIXED | As m-9. | — |
| R-16 (low) — 30-day rule verified | FIXED | US-11 quotes §170(f)(12)(C); tool says "within 30 days"; Q13 half-closed. | 26 U.S.C. §170(f)(12)(C) |
| R-17 (low) — goods plus IRB | FIXED | Fourth enum value (see m-8), US-TC-24. | Reg. 1.170A-13(f)(2); Pub 1771 |
| R-18 (low) — input schema inconsistency | FIXED | Canonical schema: block in rules.yaml; every test uses base: + the schema's names only (amount_of_gift, advantage.amount, advantage.is_cash_or_near_cash, good_faith_estimate, receipt_year, clock.today). | — |
| R-19 (low) — provided with FMV 0 | FIXED | US-14(3) rejects; US-TC-23. | — |
| R-20 (low) — donor name not statutory | FIXED | F-US-04 satisfies US-16 (product rule) with the note; US-14 note. | §170(f)(8)(B) |
Resolutions: Codex review
| Finding | Resolution | What changed / why | Source |
|---|---|---|---|
DR-001 (critical) — FMV <= P vs rejection of FMV == P | FIXED | 0 < FMV < P everywhere (US-04, US-14, US-15, schema constraint); US-TC-20 recomputed as invalid. | Pub 1771 p.7 |
| DR-002 (major) — CA fixtures omit signer and gated fields | FIXED | fixtures.ca_valid (charity, registration number, signer, serial, donor, clock); every CA case is base: ca_valid + overrides; validator audits every required field on every document-generating case. | — |
| DR-003 (major) — US fixtures omit org, donor, issue date | FIXED | fixtures.us_valid; same audit. | — |
| DR-004 (major) — no canonical schema; aliased names | FIXED | schema.ca_input/ca_output/us_input/us_output + money_convention; templates use the same names. | — |
DR-005 (major) — empty satisfies | FIXED | New product rules US-16 (layout/delivery) and US-13 (signature optional); F-US-02/03/05/15/16 map to them; validator fails on any empty satisfies. | Pub 1771 p.3 |
| DR-006 (major) — no batch serial uniqueness / duplicate test | FIXED | CA-03 formula asserts distinct serials; CA-16(8); CA-TC-26. | Reg. 3501(1)(c) |
| DR-007 (major) — goods_status required only ≥ $250 | FIXED | US-02/US-14: required for EVERY gift; US-TC-27 ($100 gift, status missing → invalid). | §170(f)(8)(B)(ii) |
| DR-008 (major) — signer has no field ids / validation | FIXED | F-CA-23 (name + title, both non-blank), F-CA-18 (line), F-CA-24 (banner); CA-TC-30. | Reg. 3501(1)(i), (2) |
| DR-009 (major) — tax year, gift date, description/GFE, issue date can be blank | FIXED | Schema required/required_when; US-14(5)-(7); tests US-TC-28 (tax year), US-TC-32 (date outside year), US-TC-23 (GFE), CA-TC-38/39/40; validator's required-field audit. | — |
| DR-010 (minor) — no exact-10% case | FIXED | CA-TC-22 (G=500, A=50 → nominal) and CA-TC-23 (A=50.01 → $449.99). | CRA Split receipting |
| DR-011 (minor) — $0 gifts untested | FIXED | CA-TC-25 and US-TC-22. | — |
| DR-012 (minor) — cents test not discriminating | FIXED | CA-TC-24 (G=333.35, A=33.34 → $300.01). | — |
| DR-013 (minor) — Q8 and Q12 unresolved | FIXED | Q12 RESOLVED (Pub 526, net). Q8 closed as a labelled PRODUCT ASSUMPTION (folio and Reg. 3501(1)(h.1) speak of "the gift"; no aggregation guidance exists; per-gift is the conservative reading). | Pub 526; Folio S7-F1-C1 |
Still open / needs the founder
- Founder decisions encoded as defaults (review wording): the not-registered thank-you letter (CA-20); the nominal-advantage printing convention (CA-09); per-gift nominal threshold on cumulative receipts (CA-18); the mixed goods + intangible-religious-benefits enum (US-02(7)); CAD-only with refer for other currencies (CA-19).
- Unverified primary text: ITA s.248(30)–(32) verbatim (Q1); Reg. 1.170A-13(f)(2) verbatim (Q16); current-year token figures (Q10); s.6714 verbatim; CRA foreign-currency guidance (Q9); a canada.ca prose page stating "RR" (structure verified from the CRA search tool; RR confirmed only by the sample receipt image and List entries).
- Implementation choice, no rule impact: whether to add owner-password PDF encryption in addition to the signing workflow.
Final-pass resolutions (2026-09-25, after the final Fable verification and the Codex final findings)
Editor: Claude Fable 5.1. Inputs: donation-receipts-2026-09-25-final-fable.md (PASS, 9 nits) and Codex's final findings DR-004 / DR-014 / DR-015 / DR-016 / DR-017. Files changed: rules.yaml, test-cases.yaml, receipt-templates.md, open-questions.md, validate.py; new requirements.txt. No app code.
Codex final findings
| Finding | Resolution | What changed |
|---|---|---|
DR-004 — tests used undeclared output aliases (per_gift_eligible, per_gift_dates_printed, per_gift_deductible_ceiling) | FIXED | Every per-gift expectation is now the schema's per_gift list of rows (CA: date_printed, amount_of_gift, advantage_raw, advantage_effective, eligible_amount, nominal; US: date_received, amount, good_faith_estimate, net_contribution, deductible_ceiling, cwa_required, disclosure_required) in CA-TC-16/20 and US-TC-05/06/07/08/18/21/24/29/33. Output keys are now a CLOSED SET: schema.ca_output / us_output gained the keys the tests legitimately used (title, sentence_printed, donor_name_printed, gifts_included, gifts_excluded, log_row_count, message_after_issue, timing_message, tips, goods_lines), test-only matchers are declared in schema.output_matchers (tips_contain, message_after_issue_contains), and note_to_treasurer moved out of expected_output to the case level. Inputs are closed too: refer_description and donor_is_issuer declared; the seven refer cases renamed description → refer_description. validate.py FAILS on any undeclared output key, any undeclared input key, and on any key its own engines emit that the schema lacks. Money keys and per-row money columns are read from the schema types, not hard-coded. |
| DR-015 — blankness of ALL required fields before parsing | FIXED | ca_required_blank() / us_required_blank() run right after the forbidden-input check and before any refer gate, date.fromisoformat or Decimal: mode, receipt_type, clock.today, charity/signer/donor fields (per attestation branch; skipped for an anonymous donor, whose refer decides), serial_start / replaces_serial_number / receipt_year (conditional), every gift's date_received, postmark_date when received_by_mail, amount, advantage description when amount > 0 (US: goods_status, description and GFE when goods provided). CA-16(4) and US-14(5) rewritten to say so, with the gate ORDER in each formula. New tests: CA-TC-41 (clock blank → CA-04), CA-TC-42 (mailed gift without postmark → CA-16), CA-TC-43 (date_received blank), US-TC-34 (clock blank → US-14), US-TC-35 (date_received blank). |
| DR-016 — template placeholders | FIXED | receipt-templates.md rewritten: every placeholder is a canonical schema path ({charity.name}, {donor.first_name} {donor.middle_initial} {donor.last_name}, {amount_of_gift}, {amount_of_advantage}, {eligible_amount}, {replaces_serial_number}, {donor_name_printed}, {per_gift[i].amount}, {per_gift[i].good_faith_estimate}, {per_gift[i].deductible_ceiling}, {goods_lines[i]}, {gifts_excluded[i].reason} ...); {first}, {G}, {A_eff}, {original}, {donor name}, ${amount}, ${P}, ${GFE} are gone. Each placeholder line carries its F-id and rule id, including the thank-you letter (F-CA-28/02/03/14; the closing is tagged as NOT F-CA-23) and the US signer line (F-US-15, US-13). validate.py parses the file: 45 distinct placeholders, all resolve; every F-* id and rule id mentioned must exist. |
| DR-017 — G=$50 boundary pair | FIXED | CA-TC-44: A=$5.00 → nominal, eligible $50.00, advantage $0.00. CA-TC-45: A=$5.01 → not nominal, eligible $44.99. CA-09 notes list the pair. |
| DR-014 — PyYAML missing | FIXED | validate.py prints PyYAML is not installed. Run: pip install pyyaml (or: pip install -r requirements.txt) and exits 2; requirements.txt (pyyaml>=6.0) sits beside it. |
Fable's nine nits
| # | Nit | Resolution |
|---|---|---|
| 1 | US goods line drifted (template vs rule/tests) | FIXED — one pattern everywhere, the longer form: "In exchange for your contribution of {amount} on {date_received}, we gave you {goods_description} with an estimated fair market value of {good_faith_estimate}." Output is goods_lines (one per gift with goods; the old single-goods_line engine returned nothing for two such gifts). US-04, F-US-11, template §2(b), US-TC-05/06/08/24 aligned; US-TC-36 (two gifts with goods → two lines) added. Recorded as open-questions item 24. |
| 2 | Thank-you letter printed the signer although F-CA-23 is "absent" | FIXED — closing is "With gratitude, {charity.name}"; signer inputs are not required and never printed on the CA-20 path (a blank signer cannot render ", "). CA-20 rule/formula and open-questions item 20 updated. |
| 3 | §0 "official never appears" contradicted the letter title | FIXED — §0 now says the words never appear on that path "except inside the two negations" (title and mandatory sentence); CA-20 says the same. |
| 4 | CA-18 "not receiptable" row neither templated nor tested | FIXED (product decision) — a gift failing CA-10 on a cumulative receipt is EXCLUDED: not printed, not in the totals; the treasurer sees an on-screen notice per excluded gift (gifts_excluded rows + tips), wording in the template §1a; if every gift fails → refer CA-10. Tests CA-TC-46 (one excluded, totals over the other two) and CA-TC-47 (all excluded → refer). Labelled PRODUCT ASSUMPTION in CA-18 notes and open-questions item 8. |
| 5 | Validator skipped the 8 None-message refer cases; fields_present/absent not computed | FIXED — the eight messages now live in rules.yaml as refer_messages on CA-15, CA-17, CA-19, US-09, US-10, US-11, US-12 (12 entries); the engines return them and the validator compares every message exactly (a None from the engine is a mismatch, not a skip). fields_present is COMPUTED for every document (CA: 19 base ids + individual/initial/joint/replacement/cumulative/preview/issue conditionals; US: 10 base ids + address/EIN/statement/goods/IRB/disclosure/signer conditionals) and checked: expected-present must be a subset, expected-absent must be disjoint. |
| 6 | Banner still said "Not valid until signed" after signing | FIXED — "Valid only when signed by an authorized person" in CA-11 (rule + formula), F-CA-24, schema note, template (banner + download caption), CA-TC-01, validator, open-questions item 3. Grep for the old wording across the five files: 0 hits. |
| 7 | "M." vs "A" | FIXED — the initial prints as entered, no period added: template {donor.middle_initial}, F-CA-10 note, schema note on donor_name_printed; "Ruth A Vanderberg" everywhere. |
| 8 | Serial fallback only in the validator | FIXED — CA-03 rule text and formula document the no-trailing-digits fallback ("B" → "B-1", "B-2"); template §1 note; open-questions item 19; test CA-TC-48. |
| 9 | CA-03 cited Q17 | FIXED — CA-03 notes cite open-questions Q19; item 19 states that CA-03 references it. |
Mechanical evidence
$ python validate.py
rules: 38 ({'computed': 28, 'refer': 7, 'product': 3}); fields: 47; refer messages: 12; template placeholders: 45; test cases: 84 {'CA': {'computed': 24, 'refer': 10, 'invalid': 14}, 'US': {'computed': 19, 'refer': 5, 'invalid': 12}}
RESULT: PASS (0 problem(s)) exit 0
$ python validate.py --selftest # corrupts one expectation AND adds one undeclared output key
FAIL CA-TC-01
- eligible_amount: got 10000 expected 9999
FAIL CA-TC-02
- undeclared output key 'per_gift_eligible' in expected_output (schema.ca_output)
RESULT: FAIL (2 problem(s)) exit 1
$ # DR-004 positive control on the REAL file: `per_gift_eligible: [...]` injected into CA-TC-20
FAIL CA-TC-20
- undeclared output key 'per_gift_eligible' in expected_output (schema.ca_output)
RESULT: FAIL (1 problem(s)) exit 1
$ # injected line removed; sha256 of test-cases.yaml identical before and after; validate.py PASS again
Case count 73 → 84 (CA 40 → 48, US 33 → 36). Nothing in this pass changes a threshold, a formula result, or a printed legal element other than the banner wording (nit 6) and the goods-line pattern (nit 1).
Codex re-check resolutions (orchestrator, 2026-09-25)
The Codex re-check confirmed DR-004 and DR-017 PASS, and all 43 computed cases recompute in integer cents with zero mismatches. Two items remained:
- DR-015: FIXED.
expand_generated()no longer parsesgifts_generated.startbefore the engine's blank-field audit. A blank or malformed start passes through unparsed as one gift, soca_eval/us_evalreport it through their ordered required-field checks. Positive control, using Codex's exact probe (CA-TC-02 with a blankclock.todayplus a malformed start): it previously raised aValueError, and now returnsinvalid CA-04 "The device date is missing…". - DR-016: FIXED. 15 layout lines are now tagged with a field id and rule id.
validate.pyREQUIRES every placeholder line inside the printed layouts to carry both. Positive control: removing one tag fails with "TEMPLATE line 124: placeholder without field id + rule id tag"; restored, it passes.
The validator passes on 84 cases, and --selftest fails as it should. Status: donation-receipt rules CLEARED for build.