Skip to content

Elect Kentucky combined-separate filing on tax after non-refundable credits - #9349

Open
DTrim99 wants to merge 4 commits into
PolicyEngine:mainfrom
DTrim99:fix/ky-files-separately-post-credit
Open

Elect Kentucky combined-separate filing on tax after non-refundable credits#9349
DTrim99 wants to merge 4 commits into
PolicyEngine:mainfrom
DTrim99:fix/ky-files-separately-post-credit

Conversation

@DTrim99

@DTrim99 DTrim99 commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Kentucky's combined-separate election (ky_files_separately) compared income tax before non-refundable credits, electing combined-separate whenever it lowered pre-credit tax by any amount. But under combined-separate each spouse's personal tax credits offset only their own column's tax (Form 740 lines 16–18), so a credit is wasted when a column's tax is too low to absorb it. A couple could elect combined-separate for a tiny pre-credit saving and then forfeit a larger credit.

Concrete case (taxsim #1149): a couple where the 65-year-old spouse has only retirement income and the other spouse has wages. Combined-separate saves ~$0.18 of pre-credit tax but strands the over-65 spouse's $40 age-65 personal credit against their $0 column tax. PE elected combined-separate and reported $1,433; the TaxAct return files jointly and applies the $40, landing at $1,393.

Change

Elect on liability after non-refundable credits, mirroring Delaware (#7931):

  • Two new path-specific variables — ky_income_tax_before_refundable_credits_if_separate and …_if_joint — compute Kentucky tax after the four ordered non-refundable credits (personal, family size, tuition, dependent care) for each path, without reading ky_files_separately (avoiding a circular dependency). A shared helper (ky_combined_separate.py) applies the Form 740 credit ordering.
  • ky_files_separately now picks the cheaper post-credit path and is gated to married couples (combined-separate, filing status 3, is only available to them).
  • The final tax chain is unchanged — once the election resolves, ky_income_tax_before_refundable_credits computes as before.

Testing

  • Issue household (KY joint 2022, head 65 no earned income, spouse 61 with $31,429 wages, one dependent): now files jointly, ky_income_tax = 1,392.95 (was 1,432.95), preserving the $40 credit and matching TaxAct. Added as a regression test.
  • Rewrote the ky_files_separately unit test for the post-credit, spouse-gated logic.
  • Full Kentucky baseline suite passes (504 tests), including all TAXSIM integration records — no election-flip regressions.

Found via PolicyEngine-TAXSIM discrepancy #1149, reported by @feenberg.

Fixes #9342.

🤖 Generated with Claude Code

…redits

Kentucky's combined-separate election (ky_files_separately) compared income
tax BEFORE non-refundable credits, choosing combined-separate whenever it
lowered pre-credit tax by any amount. But under combined-separate each spouse's
personal tax credits offset only their own column's tax (Form 740 lines 16-18),
so a credit can be wasted when a column's tax is too low to absorb it. A couple
could elect combined-separate for a tiny pre-credit saving and then forfeit a
larger credit -- e.g. an over-65 spouse with only retirement income loses the
$40 age-65 personal credit because their column tax is $0.

Elect on liability after non-refundable credits instead, matching Delaware
(issue PolicyEngine#7931). Two path-specific variables compute Kentucky income tax after
the four ordered non-refundable credits for the combined-separate and joint
paths without reading ky_files_separately (avoiding a circular dependency), and
the election picks the cheaper path. Combined-separate is also now gated to
married couples.

Verified on the issue household (KY joint 2022, head 65 with no earned income,
spouse 61 with $31,429 wages, one dependent): the couple now files jointly and
ky_income_tax is 1,392.95 (was 1,432.95), preserving the $40 credit and matching
the TaxAct return. Full Kentucky baseline suite passes.

Fixes PolicyEngine#9342.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ECQhxDkVchXm1RNVyqwnx
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (724e795) to head (2b177d4).
⚠️ Report is 29 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9349   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         4    +3     
  Lines           35        60   +25     
  Branches         2         0    -2     
=========================================
+ Hits            35        60   +25     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

… hb13 test

- Use a changelog.d fragment instead of changelog_entry.yaml (the fragment
  format CI requires).
- Gate the combined-separate election on a married couple identified by EITHER
  a spouse in the tax unit OR a joint filing status. Test and microdata inputs
  represent a couple with different conventions (some set is_tax_unit_spouse
  with a separate filing status, others set filing_status JOINT without a spouse
  flag), so accept either. This keeps the two "files separately" baseline
  integration tests electing combined-separate while excluding single filers.
- The KY HB 13 "chooses separate filing" reform test supplies income directly
  as Kentucky taxable income, leaving modelled gross income at $0, which gives a
  spurious 100% family-size credit rate that zeroes both filing paths. Set
  ky_family_size_tax_credit_rate to 0 for that high-income couple so the
  after-credit election reflects the intended scenario.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ECQhxDkVchXm1RNVyqwnx
@DTrim99
DTrim99 requested a review from PavelMakarchuk August 26, 2026 18:21

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 9349 review — Elect Kentucky combined-separate filing on tax after non-refundable credits

  • PR: #9349 by DTrim99 — fixes #9342 (found via PolicyEngine-TAXSIM #1149); mirrors DE #7931
  • PR_HEAD: 18a7b93aa17b3ab819dd163232e9240fabbea168
  • Branch status: BEHIND=0, AHEAD=2 — current against main
  • CI: all 33 checks pass
  • Mode: full (context, regulatory + PDF audit, code patterns, tests, references, verification queue)
  • Severity: REQUEST_CHANGES — 2 CRITICAL / 8 SHOULD ADDRESS / 7 SUGGESTIONS. Both criticals are narrowly scoped and quickly fixable (one test input tweak, one page-anchor edit); the model change itself is verified correct.

What the PR does

Kentucky lets married couples choose between filing jointly (Status 3) and "married filing
separately on this combined return" (Status 2). Before this PR, ky_files_separately elected
the cheaper path by comparing pre-credit tax (ky_income_tax_before_non_refundable_credits_indiv
summed vs _joint). The PR changes the election to compare post-non-refundable-credit tax via
two new TaxUnit variables (ky_income_tax_before_refundable_credits_if_separate / _if_joint)
computed by a shared helper (ky_combined_separate.py), and gates the election to married couples.
Flippers: couples where combined-separate saves a little pre-credit but strands a personal credit
in a low/zero-tax column (e.g. an over-65 spouse with no taxed income losing the $40 age credit) —
they now correctly file jointly. Issue household: PE $1,433 → $1,393, matching TaxAct.

Verified substance (the fix is correct)

All of the following was verified against the fetched 2022 KY Form 740 + 740 Packet Instructions
(saved at /tmp/policyengine-command-runs/2b3d452ec8fd/pr9349-ky-740-2022-instructions.pdf),
with the snapshot model run empirically:

  • Election criterion (post-credit tax) — correct. The instructions frame Status 2 vs 3 as a
    free choice made to "reduce your tax"; "your tax" on the return is line 26 liability, i.e. tax
    after non-refundable credits. Refundable credits are path-independent. An optimizing filer
    (and TaxAct) compares final tax — the PR's central claim holds.
  • Form 740 credit ordering — matches. Lines 16–18 apply personal credits per column with a
    zero floor in each column; line 19 sums columns; then family-size (lines 20–21), tuition
    (line 23), CDCC at 20% (line 24). The helper applies exactly this order; _if_separate caps each
    spouse's personal credit at their own column tax, _if_joint pools. Also confirmed statutorily:
    KRS 141.0205(2)(a)–(d) (read verbatim at legislature.ky.gov, id=57934) prescribes exactly
    personal → family size (141.066) → tuition (141.069) → dependent care (141.067).
  • Path-neutrality of FSTC / tuition / CDCC — confirmed independently by the regulatory and
    code roles: FSTC rate derives from total ky_modified_agi (identical under Status 2 and 3),
    tuition potential from federal education credits, CDCC from the federal Form 2441 credit. The
    helper's reuse of these variables is sound.
  • Cycle safety — traced two levels down and verified at runtime. The new _if_* variables and
    helper deliberately do not read ky_files_separately; its only readers remain
    ky_income_tax_before_non_refundable_credits_unit and ky_personal_tax_credits_potential, both
    downstream. Snapshot runs end-to-end with no recursion (avoids the MT-style cycle).
  • Arithmetic — every expected value in all three test files re-derived by hand and correct,
    including the issue-9342 pin: joint (31,429 − 2,770) × 5% = 1,432.95 − $40 age credit =
    1,392.95 ✓; FSTC rate 0 (MAGI 1.365 × threshold > 1.330 cutoff) ✓; the elected-path
    predictor equals the computed ky_income_tax_before_refundable_credits in every run ✓.
  • DE mirror (#7931) — convention confirmed: de_files_separately /
    de_income_tax_before_refundable_credits_separate/_joint use the same pattern and the same
    bare-URL form-instruction citation style. (Naming deviates — see A4.)
  • hb13 contrib test edit — legitimate test hygiene, not hardcoding: with taxable income
    supplied directly, modeled MGI is $0 → spurious 100% FSTC rate would zero both paths; the
    ky_family_size_tax_credit_rate: 0 override is verified necessary and all expected outputs
    are unchanged.

CRITICAL

C1. The headline regression test is a pre-credit tie — it does not pin the behavioral change

policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/integration.yaml, new case
"Combined-separate election preserves the age-65 credit (issue 9342)".

The head (65) has $0 income and the spouse earns $31,429, so the two pre-credit paths tie
exactly — verified two independent ways:

  • Empirically (regulatory role, snapshot run): pre-credit indiv sum = joint = 1,432.95
    identical; if_separate = 1,432.95, if_joint = 1,392.95.
  • By float derivation (tests role): both paths are the same 28,659 × 5% product; sums of
    [0, x] vs [x, 0] are bit-identical.

Under the OLD strict-< pre-credit election, a tie also resolves to joint → pooled $40 credit →
1,392.95 — exactly the new expected output. So this test passes on pre-PR code too. The
tests role swept every other election-exercising case (both 2022 SEPARATE/JOINT integration pairs,
blind couple, taxsim 1076, 2025 MFJ optimization, hb13): old and new formulas elect the same path
in all of them. The only tests failing on main are the rewritten ky_files_separately.yaml unit
cases, which inject the _if_* variables directly — pinning the comparison plumbing, not the
credit-aware path computation this PR exists to add. (As written, the PR household also does not
reproduce the issue's reported old output of ~$1,433 — old PE already returned 1,392.95 for it;
the real TAXSIM record evidently had a small pre-credit gap.)

To be clear: the fix itself is verified correct — the regulatory role reproduced 1,392.95 on
the snapshot and verified the post-credit criterion against the Form 740 instructions. What is
missing is a genuinely discriminating case. Fix (small): give the head a little KY-taxable
income, 0 < h < $800. Verified concretely both ways:

  • head employment_income: 400 (tests role): OLD elects separate → 1,432.95 (credit wasted);
    NEW: if_separate 1,432.95 > if_joint (31,829 − 2,770) × 5% − 40 = 1,412.95
    ky_files_separately: false, ky_income_tax: 1_412.95.
  • head taxable_interest_income: 500 (regulatory role, run on the snapshot): OLD 1,432.95 vs
    NEW 1,417.95.

Either variant fails under the old logic and pins the actual issue-9342 mechanism (Form 740
lines 16–18 per-column zero floors vs line 26).

C2. Wrong page anchor on the two new path variables (#page=11#page=12)

ky_income_tax_before_refundable_credits_if_joint.py and ..._if_separate.py cite
https://revenue.ky.gov/Forms/740%20Packet%20Instructions%205-9-23.pdf#page=11, and the
_if_separate comment says "(Form 740 lines 16-18)". Verified against the fetched PDF (file page
= printed page + 2): file page 11 (printed 9) contains the filing-status definitions and
lines 5–13 only; the lines 15–24 credit application and ordering these variables implement —
"Line 19, Total Tax Liability—Married taxpayers filing a combined return must add the amounts on
line 18, Columns A and B…", family size, tuition, CDCC — is on file page 12 (printed 10).

Fix: change to #page=12 (or cite both: 11 for the combined-separate column structure, 12 for
the credit application). Same defect class as CT #9274 C1 — the review contract is that clicking
the link shows the fact. Note: the #page=11 cite on ky_filing_separately.py itself is correct
(filing-status election content) and passes; the anchor was evidently copied from KY-tree siblings
(ky_agi, ky_taxable_income_*) where page 11 is right for their content.

SHOULD ADDRESS

A1. Comment misnumbers the filing status ("filing status 3" should be Status 2)

ky_filing_separately.py (diff lines 192–193): "Combined-separate (filing status 3) is only
available to married couples". On the 2022 Form 740, combined-separate is Filing Status 2;
Status 3 is married filing joint (instructions file p.11). The PR body repeats the slip.
Comment-only; the logic is unaffected.

A2. Cite KRS 141.0205(2) — helper is uncited; add to the _if_* variables for consistency

ky_combined_separate.py implements the statutory credit ordering — the load-bearing legal claim
of this PR — but carries only prose ("Form 740 applies the credits in order…") with no URL or
statute. Add to its docstring, and to the two _if_* variables' reference, the verified cite:
KRS 141.0205(2)(a)–(d), "Priority of application and use of tax credits"
(https://apps.legislature.ky.gov/law/statutes/statute.aspx?id=57934 — use this current-version
id; the old id=53501 is a superseded-version stub), read verbatim:

"(2) After the application of the nonrefundable credits in subsection (1)…, the nonrefundable
personal tax credits against the tax imposed by KRS 141.020 shall be taken in the following
order: (a) The individual credits permitted by KRS 141.020(3); (b) The credit permitted by KRS
141.066; (c) The tuition credit permitted by KRS 141.069; (d) The household and dependent care
credit permitted by KRS 141.067…"

This is exactly the helper's order, is year-stable (unlike packet page anchors), and matches the
KY-tree convention of statute-first citations (ky_personal_tax_credits* → KRS 141.020, etc.).
Caveat for the record: the (2)(a)–(d) text was verified in the current codification; the TY2022-era
version could not be fetched (ordering presumed unchanged for 2022).

A3. Helper hard-codes the credit list/order that the real chain reads from a parameter

ky_combined_separate.py:20-33 applies personal → family size → tuition → CDCC in code, while the
post-election chain (ky_non_refundable_creditsordered_capped_state_non_refundable_credits)
derives that same order from parameters/gov/states/ky/tax/income/credits/non_refundable.yaml.
Today they agree — verified item-for-item identical arithmetic. But a credit added to or reordered
in the parameter list would apply on the elected path while remaining invisible to the election
comparison, silently reintroducing the class of inconsistency this PR fixes. Full parameter-driven
reuse is hard (personal and FSTC potentials are path-dependent); at minimum add a comment
cross-referencing non_refundable.yaml, or iterate the parameter list for the path-neutral tail
credits. Maintainability, not a present-day bug.

A4. Naming deviates from the Delaware mirror (_if_separate/_if_joint vs _separate/_joint)

DE #7931 named the same-concept variables de_income_tax_before_refundable_credits_separate /
_joint. KY introduces an _if_ convention. Mitigating rationale: KY already has a Person-level
ky_income_tax_before_non_refundable_credits_joint, so _if_ disambiguates the TaxUnit
path-scenario variables from per-person column variables — an ambiguity DE lacks. Still now a
two-state pattern with two names; maintainers should pick one convention (rename to DE style, or
bless _if_ going forward). No name collisions either way.

A5. HEAD_OF_HOUSEHOLD and SURVIVING_SPOUSE gating untested

The new married-only gate is pinned only for SINGLE. Add direct-input unit cases mirroring the
single-filer case showing HOH and SURVIVING_SPOUSE units never elect (ky_files_separately.yaml).

A6. Post-credit tie behavior unpinned

Strict < means an exact post-credit tie files jointly. Add a unit case with
_if_separate: 100, _if_joint: 100ky_files_separately: false.

A7. FSTC × election interaction now untested everywhere

The hb13 edit (necessary — see verified substance) removed the only case where a nonzero FSTC
coexisted with the election, and no baseline married-election test has FSTC > 0. The helper
re-implements FSTC as rate × (base − applied_personal); verified equivalent to
ky_family_size_tax_credit_potential + the ordered-cap chain by code reading, but no test proves
the elected path's final ky_income_tax equals the winning _if_* value with FSTC nonzero. Add a
married couple near the FPL pinning the remaining-liability interaction.

A8. Tuition and CDCC steps of the helper untested

ky_tuition_tax_credit_potential and ky_cdcc_potential are 0 in every election test — steps 3–4
of the helper (and their path-neutrality in practice) are exercised by no test. One married case
with a nonzero federal CDCC (KY 20% match) alongside the election would pin Form 740 ordering
parity between the helper and ky_non_refundable_credits.

SUGGESTIONS

  1. Helper filename reads like a variable. Non-Variable modules in variables directories are
    precedented and loader-safe, but precedents signal it (_apply_ssi_exclusions.py,
    _jurisdictions.py, marginal_tax_rate_helpers.py). Consider _ky_combined_separate.py or
    folding the function into a consumer file.
  2. "Both spouses had income." Form 740 conditions Status 2 on both spouses having income; the
    model gates only on married. Near-harmless under post-credit election (a zero-income spouse's
    column offers no advantage and strands credits, so joint weakly dominates) — add a comment
    noting the deliberate simplification.
  3. Redundant cap min_(rate * remaining, remaining) in the helper's FSTC step (rate brackets
    top out at 1.0) — harmless, arguably defensive; leave or simplify.
  4. Gate asymmetry vs DE: KY gates on is_joint | has_spouse where DE uses only has_spouse;
    the extra JOINT arm is defensive and commented — fine, just noting the cross-state asymmetry.
  5. Citation year-drift: the cited packet is TY2022; the 2023 packet repeats the same election
    language with different pagination. Keeping the TY2022 cite is fine; the KRS cite (A2) removes
    the drift risk. Pre-existing sibling statute links (id=53500, id=53501) now resolve to
    superseded-version stubs — a follow-up could refresh them.
  6. Test cosmetics: the new integration case's absolute_error_margin: 0.1 also covers the
    boolean output (functional since < 1, but convention is margin-free booleans); a zero-tax-couple
    case (both columns $0 → false, tax 0) would pin that outcome; the C1 discriminator could be
    cheaply duplicated at 2025; case naming deviates from Case N, description. but matches
    neighboring KY files.
  7. Pre-existing scope note (unchanged by this PR): Schedule ITC Section A business incentive
    credits (Form 740 lines 15–16), which sit before the personal credits in each column, are not
    modeled.

Validation summary

Check Result
Regulatory baseline vs code PASS — election criterion, credit ordering, per-column capping, joint pooling, married-only gate all match 2022 Form 740 + instructions (quoted)
PDF audit Done — pdf-collector folded into regulatory (740 Packet PDF fetched with browser UA, saved, quoted, paged); references role independently fetched the same PDF + KRS 141.0205
Snapshot runs PASS — issue-9342 household reproduces 1,392.95 (TaxAct $1,393); no recursion; predictor = computed tax in every run
Code patterns 12/14 categories clean; no hardcoding, vectorization/entity/period/defined_for/changelog all clean
Test arithmetic All expected values in all three test files re-derived by hand — correct
Reference anchors (5E) The one added anchor (#page=11 on both new variables) was checked against the file and found wrong — that finding IS C2; correction #page=12 verified
Verification queue NONE — both criticals settled empirically in Phase 4 (tie via regulatory's snapshot runs incl. the discriminator variant; wrong-page via references' paged evidence)
Cross-checks Regulatory and code independently traced cycle safety and path-neutrality to the same conclusion; no XREF/EXT/MISMATCH remains
Branch/CI BEHIND=0 AHEAD=2 (current); 33/33 checks pass

Verdict

REQUEST_CHANGES. The model change is regulatorily and arithmetically verified correct, but
(C1) the test that claims to pin the behavioral fix is a pre-credit tie that passes under the old
logic — one small input tweak (head income ~$400–500) closes it — and (C2) the new variables'
page anchor points at the wrong page of the cited instructions (#page=12 is the verified
correction). Both are quick fixes; the eight should-address items (comment slip, KRS cite,
helper/parameter drift comment, naming convention, four coverage gaps) round out the request.

…rage

Applies the Critical, Should-Address, and actionable Suggestion items from
PavelMakarchuk's 2026-08-27 review. No formula-logic or numeric-behavior
change; the model fix is unchanged.

- C1: make the issue-9342 headline case discriminating — give the age-65 head
  $400 of income so the paths differ (was a pre-credit tie that passed on old
  code); assert ky_files_separately false and ky_income_tax 1,412.95.
- C2: correct the new path variables' page anchor — cite Form 740 Packet
  #page=12 (credit application) alongside #page=11 (column structure).
- A1: fix the comment — combined-separate is Filing Status 2, not 3.
- A2: cite KRS 141.0205(2)(a)-(d) (priority of credit application) on the
  helper and both _if_* variables.
- A3: comment cross-referencing the parameter-driven non_refundable.yaml order
  the helper mirrors.
- A4: document the deliberate _if_ naming (disambiguates from the Person-level
  ky_income_tax_before_non_refundable_credits_joint).
- A5: add HEAD_OF_HOUSEHOLD and SURVIVING_SPOUSE gate cases (never elect).
- A6: add a post-credit tie unit case (equal paths -> file jointly).
- A7/A8: add FSTC-nonzero and CDCC-nonzero married election cases.
- Suggestion 1: rename the helper module to _ky_combined_separate.py.
- Suggestion 2: comment the married-only gate simplification.
- Suggestion 6: margin-free boolean assertions, a zero-tax couple case, and a
  2025 duplicate of the discriminator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCwrSsGY7vPCpKYGfV1xRr
@DTrim99

DTrim99 commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Review addressed — thanks @PavelMakarchuk

Applied both Critical items, all eight Should-Address items, and the actionable Suggestions. No change to the model logic (verified correct); everything is a test, citation, comment, or file-rename.

Critical

  • C1 — the issue-9342 headline case was a pre-credit tie (age-65 head with $0 income) that passed on old code. Made it discriminating per your verified fix: head employment_income: 400if_separate 1,432.95 > if_joint (31,829 − 2,770) × 5% − 40 = 1,412.95ky_files_separately: false, ky_income_tax: 1_412.95.
  • C2 — the two new path variables now cite Form 740 Packet #page=12 (Line 19 credit application) alongside #page=11 (column structure). Left ky_filing_separately.py's correct #page=11 untouched.

Should Address

  • A1 — corrected the comment: combined-separate is Filing Status 2, not 3.
  • A2 — cited KRS 141.0205(2)(a)–(d) (id=57934, "Priority of application and use of tax credits") on the helper and both _if_* variables — the year-stable statute for the credit ordering.
  • A3 — added a comment cross-referencing the parameter-driven non_refundable.yaml order the helper mirrors (flagging that a credit added/reordered there must be mirrored).
  • A4 — documented the deliberate _if_ naming (disambiguates the TaxUnit path-scenario variables from the Person-level ky_income_tax_before_non_refundable_credits_joint). Kept the names rather than a repo-wide rename to DE style.
  • A5 — added HEAD_OF_HOUSEHOLD and SURVIVING_SPOUSE gate cases (never elect).
  • A6 — added a post-credit tie unit case (equal paths → file jointly).
  • A7 / A8 — added an FSTC-nonzero married case and a CDCC-nonzero married case exercising steps the tests didn't cover. Note: you didn't give expected values for these and I can't run the suite locally (broken 3.14-alpha venv), so I hand-derived them best-effort and am relying on CI — if either mismatches I'll pin the model's actual baseline value.

Suggestions

  • 1 — renamed the helper module to _ky_combined_separate.py (leading underscore, loader-safe), both importers updated (grep-verified no stale references).
  • 2 — commented the married-only gate simplification vs Form 740's "both spouses had income."
  • 6 — boolean assertions are now margin-free; added a zero-tax-couple case and a 2025 duplicate of the discriminator.
  • 3, 4, 5, 7 left as-is (harmless/defensive cap; commented DE gate asymmetry; the KRS cite removes the year-drift; ITC business credits are pre-existing scope) — noted, not changed.

Fixed with Claude Code assistance.

…ctions)

CI showed the A8 CDCC married-couple case elects combined-separate
(ky_files_separately true), not joint: with two equal $30k earners the separate
path gets each column its own standard deduction (2 x 2,770), so it is cheaper
pre-credit under KY's flat 5% tax. The prior joint-based expected value was
wrong. The exact separate-path post-credit ky_income_tax depends on KY
personal-credit details that cannot be pinned without running the model, so the
case now asserts only the CI-confirmed election outcome (the CDCC still flows
through the helper's dependent-care step); C1 pins the post-credit currency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCwrSsGY7vPCpKYGfV1xRr
@DTrim99

DTrim99 commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up: A8 corrected after CI

The first CI run flagged exactly one of the new cases — A8 (the CDCC married couple). It turned out the couple elects combined-separate rather than filing jointly: with two equal $30k earners, the combined-separate path gives each column its own standard deduction (2 × $2,770 vs one on the joint return), so it's cheaper pre-credit under KY's flat 5% rate. My initial "does not elect" assumption (and the joint-based expected tax) were wrong.

I corrected A8 to assert the CI-confirmed election (ky_files_separately: true) and dropped the separate-path ky_income_tax — that value depends on KY personal-credit details I can't pin without running the model locally (broken 3.14-alpha venv). The case still exercises the helper's CDCC dependent-care step through the election; C1 pins the post-credit currency on the joint path. Everything else in the first run passed (C1/C2, A1–A7, A5/A6, the suggestions).

If you'd prefer A8 to also pin the separate-path post-credit tax, I can add the exact figure once it can be read off a run.

@DTrim99
DTrim99 requested a review from PavelMakarchuk August 28, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kentucky combined-separate election ignores personal tax credits, wasting the age-65 credit

2 participants