@invaro/opentax 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/bundle/http.js +2616 -885
- package/bundle/index.js +1726 -440
- package/bundle/main.js +3308 -1062
- package/package.json +6 -5
package/bundle/index.js
CHANGED
|
@@ -1035,15 +1035,15 @@ var require_codegen = __commonJS({
|
|
|
1035
1035
|
}
|
|
1036
1036
|
exports.not = not;
|
|
1037
1037
|
var andCode = mappend(exports.operators.AND);
|
|
1038
|
-
function
|
|
1038
|
+
function and2(...args) {
|
|
1039
1039
|
return args.reduce(andCode);
|
|
1040
1040
|
}
|
|
1041
|
-
exports.and =
|
|
1041
|
+
exports.and = and2;
|
|
1042
1042
|
var orCode = mappend(exports.operators.OR);
|
|
1043
|
-
function
|
|
1043
|
+
function or2(...args) {
|
|
1044
1044
|
return args.reduce(orCode);
|
|
1045
1045
|
}
|
|
1046
|
-
exports.or =
|
|
1046
|
+
exports.or = or2;
|
|
1047
1047
|
function mappend(op) {
|
|
1048
1048
|
return (x, y) => x === code_1.nil ? y : y === code_1.nil ? x : (0, code_1._)`${par(x)} ${op} ${par(y)}`;
|
|
1049
1049
|
}
|
|
@@ -3104,15 +3104,16 @@ var require_data = __commonJS({
|
|
|
3104
3104
|
}
|
|
3105
3105
|
});
|
|
3106
3106
|
|
|
3107
|
-
// ../../node_modules/.pnpm/fast-uri@
|
|
3107
|
+
// ../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/lib/utils.js
|
|
3108
3108
|
var require_utils = __commonJS({
|
|
3109
|
-
"../../node_modules/.pnpm/fast-uri@
|
|
3109
|
+
"../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/lib/utils.js"(exports, module) {
|
|
3110
3110
|
"use strict";
|
|
3111
3111
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3112
3112
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
3113
3113
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3114
3114
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3115
3115
|
var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
|
|
3116
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/?]$/iu);
|
|
3116
3117
|
function stringArrayToHexStripped(input) {
|
|
3117
3118
|
let acc = "";
|
|
3118
3119
|
let code = 0;
|
|
@@ -3255,7 +3256,7 @@ var require_utils = __commonJS({
|
|
|
3255
3256
|
continue;
|
|
3256
3257
|
}
|
|
3257
3258
|
} else if (input[0] === "/") {
|
|
3258
|
-
if (input[1] === "."
|
|
3259
|
+
if (input[1] === ".") {
|
|
3259
3260
|
output.push("/");
|
|
3260
3261
|
break;
|
|
3261
3262
|
}
|
|
@@ -3337,10 +3338,30 @@ var require_utils = __commonJS({
|
|
|
3337
3338
|
}
|
|
3338
3339
|
return output;
|
|
3339
3340
|
}
|
|
3341
|
+
var BYTE_HEX = new Array(256);
|
|
3342
|
+
{
|
|
3343
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
3344
|
+
for (let i = 0; i < 256; i++) {
|
|
3345
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
function isEscapeSafe(cp) {
|
|
3349
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
3350
|
+
}
|
|
3351
|
+
function percentEncodeNonAscii(cp) {
|
|
3352
|
+
if (cp < 2048) {
|
|
3353
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
3354
|
+
}
|
|
3355
|
+
if (cp < 65536) {
|
|
3356
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3357
|
+
}
|
|
3358
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3359
|
+
}
|
|
3340
3360
|
function normalizePathEncoding(input) {
|
|
3341
3361
|
let output = "";
|
|
3342
3362
|
for (let i = 0; i < input.length; i++) {
|
|
3343
|
-
|
|
3363
|
+
const ch = input[i];
|
|
3364
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3344
3365
|
const hex = input.slice(i + 1, i + 3);
|
|
3345
3366
|
if (isHexPair(hex)) {
|
|
3346
3367
|
const normalizedHex = hex.toUpperCase();
|
|
@@ -3354,10 +3375,66 @@ var require_utils = __commonJS({
|
|
|
3354
3375
|
continue;
|
|
3355
3376
|
}
|
|
3356
3377
|
}
|
|
3357
|
-
if (isPathCharacter(
|
|
3358
|
-
output +=
|
|
3378
|
+
if (isPathCharacter(ch)) {
|
|
3379
|
+
output += ch;
|
|
3359
3380
|
} else {
|
|
3360
|
-
|
|
3381
|
+
const code = input.charCodeAt(i);
|
|
3382
|
+
if (code < 128) {
|
|
3383
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3384
|
+
} else if (code < 55296 || code > 57343) {
|
|
3385
|
+
output += percentEncodeNonAscii(code);
|
|
3386
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3387
|
+
const low = input.charCodeAt(i + 1);
|
|
3388
|
+
if (low >= 56320 && low <= 57343) {
|
|
3389
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3390
|
+
i++;
|
|
3391
|
+
} else {
|
|
3392
|
+
output += percentEncodeNonAscii(65533);
|
|
3393
|
+
}
|
|
3394
|
+
} else {
|
|
3395
|
+
output += percentEncodeNonAscii(65533);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
return output;
|
|
3400
|
+
}
|
|
3401
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
3402
|
+
let output = "";
|
|
3403
|
+
for (let i = 0; i < input.length; i++) {
|
|
3404
|
+
const ch = input[i];
|
|
3405
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3406
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3407
|
+
if (isHexPair(hex)) {
|
|
3408
|
+
const normalizedHex = hex.toUpperCase();
|
|
3409
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3410
|
+
if (isUnreserved(decoded)) {
|
|
3411
|
+
output += decoded;
|
|
3412
|
+
} else {
|
|
3413
|
+
output += "%" + normalizedHex;
|
|
3414
|
+
}
|
|
3415
|
+
i += 2;
|
|
3416
|
+
continue;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
3420
|
+
output += ch;
|
|
3421
|
+
} else {
|
|
3422
|
+
const code = input.charCodeAt(i);
|
|
3423
|
+
if (code < 128) {
|
|
3424
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3425
|
+
} else if (code < 55296 || code > 57343) {
|
|
3426
|
+
output += percentEncodeNonAscii(code);
|
|
3427
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3428
|
+
const low = input.charCodeAt(i + 1);
|
|
3429
|
+
if (low >= 56320 && low <= 57343) {
|
|
3430
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3431
|
+
i++;
|
|
3432
|
+
} else {
|
|
3433
|
+
output += percentEncodeNonAscii(65533);
|
|
3434
|
+
}
|
|
3435
|
+
} else {
|
|
3436
|
+
output += percentEncodeNonAscii(65533);
|
|
3437
|
+
}
|
|
3361
3438
|
}
|
|
3362
3439
|
}
|
|
3363
3440
|
return output;
|
|
@@ -3365,7 +3442,8 @@ var require_utils = __commonJS({
|
|
|
3365
3442
|
function escapePreservingEscapes(input) {
|
|
3366
3443
|
let output = "";
|
|
3367
3444
|
for (let i = 0; i < input.length; i++) {
|
|
3368
|
-
|
|
3445
|
+
const ch = input[i];
|
|
3446
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3369
3447
|
const hex = input.slice(i + 1, i + 3);
|
|
3370
3448
|
if (isHexPair(hex)) {
|
|
3371
3449
|
output += "%" + hex.toUpperCase();
|
|
@@ -3373,7 +3451,22 @@ var require_utils = __commonJS({
|
|
|
3373
3451
|
continue;
|
|
3374
3452
|
}
|
|
3375
3453
|
}
|
|
3376
|
-
|
|
3454
|
+
const code = input.charCodeAt(i);
|
|
3455
|
+
if (code < 128) {
|
|
3456
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3457
|
+
} else if (code < 55296 || code > 57343) {
|
|
3458
|
+
output += percentEncodeNonAscii(code);
|
|
3459
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3460
|
+
const low = input.charCodeAt(i + 1);
|
|
3461
|
+
if (low >= 56320 && low <= 57343) {
|
|
3462
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3463
|
+
i++;
|
|
3464
|
+
} else {
|
|
3465
|
+
output += percentEncodeNonAscii(65533);
|
|
3466
|
+
}
|
|
3467
|
+
} else {
|
|
3468
|
+
output += percentEncodeNonAscii(65533);
|
|
3469
|
+
}
|
|
3377
3470
|
}
|
|
3378
3471
|
return output;
|
|
3379
3472
|
}
|
|
@@ -3407,6 +3500,7 @@ var require_utils = __commonJS({
|
|
|
3407
3500
|
reescapeHostDelimiters,
|
|
3408
3501
|
normalizePercentEncoding,
|
|
3409
3502
|
normalizePathEncoding,
|
|
3503
|
+
normalizeQueryFragmentEncoding,
|
|
3410
3504
|
escapePreservingEscapes,
|
|
3411
3505
|
removeDotSegments,
|
|
3412
3506
|
isIPv4,
|
|
@@ -3417,9 +3511,9 @@ var require_utils = __commonJS({
|
|
|
3417
3511
|
}
|
|
3418
3512
|
});
|
|
3419
3513
|
|
|
3420
|
-
// ../../node_modules/.pnpm/fast-uri@
|
|
3514
|
+
// ../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/lib/schemes.js
|
|
3421
3515
|
var require_schemes = __commonJS({
|
|
3422
|
-
"../../node_modules/.pnpm/fast-uri@
|
|
3516
|
+
"../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3423
3517
|
"use strict";
|
|
3424
3518
|
var { isUUID } = require_utils();
|
|
3425
3519
|
var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
|
|
@@ -3627,11 +3721,11 @@ var require_schemes = __commonJS({
|
|
|
3627
3721
|
}
|
|
3628
3722
|
});
|
|
3629
3723
|
|
|
3630
|
-
// ../../node_modules/.pnpm/fast-uri@
|
|
3724
|
+
// ../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/index.js
|
|
3631
3725
|
var require_fast_uri = __commonJS({
|
|
3632
|
-
"../../node_modules/.pnpm/fast-uri@
|
|
3726
|
+
"../../node_modules/.pnpm/fast-uri@4.1.1/node_modules/fast-uri/index.js"(exports, module) {
|
|
3633
3727
|
"use strict";
|
|
3634
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3728
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3635
3729
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3636
3730
|
function normalize(uri, options) {
|
|
3637
3731
|
if (typeof uri === "string") {
|
|
@@ -3770,6 +3864,7 @@ var require_fast_uri = __commonJS({
|
|
|
3770
3864
|
return uriTokens.join("");
|
|
3771
3865
|
}
|
|
3772
3866
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3867
|
+
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3773
3868
|
function getParseError(parsed, matches) {
|
|
3774
3869
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3775
3870
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3799,9 +3894,14 @@ var require_fast_uri = __commonJS({
|
|
|
3799
3894
|
uri = "//" + uri;
|
|
3800
3895
|
}
|
|
3801
3896
|
}
|
|
3897
|
+
const authorityMatch = uri.match(AUTHORITY_PREFIX);
|
|
3898
|
+
if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
|
|
3899
|
+
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3900
|
+
malformedAuthorityOrPort = true;
|
|
3901
|
+
}
|
|
3802
3902
|
const matches = uri.match(URI_PARSE);
|
|
3803
3903
|
if (matches) {
|
|
3804
|
-
parsed.scheme = matches[1];
|
|
3904
|
+
parsed.scheme = matches[1] === void 0 ? void 0 : matches[1].toLowerCase();
|
|
3805
3905
|
parsed.userinfo = matches[3];
|
|
3806
3906
|
parsed.host = matches[4];
|
|
3807
3907
|
parsed.port = parseInt(matches[5], 10);
|
|
@@ -3860,12 +3960,11 @@ var require_fast_uri = __commonJS({
|
|
|
3860
3960
|
if (parsed.path) {
|
|
3861
3961
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
3862
3962
|
}
|
|
3963
|
+
if (parsed.query) {
|
|
3964
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
3965
|
+
}
|
|
3863
3966
|
if (parsed.fragment) {
|
|
3864
|
-
|
|
3865
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3866
|
-
} catch {
|
|
3867
|
-
parsed.error = parsed.error || "URI malformed";
|
|
3868
|
-
}
|
|
3967
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
3869
3968
|
}
|
|
3870
3969
|
}
|
|
3871
3970
|
if (schemeHandler && schemeHandler.parse) {
|
|
@@ -7501,12 +7600,12 @@ var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error });
|
|
|
7501
7600
|
function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
7502
7601
|
const fieldErrors = {};
|
|
7503
7602
|
const formErrors = [];
|
|
7504
|
-
for (const
|
|
7505
|
-
if (
|
|
7506
|
-
fieldErrors[
|
|
7507
|
-
fieldErrors[
|
|
7603
|
+
for (const sub3 of error2.issues) {
|
|
7604
|
+
if (sub3.path.length > 0) {
|
|
7605
|
+
fieldErrors[sub3.path[0]] = fieldErrors[sub3.path[0]] || [];
|
|
7606
|
+
fieldErrors[sub3.path[0]].push(mapper(sub3));
|
|
7508
7607
|
} else {
|
|
7509
|
-
formErrors.push(mapper(
|
|
7608
|
+
formErrors.push(mapper(sub3));
|
|
7510
7609
|
}
|
|
7511
7610
|
}
|
|
7512
7611
|
return { formErrors, fieldErrors };
|
|
@@ -13015,7 +13114,7 @@ var StdioServerTransport = class {
|
|
|
13015
13114
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13016
13115
|
import { homedir } from "node:os";
|
|
13017
13116
|
import { join } from "node:path";
|
|
13018
|
-
var VERSION = "0.
|
|
13117
|
+
var VERSION = "0.4.0";
|
|
13019
13118
|
var ENDPOINT = "https://opentax.invaro.ai/api/t";
|
|
13020
13119
|
function optedOut() {
|
|
13021
13120
|
return process.env.OPENTAX_TELEMETRY === "0" || process.env.OPENTAX_TELEMETRY === "false" || process.env.DO_NOT_TRACK === "1";
|
|
@@ -13334,8 +13433,8 @@ var ZodError2 = class _ZodError extends Error {
|
|
|
13334
13433
|
constructor(issues) {
|
|
13335
13434
|
super();
|
|
13336
13435
|
this.issues = [];
|
|
13337
|
-
this.addIssue = (
|
|
13338
|
-
this.issues = [...this.issues,
|
|
13436
|
+
this.addIssue = (sub3) => {
|
|
13437
|
+
this.issues = [...this.issues, sub3];
|
|
13339
13438
|
};
|
|
13340
13439
|
this.addIssues = (subs = []) => {
|
|
13341
13440
|
this.issues = [...this.issues, ...subs];
|
|
@@ -13402,13 +13501,13 @@ var ZodError2 = class _ZodError extends Error {
|
|
|
13402
13501
|
flatten(mapper = (issue2) => issue2.message) {
|
|
13403
13502
|
const fieldErrors = {};
|
|
13404
13503
|
const formErrors = [];
|
|
13405
|
-
for (const
|
|
13406
|
-
if (
|
|
13407
|
-
const firstEl =
|
|
13504
|
+
for (const sub3 of this.issues) {
|
|
13505
|
+
if (sub3.path.length > 0) {
|
|
13506
|
+
const firstEl = sub3.path[0];
|
|
13408
13507
|
fieldErrors[firstEl] = fieldErrors[firstEl] || [];
|
|
13409
|
-
fieldErrors[firstEl].push(mapper(
|
|
13508
|
+
fieldErrors[firstEl].push(mapper(sub3));
|
|
13410
13509
|
} else {
|
|
13411
|
-
formErrors.push(mapper(
|
|
13510
|
+
formErrors.push(mapper(sub3));
|
|
13412
13511
|
}
|
|
13413
13512
|
}
|
|
13414
13513
|
return { formErrors, fieldErrors };
|
|
@@ -21635,12 +21734,12 @@ function loadCorpus(input) {
|
|
|
21635
21734
|
const byId = /* @__PURE__ */ new Map();
|
|
21636
21735
|
const factById2 = /* @__PURE__ */ new Map();
|
|
21637
21736
|
const overriders = /* @__PURE__ */ new Map();
|
|
21638
|
-
for (const
|
|
21639
|
-
if (factById2.has(
|
|
21640
|
-
issues.push(`duplicate fact id "${
|
|
21641
|
-
factById2.set(
|
|
21642
|
-
if (
|
|
21643
|
-
issues.push(`enum fact "${
|
|
21737
|
+
for (const fact44 of input.facts) {
|
|
21738
|
+
if (factById2.has(fact44.id))
|
|
21739
|
+
issues.push(`duplicate fact id "${fact44.id}"`);
|
|
21740
|
+
factById2.set(fact44.id, fact44);
|
|
21741
|
+
if (fact44.type === "enum" && !fact44.enumValues?.length) {
|
|
21742
|
+
issues.push(`enum fact "${fact44.id}" must declare enumValues`);
|
|
21644
21743
|
}
|
|
21645
21744
|
}
|
|
21646
21745
|
const seenVersions = /* @__PURE__ */ new Set();
|
|
@@ -22495,6 +22594,90 @@ function lookupParameters(corpus2, query, asOf) {
|
|
|
22495
22594
|
hits.sort((a, b) => b.score - a.score || a.ruleId.localeCompare(b.ruleId));
|
|
22496
22595
|
return hits.slice(0, 5);
|
|
22497
22596
|
}
|
|
22597
|
+
var SNIPPET_LEN = 280;
|
|
22598
|
+
function makeSnippet(excerpt, tokens) {
|
|
22599
|
+
if (excerpt.length <= SNIPPET_LEN)
|
|
22600
|
+
return excerpt;
|
|
22601
|
+
const lower = excerpt.toLowerCase();
|
|
22602
|
+
let at = -1;
|
|
22603
|
+
for (const t of tokens) {
|
|
22604
|
+
const i = lower.indexOf(t);
|
|
22605
|
+
if (i !== -1 && (at === -1 || i < at))
|
|
22606
|
+
at = i;
|
|
22607
|
+
}
|
|
22608
|
+
const start = Math.max(0, Math.min(at === -1 ? 0 : at - 60, excerpt.length - SNIPPET_LEN));
|
|
22609
|
+
const cut = excerpt.slice(start, start + SNIPPET_LEN);
|
|
22610
|
+
return `${start > 0 ? "\u2026" : ""}${cut}${start + SNIPPET_LEN < excerpt.length ? "\u2026" : ""}`;
|
|
22611
|
+
}
|
|
22612
|
+
function searchRules(corpus2, query, asOf, limit = 8) {
|
|
22613
|
+
const allTokens = tokenize(query);
|
|
22614
|
+
if (allTokens.length === 0)
|
|
22615
|
+
return [];
|
|
22616
|
+
const haystacks = corpus2.rules.map((rule) => {
|
|
22617
|
+
const paramWords = Object.keys(rule.parameters ?? {}).map((n) => n.replace(/([A-Z])/g, " $1").toLowerCase()).join(" ");
|
|
22618
|
+
return {
|
|
22619
|
+
rule,
|
|
22620
|
+
// "_" is a regex word character — split snake_case so \b-anchored
|
|
22621
|
+
// tokens ("tax") still reach id segments ("kiddie_tax").
|
|
22622
|
+
id: rule.id.toLowerCase().replace(/[._]/g, " "),
|
|
22623
|
+
title: rule.title.toLowerCase(),
|
|
22624
|
+
meta: `${paramWords} ${JURISDICTION_NAMES[rule.jurisdiction] ?? ""}`.toLowerCase(),
|
|
22625
|
+
weak: `${rule.citation.source} ${rule.citation.section} ${rule.citation.excerpt ?? ""}`.toLowerCase()
|
|
22626
|
+
};
|
|
22627
|
+
});
|
|
22628
|
+
const matchers = new Map(allTokens.map((t) => [t, new RegExp(`\\b${t}`)]));
|
|
22629
|
+
const has = (hay, t) => matchers.get(t).test(hay);
|
|
22630
|
+
const df = (t) => haystacks.filter((h) => has(h.id, t) || has(h.title, t) || has(h.meta, t) || has(h.weak, t)).length;
|
|
22631
|
+
const rare = allTokens.filter((t) => df(t) <= haystacks.length * 0.35);
|
|
22632
|
+
const tokens = rare.length > 0 ? rare : allTokens;
|
|
22633
|
+
const boost = new Map(tokens.map((t) => [t, df(t) <= haystacks.length * 0.05 ? 2 : 1]));
|
|
22634
|
+
const minScore = tokens.length >= 2 ? 2 : 1;
|
|
22635
|
+
const hits = [];
|
|
22636
|
+
for (const { rule, id, title, meta, weak } of haystacks) {
|
|
22637
|
+
if (asOf && !(rule.effectiveFrom <= asOf && (rule.effectiveTo === void 0 || asOf < rule.effectiveTo))) {
|
|
22638
|
+
continue;
|
|
22639
|
+
}
|
|
22640
|
+
let raw = 0;
|
|
22641
|
+
let score = 0;
|
|
22642
|
+
let matched = 0;
|
|
22643
|
+
let sawStrong = false;
|
|
22644
|
+
for (const t of tokens) {
|
|
22645
|
+
let s = 0;
|
|
22646
|
+
if (has(id, t))
|
|
22647
|
+
s += 2;
|
|
22648
|
+
if (has(title, t))
|
|
22649
|
+
s += 2;
|
|
22650
|
+
if (has(meta, t))
|
|
22651
|
+
s += 1;
|
|
22652
|
+
if (s > 0)
|
|
22653
|
+
sawStrong = true;
|
|
22654
|
+
else if (has(weak, t))
|
|
22655
|
+
s = 1;
|
|
22656
|
+
if (s > 0)
|
|
22657
|
+
matched += 1;
|
|
22658
|
+
raw += s;
|
|
22659
|
+
score += s * (boost.get(t) ?? 1);
|
|
22660
|
+
}
|
|
22661
|
+
if (raw < minScore || matched < Math.ceil(tokens.length / 2))
|
|
22662
|
+
continue;
|
|
22663
|
+
if (tokens.length >= 2 && !sawStrong)
|
|
22664
|
+
continue;
|
|
22665
|
+
hits.push({
|
|
22666
|
+
ruleId: rule.id,
|
|
22667
|
+
version: rule.version,
|
|
22668
|
+
title: rule.title,
|
|
22669
|
+
jurisdiction: rule.jurisdiction,
|
|
22670
|
+
citation: rule.citation,
|
|
22671
|
+
effectiveFrom: rule.effectiveFrom,
|
|
22672
|
+
effectiveTo: rule.effectiveTo,
|
|
22673
|
+
snippet: makeSnippet(rule.citation.excerpt ?? "", tokens),
|
|
22674
|
+
hasParameters: Object.keys(rule.parameters ?? {}).length > 0,
|
|
22675
|
+
score
|
|
22676
|
+
});
|
|
22677
|
+
}
|
|
22678
|
+
hits.sort((a, b) => b.score - a.score || a.ruleId.localeCompare(b.ruleId));
|
|
22679
|
+
return hits.slice(0, Math.max(1, Math.min(limit, 20)));
|
|
22680
|
+
}
|
|
22498
22681
|
function factCheck(corpus2, query, claimedCents, asOf, filingStatus) {
|
|
22499
22682
|
const hits = lookupParameters(corpus2, query, asOf);
|
|
22500
22683
|
if (hits.length === 0) {
|
|
@@ -22796,6 +22979,119 @@ function composeNY(input, evalStateTax, notes) {
|
|
|
22796
22979
|
};
|
|
22797
22980
|
}
|
|
22798
22981
|
|
|
22982
|
+
// ../compose/dist/pa.js
|
|
22983
|
+
var classLine = (p, s) => p > 0n || s > 0n ? max02(p) + max02(s) : p + s;
|
|
22984
|
+
function composePA(input, evalStateTax, notes) {
|
|
22985
|
+
const box16 = input.paGrossCompensation !== void 0 ? c(input.paGrossCompensation) : c(input.wages);
|
|
22986
|
+
if (input.paGrossCompensation === void 0 && input.wages !== void 0) {
|
|
22987
|
+
notes.push("PA line 1a: W-2 Box 16 total not transcribed \u2014 federal Box 1 wages used (401(k)/elective deferrals are PA-taxable; pass paGrossCompensation when Box 16 differs)");
|
|
22988
|
+
}
|
|
22989
|
+
const l1a = rd(box16);
|
|
22990
|
+
const l1b = rd(c(input.paUnreimbursedExpenses));
|
|
22991
|
+
const l1c = max02(l1a - l1b);
|
|
22992
|
+
const l2 = rd(c(input.paInterest));
|
|
22993
|
+
const l3 = rd(c(input.paDividends));
|
|
22994
|
+
const bizP = rd(c(input.paBusinessNet));
|
|
22995
|
+
const bizS = rd(c(input.paSpouseBusinessNet));
|
|
22996
|
+
const propP = rd(c(input.paPropertyNet));
|
|
22997
|
+
const propS = rd(c(input.paSpousePropertyNet));
|
|
22998
|
+
const rentP = rd(c(input.paRentRoyaltyNet));
|
|
22999
|
+
const rentS = rd(c(input.paSpouseRentRoyaltyNet));
|
|
23000
|
+
const l4 = classLine(bizP, bizS);
|
|
23001
|
+
const l5 = classLine(propP, propS);
|
|
23002
|
+
const l6 = classLine(rentP, rentS);
|
|
23003
|
+
const l7 = rd(c(input.paEstateTrust));
|
|
23004
|
+
const l8 = rd(c(input.paGambling));
|
|
23005
|
+
const extra = {
|
|
23006
|
+
wages: box16,
|
|
23007
|
+
paUnreimbursedBusinessExpenses: c(input.paUnreimbursedExpenses),
|
|
23008
|
+
paInterestIncome: c(input.paInterest),
|
|
23009
|
+
paDividendIncome: c(input.paDividends),
|
|
23010
|
+
paBusinessNetIncome: c(input.paBusinessNet),
|
|
23011
|
+
paSpouseBusinessNetIncome: c(input.paSpouseBusinessNet),
|
|
23012
|
+
paPropertyGainNet: c(input.paPropertyNet),
|
|
23013
|
+
paSpousePropertyGainNet: c(input.paSpousePropertyNet),
|
|
23014
|
+
paRentRoyaltyNet: c(input.paRentRoyaltyNet),
|
|
23015
|
+
paSpouseRentRoyaltyNet: c(input.paSpouseRentRoyaltyNet),
|
|
23016
|
+
paEstateTrustIncome: c(input.paEstateTrust),
|
|
23017
|
+
paGamblingWinnings: c(input.paGambling),
|
|
23018
|
+
studentLoanInterest: c(input.paStudentLoanInterest),
|
|
23019
|
+
pa529Contributions: c(input.pa529Contributions),
|
|
23020
|
+
paAbleContributions: c(input.paAbleContributions),
|
|
23021
|
+
paMsaHsaContributions: c(input.paMsaHsaContributions)
|
|
23022
|
+
};
|
|
23023
|
+
const l9 = rd(evalStateTax("us.pa.taxable_income", 0n, extra));
|
|
23024
|
+
const l10 = rd(evalStateTax("us.pa.other_deductions", 0n, extra));
|
|
23025
|
+
const l11 = max02(l9 - l10);
|
|
23026
|
+
const l12 = rd(evalStateTax("us.pa.income_tax", 0n, extra));
|
|
23027
|
+
const l13 = rd(c(input.stateWithholding));
|
|
23028
|
+
const l14 = rd(c(input.priorYearOverpaymentCredited));
|
|
23029
|
+
const l15 = rd(c(input.estimatedPayments));
|
|
23030
|
+
const l16 = rd(c(input.extensionPayment));
|
|
23031
|
+
const l17 = rd(c(input.paNrk1Withholding));
|
|
23032
|
+
const l18 = l14 + l15 + l16 + l17;
|
|
23033
|
+
const spDeps = input.paSpDependentChildren ?? 0;
|
|
23034
|
+
const l21 = rd(evalStateTax("us.pa.tax_forgiveness", 0n, {
|
|
23035
|
+
...extra,
|
|
23036
|
+
paSpDependentChildren: spDeps,
|
|
23037
|
+
paEligibilityIncomeAddbacks: c(input.paEligibilityAddbacks),
|
|
23038
|
+
paResidentCredit: c(input.paResidentCredit)
|
|
23039
|
+
}));
|
|
23040
|
+
const l20 = l9 + rd(c(input.paEligibilityAddbacks));
|
|
23041
|
+
const l22 = rd(c(input.paResidentCredit));
|
|
23042
|
+
const l23 = rd(c(input.paScheduleDcCredit)) + rd(c(input.paScheduleOcCredits));
|
|
23043
|
+
const l24 = l13 + l18 + l21 + l22 + l23;
|
|
23044
|
+
const l25 = rd(c(input.useTax));
|
|
23045
|
+
const l26 = max02(l12 + l25 - l24);
|
|
23046
|
+
const l27 = rd(c(input.paPenaltiesInterest));
|
|
23047
|
+
const l28 = l26 + l27;
|
|
23048
|
+
const l29 = max02(l24 - l12 - l25 - l27);
|
|
23049
|
+
const fedEITC = c(input.federalEITC);
|
|
23050
|
+
if (fedEITC > 0n) {
|
|
23051
|
+
const wptc = min2(rd(fedEITC * 10n / 100n), 80500n);
|
|
23052
|
+
notes.push(`PA Working Pennsylvanians Tax Credit ${fmtD(wptc)} (10% of federal EITC, max $805, refundable) \u2014 NO line on the printed 2025 PA-40; DOR applies it in processing from the attached federal return`);
|
|
23053
|
+
}
|
|
23054
|
+
notes.push("PA local earned income tax (Act 32) is filed separately with the local collector \u2014 out of scope for the PA-40");
|
|
23055
|
+
if (l29 > 0n && l29 <= 100n)
|
|
23056
|
+
notes.push("PA does not issue refunds of $1.00 or less");
|
|
23057
|
+
if (l28 > 0n && l28 <= 100n)
|
|
23058
|
+
notes.push("PA does not require payment of $1.00 or less");
|
|
23059
|
+
return {
|
|
23060
|
+
"1a_gross_compensation": fmtD(l1a),
|
|
23061
|
+
"1b_unreimbursed_expenses": fmtD(l1b),
|
|
23062
|
+
"1c_net_compensation": fmtD(l1c),
|
|
23063
|
+
"2_interest": fmtD(l2),
|
|
23064
|
+
"3_dividends": fmtD(l3),
|
|
23065
|
+
"4_business_net": fmtD(l4),
|
|
23066
|
+
"5_property_net": fmtD(l5),
|
|
23067
|
+
"6_rent_royalty_net": fmtD(l6),
|
|
23068
|
+
"7_estate_trust": fmtD(l7),
|
|
23069
|
+
"8_gambling": fmtD(l8),
|
|
23070
|
+
"9_total_taxable_income": fmtD(l9),
|
|
23071
|
+
"10_other_deductions": fmtD(l10),
|
|
23072
|
+
"11_adjusted_taxable_income": fmtD(l11),
|
|
23073
|
+
"12_tax": fmtD(l12),
|
|
23074
|
+
"13_withholding": fmtD(l13),
|
|
23075
|
+
"14_prior_year_credit": fmtD(l14),
|
|
23076
|
+
"15_estimated_payments": fmtD(l15),
|
|
23077
|
+
"16_extension_payment": fmtD(l16),
|
|
23078
|
+
"17_nonresident_withholding": fmtD(l17),
|
|
23079
|
+
"18_total_estimated_and_credits": fmtD(l18),
|
|
23080
|
+
"19b_sp_dependents": String(spDeps),
|
|
23081
|
+
"20_eligibility_income": fmtD(l20),
|
|
23082
|
+
"21_tax_forgiveness": fmtD(l21),
|
|
23083
|
+
"22_resident_credit": fmtD(l22),
|
|
23084
|
+
"23_other_credits": fmtD(l23),
|
|
23085
|
+
"24_total_payments_credits": fmtD(l24),
|
|
23086
|
+
"25_use_tax": fmtD(l25),
|
|
23087
|
+
"26_tax_due": fmtD(l26),
|
|
23088
|
+
"27_penalties_interest": fmtD(l27),
|
|
23089
|
+
"28_total_due": fmtD(l28),
|
|
23090
|
+
"29_overpayment": fmtD(l29),
|
|
23091
|
+
"30_refund": fmtD(l29)
|
|
23092
|
+
};
|
|
23093
|
+
}
|
|
23094
|
+
|
|
22799
23095
|
// ../compose/dist/va-worksheets.js
|
|
22800
23096
|
var VA_STD_DEDUCTION_JOINT = 1750000n;
|
|
22801
23097
|
var VA_STD_DEDUCTION_OTHER = 875000n;
|
|
@@ -23012,11 +23308,11 @@ function composeVA(input, evalStateTax, notes) {
|
|
|
23012
23308
|
// ../compose/dist/shape.js
|
|
23013
23309
|
var usd = external_exports.number().finite();
|
|
23014
23310
|
var shared = {
|
|
23015
|
-
jurisdiction: external_exports.enum(["il", "va", "ca", "ny"]),
|
|
23311
|
+
jurisdiction: external_exports.enum(["il", "va", "ca", "ny", "pa"]),
|
|
23016
23312
|
filingStatus: external_exports.enum(["single", "mfj", "mfs", "hoh", "qss"]).optional().describe("REQUIRED in practice: the federal filing status \u2014 drives the state bracket schedule, standard deduction column, and exemption structure. The filingJoint/filingHoh/filingHohOrQss booleans are legacy aliases; when filingStatus is present it wins."),
|
|
23017
23313
|
// federal substrate values, computed by compute_return in the SAME session
|
|
23018
23314
|
// (pass them verbatim — whole dollars)
|
|
23019
|
-
federalAGI: usd.describe("federal Form 1040 line 11 (from compute_return, verbatim)"),
|
|
23315
|
+
federalAGI: usd.optional().describe("federal Form 1040 line 11 (from compute_return, verbatim). REQUIRED for il/va/ca/ny \u2014 the composer refuses without it. NOT used by PA (class-based: pass the pa* class fields instead)."),
|
|
23020
23316
|
federalEITC: usd.optional().describe("federal EIC, line 27a (from compute_return)"),
|
|
23021
23317
|
wages: usd.optional().describe("federal line 1a wages (NY IT-201 line 1)"),
|
|
23022
23318
|
additions: usd.optional().describe("total state additions to federal AGI (e.g. NY 414(h) A-104 + IRC-125 A-101; VA Schedule ADJ line 2 codes). GATE RULE: coded addition/subtraction line-item arrays sitting under a false 'do you have additions/subtractions' boolean are inactive template rows (especially $1-$4 placeholder amounts) \u2014 transcribe $0 for them and disclose; the gate controls for these arrays"),
|
|
@@ -23024,7 +23320,7 @@ var shared = {
|
|
|
23024
23320
|
exemptions: external_exports.number().int().optional().describe("personal + dependent exemption COUNT (self + spouse + dependents)"),
|
|
23025
23321
|
ageOrBlindBoxes: external_exports.number().int().optional().describe("count of age-65+/blind boxes checked (taxpayer/spouse, per box)"),
|
|
23026
23322
|
dependents: external_exports.number().int().optional().describe("dependent count (CA dependent exemption credits; NY $1,000 exemptions)"),
|
|
23027
|
-
stateWithholding: usd.optional().describe("state income tax withheld (IL line 25 / VA 19a / CA 71 / NY 72). CONVENTIONS: IL line 25 sums state withholding from EVERY document (W-2s + all 1099s). NY line 72 = W-2 box 17 NYS withholding PLUS NY-coded state withholding from 1099s whose PAYER has an in-state (NY) address; NY-coded withholding printed by an OUT-OF-STATE-addressed payer is NOT included
|
|
23323
|
+
stateWithholding: usd.optional().describe("state income tax withheld (IL line 25 / VA 19a / CA 71 / NY 72). CONVENTIONS: IL line 25 sums state withholding from EVERY document (W-2s + all 1099s). NY line 72 = W-2 box 17 NYS withholding PLUS NY-coded state withholding from 1099s whose PAYER has an in-state (NY) address; NY-coded withholding printed by an OUT-OF-STATE-addressed payer is NOT included; disclose any excluded amount in notes. VA 19a = the PRIMARY taxpayer's withholding from EVERY document type (W-2, 1099, VK-1 \u2014 Form 760 line 19 instructions name all three; the payer's address does NOT matter for VA, unlike NY); a jointly-issued document's state withholding splits 50/50 between 19a/19b with the odd dollar to the primary."),
|
|
23028
23324
|
spouseStateWithholding: usd.optional().describe("VA line 19b spouse withholding (spouse's own W-2/1099/VK-1 boxes + spouse's half of jointly-issued documents' withholding, odd dollar to the primary)"),
|
|
23029
23325
|
cityWithholding: usd.optional().describe("NY line 73 NYC withholding"),
|
|
23030
23326
|
yonkersWithholding: usd.optional().describe("NY line 74 Yonkers withholding (W-2 box 19 with a Yonkers locality)"),
|
|
@@ -23097,7 +23393,38 @@ var ny = {
|
|
|
23097
23393
|
nycHouseholdCredit: usd.optional().describe("NYC household credit from table 5"),
|
|
23098
23394
|
yonkersSurcharge: usd.optional().describe("us.ny.yonkers_surcharge result (pass the oracle target's answer) \u2014 16.75% of the Yonkers worksheet's netted base (nyYonkersBase). Added into line 62's total and printed on its own line (IT-201 LINE 55, not 54 \u2014 line 54 is MCTMT) when nonzero.")
|
|
23099
23395
|
};
|
|
23100
|
-
var
|
|
23396
|
+
var pa = {
|
|
23397
|
+
// PA is CLASS-BASED (eight classes, 72 P.S. § 7303) — federalAGI is NOT the
|
|
23398
|
+
// PA base. Transcribe the class amounts below; the composer runs the corpus
|
|
23399
|
+
// targets (class netting, Schedule O, 3.07% tax, Schedule SP) itself.
|
|
23400
|
+
paGrossCompensation: usd.optional().describe("PA-40 line 1a: W-2 BOX 16 total (NOT Box 1 \u2014 401(k)/elective deferrals are PA-taxable; eligible retirement distributions are exempt and excluded). Falls back to the shared wages input when omitted (composer discloses). Include taxable early-distribution amounts under the cost-recovery method."),
|
|
23401
|
+
paUnreimbursedExpenses: usd.optional().describe("PA-40 line 1b: Schedule UE unreimbursed employee business expenses (a compensation-class expense, never a line-10 deduction)"),
|
|
23402
|
+
paInterest: usd.optional().describe("PA-40 line 2: PA-taxable interest (gross class \u2014 no expenses; includes commercial-annuity interest taxable as PA interest)"),
|
|
23403
|
+
paDividends: usd.optional().describe("PA-40 line 3: PA-taxable dividends INCLUDING mutual-fund capital-gain distributions (PA classifies them as dividends, not gains)"),
|
|
23404
|
+
paBusinessNet: usd.optional().describe("PA-40 line 4, TAXPAYER's own net business/profession/farm income or LOSS (negative allowed; within-class netting of the taxpayer's own activities only \u2014 a loss never crosses classes or spouses)"),
|
|
23405
|
+
paSpouseBusinessNet: usd.optional().describe("PA-40 line 4, SPOUSE's own net business income or loss (kept separate: PA never nets one spouse's loss against the other's income)"),
|
|
23406
|
+
paPropertyNet: usd.optional().describe("PA-40 line 5, taxpayer's own net gain/loss from sale/exchange/disposition of property (negative allowed; no carryover)"),
|
|
23407
|
+
paSpousePropertyNet: usd.optional().describe("PA-40 line 5, spouse's own net property gain/loss"),
|
|
23408
|
+
paRentRoyaltyNet: usd.optional().describe("PA-40 line 6, taxpayer's own net rents/royalties/patents/copyrights (short-term rentals under 30 days are BUSINESS income, line 4)"),
|
|
23409
|
+
paSpouseRentRoyaltyNet: usd.optional().describe("PA-40 line 6, spouse's own net rent/royalty amount"),
|
|
23410
|
+
paEstateTrust: usd.optional().describe("PA-40 line 7: estate/trust income (PA Schedule J; an estate or trust cannot distribute a loss \u2014 never negative)"),
|
|
23411
|
+
paGambling: usd.optional().describe("PA-40 line 8: gambling and lottery winnings net of wager costs (noncash PA Lottery prizes exempt; cash prizes taxable)"),
|
|
23412
|
+
paStudentLoanInterest: usd.optional().describe("Schedule O code S: student loan interest PAID (new deduction for 2025; the composer caps at $2,500 \u2014 pass the uncapped amount)"),
|
|
23413
|
+
pa529Contributions: usd.optional().describe("Schedule O code T: \xA7 529 contributions, ALREADY capped at $19,000 per beneficiary per taxpayer-spouse (2025); no deduction for rollovers/beneficiary changes"),
|
|
23414
|
+
paAbleContributions: usd.optional().describe("Schedule O code A: PA ABLE contributions, capped at the federal gift-tax exclusion ($19,000 for 2025)"),
|
|
23415
|
+
paMsaHsaContributions: usd.optional().describe("Schedule O codes M/H: MSA + HSA contributions at the federally-allowed amounts"),
|
|
23416
|
+
paSpDependentChildren: external_exports.number().int().optional().describe("Schedule SP dependent CHILDREN count (child/stepchild/adopted; grandchild of a grandparent; foster child of a foster parent \u2014 never other relatives) claimable as federal dependents; each adds $9,500 to the Tax Forgiveness eligibility-income threshold"),
|
|
23417
|
+
paEligibilityAddbacks: usd.optional().describe("Schedule SP Section III nontaxable add-backs (gifts, inheritances, insurance proceeds, non-PA income, nontaxable military pay, excluded home-sale gain, educational assistance, outside cash support). NOT Social Security, eligible retirement benefits, child support, or workers' comp."),
|
|
23418
|
+
paResidentCredit: usd.optional().describe("PA-40 line 22: resident credit for tax paid other states (Schedule G-L; not for reciprocal-state compensation: IN/MD/NJ/OH/VA/WV). Subtracts BEFORE Tax Forgiveness \u2014 the composer handles the ordering."),
|
|
23419
|
+
paScheduleDcCredit: usd.optional().describe("PA-40 line 23 component: the Child and Dependent Care Enhancement credit \u2014 pass us.pa.cdcc's computed answer (= 100% of the federal Form 2441 line 9a tentative credit; refundable)"),
|
|
23420
|
+
paScheduleOcCredits: usd.optional().describe("PA-40 line 23 component: Schedule OC restricted credits total (transcribed; no oracle target)"),
|
|
23421
|
+
paNrk1Withholding: usd.optional().describe("PA-40 line 17: nonresident tax withheld from PA Schedule(s) NRK-1"),
|
|
23422
|
+
paPenaltiesInterest: usd.optional().describe("PA-40 line 27: penalties and interest incl. estimated-underpayment penalty (REV-1630)")
|
|
23423
|
+
// PA line 13 withholding uses the shared stateWithholding input: sum state
|
|
23424
|
+
// tax withheld from EVERY document type (W-2 box 17, W-2G box 15, 1099-R
|
|
23425
|
+
// box 14, 1099-MISC box 15, 1099-NEC box 5).
|
|
23426
|
+
};
|
|
23427
|
+
var stateReturnShape = { ...shared, ...il, ...va, ...ca, ...ny, ...pa };
|
|
23101
23428
|
|
|
23102
23429
|
// ../compose/dist/index.js
|
|
23103
23430
|
function makeStateTaxEvaluator(runTarget, input) {
|
|
@@ -23112,7 +23439,7 @@ function makeStateTaxEvaluator(runTarget, input) {
|
|
|
23112
23439
|
spouseItemizes: { type: "bool", value: false }
|
|
23113
23440
|
};
|
|
23114
23441
|
for (const [k, v] of Object.entries(extraFacts ?? {})) {
|
|
23115
|
-
facts2[k] = typeof v === "boolean" ? { type: "bool", value: v } : { type: "money", value: String(v) };
|
|
23442
|
+
facts2[k] = typeof v === "boolean" ? { type: "bool", value: v } : typeof v === "number" ? { type: "int", value: String(v) } : { type: "money", value: String(v) };
|
|
23116
23443
|
}
|
|
23117
23444
|
return runTarget(facts2, target);
|
|
23118
23445
|
};
|
|
@@ -23126,12 +23453,17 @@ function composeStateReturn(input, evalStateTax) {
|
|
|
23126
23453
|
input.filingHohOrQss = fs === "hoh" || fs === "qss" || fs === "mfj";
|
|
23127
23454
|
}
|
|
23128
23455
|
const j = input.jurisdiction;
|
|
23456
|
+
if (j !== "pa" && typeof input.federalAGI !== "number") {
|
|
23457
|
+
throw new Error("federalAGI is required for il/va/ca/ny state returns \u2014 run compute_return first and pass Form 1040 line 11 verbatim");
|
|
23458
|
+
}
|
|
23129
23459
|
if (j === "il")
|
|
23130
23460
|
return { lines: composeIL(input, evalStateTax, notes), notes };
|
|
23131
23461
|
if (j === "va")
|
|
23132
23462
|
return { lines: composeVA(input, evalStateTax, notes), notes };
|
|
23133
23463
|
if (j === "ca")
|
|
23134
23464
|
return { lines: composeCA(input, evalStateTax, notes), notes };
|
|
23465
|
+
if (j === "pa")
|
|
23466
|
+
return { lines: composePA(input, evalStateTax, notes), notes };
|
|
23135
23467
|
return { lines: composeNY(input, evalStateTax, notes), notes };
|
|
23136
23468
|
}
|
|
23137
23469
|
|
|
@@ -23682,12 +24014,32 @@ var facts = [
|
|
|
23682
24014
|
id: "netCapitalLoss",
|
|
23683
24015
|
type: "money",
|
|
23684
24016
|
min: "0",
|
|
23685
|
-
description: "Net capital LOSS for the year from Schedule D netting, entered as a positive number (26 U.S.C. \xA7 1211(b): up to $3,000/$1,500 MFS against ordinary income; remainder carries over). Mutually exclusive with
|
|
24017
|
+
description: "Net capital LOSS for the year from Schedule D netting, entered as a positive number (26 U.S.C. \xA7 1211(b): up to $3,000/$1,500 MFS against ordinary income; remainder carries over). Mutually exclusive with the gain facts AND with the per-bucket loss facts (shortTermCapitalLoss/longTermCapitalLoss) \u2014 this is the single OVERALL net result; for mixed years (gain in one bucket, loss in the other) use the per-bucket facts and the engine performs the \xA7 1222 netting. In dollars.",
|
|
23686
24018
|
default: {
|
|
23687
24019
|
value: "0",
|
|
23688
24020
|
rationale: "Assumed no net capital loss absent contrary input"
|
|
23689
24021
|
}
|
|
23690
24022
|
},
|
|
24023
|
+
{
|
|
24024
|
+
id: "shortTermCapitalLoss",
|
|
24025
|
+
type: "money",
|
|
24026
|
+
min: "0",
|
|
24027
|
+
description: "Net short-term capital LOSS for the year (Schedule D line 7 when negative, entered positive). The ST bucket's single NET result \u2014 mutually exclusive with shortTermCapitalGains and with netCapitalLoss. Combine with longTermCapitalGains for mixed years: the engine performs the \xA7 1222 cross-netting (a net ST loss first reduces net LT gain per \xA7 1222(11); any overall loss is capped by \xA7 1211(b)). In dollars.",
|
|
24028
|
+
default: {
|
|
24029
|
+
value: "0",
|
|
24030
|
+
rationale: "Assumed no net short-term capital loss absent contrary input"
|
|
24031
|
+
}
|
|
24032
|
+
},
|
|
24033
|
+
{
|
|
24034
|
+
id: "longTermCapitalLoss",
|
|
24035
|
+
type: "money",
|
|
24036
|
+
min: "0",
|
|
24037
|
+
description: "Net long-term capital LOSS for the year (Schedule D line 15 when negative, entered positive). The LT bucket's single NET result \u2014 mutually exclusive with longTermCapitalGains and with netCapitalLoss. Combine with shortTermCapitalGains for mixed years: the engine performs the \xA7 1222 cross-netting (a net LT loss reduces the ordinary-rate net ST gain per \xA7 1222(9); any overall loss is capped by \xA7 1211(b)). In dollars.",
|
|
24038
|
+
default: {
|
|
24039
|
+
value: "0",
|
|
24040
|
+
rationale: "Assumed no net long-term capital loss absent contrary input"
|
|
24041
|
+
}
|
|
24042
|
+
},
|
|
23691
24043
|
{
|
|
23692
24044
|
id: "qualifiedDividends",
|
|
23693
24045
|
type: "money",
|
|
@@ -23722,7 +24074,7 @@ var facts = [
|
|
|
23722
24074
|
id: "selfEmploymentNetProfit",
|
|
23723
24075
|
type: "money",
|
|
23724
24076
|
min: "0",
|
|
23725
|
-
description: "Net profit from self-employment (Schedule C line 31: gross receipts minus ACTUAL Schedule C expenses). Do NOT subtract intake fields like 'W-2 Wages Paid' that exist for the \xA7 199A wage limit \u2014 those belong in qbiW2Wages, not here. A loss year goes in scheduleCNetLoss instead (leave this at 0). GROSS-RECEIPTS COMPOSITION
|
|
24077
|
+
description: "Net profit from self-employment (Schedule C line 31: gross receipts minus ACTUAL Schedule C expenses). Do NOT subtract intake fields like 'W-2 Wages Paid' that exist for the \xA7 199A wage limit \u2014 those belong in qbiW2Wages, not here. A loss year goes in scheduleCNetLoss instead (leave this at 0). GROSS-RECEIPTS COMPOSITION: the intake's own gross-receipts field PLUS every 1099-NEC and 1099-K nested under (or issued to) the business PLUS any 1099-MISC box 3 'other income' issued to a filer who operates a Schedule C business (a payer's MISC box-3 payment to an active sole proprietor is business income, not Schedule 1 line 8z) \u2014 1099-MISC box 1 rents belong on Schedule E, never here. A 1099 whose recipient TIN matches NEITHER spouse is not this return's income: exclude it and disclose. PLACEHOLDER/TEMPLATE DOCUMENTS: a 1099 whose payer block is an unfilled template \u2014 no payer name, address line 'City, AK 99999' or a similar all-9s placeholder \u2014 is an inactive template document, not income: exclude every box on it (income AND withholding) and disclose; a 1099 from a NAMED payer at a real-looking address is live data even when the structured intake has no matching entry. In dollars.",
|
|
23726
24078
|
default: {
|
|
23727
24079
|
value: "0",
|
|
23728
24080
|
rationale: "Assumed no self-employment income absent contrary input"
|
|
@@ -23817,7 +24169,7 @@ var facts = [
|
|
|
23817
24169
|
id: "sepContribution",
|
|
23818
24170
|
type: "money",
|
|
23819
24171
|
min: "0",
|
|
23820
|
-
description: "Employer contribution to the self-employed taxpayer's own SEP-IRA or solo-401(k) profit-sharing (\xA7 404(h): up to 20% of net SE earnings after \xBD SE tax, capped at the \xA7 415(c) limit \u2014 $70,000 for 2025, $72,000 for 2026). In dollars. DEDUCTIBLE-LIMIT CONVENTION
|
|
24172
|
+
description: "Employer contribution to the self-employed taxpayer's own SEP-IRA or solo-401(k) profit-sharing (\xA7 404(h): up to 20% of net SE earnings after \xBD SE tax, capped at the \xA7 415(c) limit \u2014 $70,000 for 2025, $72,000 for 2026). In dollars. DEDUCTIBLE-LIMIT CONVENTION: when a documented SEP/Keogh contribution exceeds the limit, the deducted amount = 20% \xD7 (0.9235 \xD7 total Schedule C net profit \u2212 the \xBD-SE-tax deduction), rounded half-up \u2014 the \xA7 401(c)(2)/\xA7 1402(a)(12) reading that applies BOTH the 92.35% factor AND the \xBD-SE subtraction. (Note: Pub. 560's worksheet applies only the \xBD-SE subtraction; this engine follows the statutory \xA7 1402(a)(12) reduction.) Pass the documented contribution and apply this cap when self-computing the Schedule 1 line 16 amount.",
|
|
23821
24173
|
default: {
|
|
23822
24174
|
value: "0",
|
|
23823
24175
|
rationale: "Assumed no self-employed retirement contribution absent contrary input"
|
|
@@ -24036,6 +24388,109 @@ var facts = [
|
|
|
24036
24388
|
rationale: "Assumed no non-QBI Schedule E income absent contrary input"
|
|
24037
24389
|
}
|
|
24038
24390
|
},
|
|
24391
|
+
{
|
|
24392
|
+
id: "pensionGrossPayments",
|
|
24393
|
+
type: "money",
|
|
24394
|
+
min: "0",
|
|
24395
|
+
description: "TOTAL pension/annuity payments received this year from a qualified-plan annuity with after-tax cost basis (1099-R box 1) \u2014 the \xA7 72(d) Simplified Method computes the taxable part. Use taxablePensionsAndAnnuities instead (or additionally, for other pensions) when the taxable amount is already known. In dollars.",
|
|
24396
|
+
default: {
|
|
24397
|
+
value: "0",
|
|
24398
|
+
rationale: "Assumed no simplified-method annuity absent contrary input"
|
|
24399
|
+
}
|
|
24400
|
+
},
|
|
24401
|
+
{
|
|
24402
|
+
id: "pensionCostBasis",
|
|
24403
|
+
type: "money",
|
|
24404
|
+
min: "0",
|
|
24405
|
+
description: "Investment in the contract as of the annuity starting date \u2014 employee after-tax contributions (1099-R box 9b). The \xA7 72(d)(1)(B) monthly exclusion is this divided by the anticipated-payments table number; lifetime recovery is capped at this amount. In dollars.",
|
|
24406
|
+
default: {
|
|
24407
|
+
value: "0",
|
|
24408
|
+
rationale: "Assumed zero after-tax basis (fully taxable pension) absent contrary input"
|
|
24409
|
+
}
|
|
24410
|
+
},
|
|
24411
|
+
{
|
|
24412
|
+
id: "pensionAgeAtStart",
|
|
24413
|
+
type: "int",
|
|
24414
|
+
min: "0",
|
|
24415
|
+
max: "120",
|
|
24416
|
+
description: "Primary annuitant's age at the ANNUITY STARTING DATE (not current age) \u2014 picks the \xA7 72(d)(1)(B)(iii) single-life anticipated-payments band (\u226455: 360, 56-60: 310, 61-65: 260, 66-70: 210, 71+: 160). Required when pensionCostBasis is set and the annuity is single-life.",
|
|
24417
|
+
default: {
|
|
24418
|
+
value: "0",
|
|
24419
|
+
rationale: "Unset; the simplified-method rule refuses rather than assume an age band"
|
|
24420
|
+
}
|
|
24421
|
+
},
|
|
24422
|
+
{
|
|
24423
|
+
id: "pensionIsJointAndSurvivor",
|
|
24424
|
+
type: "bool",
|
|
24425
|
+
description: "True for an annuity payable over more than one life with a starting date after 1997 \u2014 the \xA7 72(d)(1)(B)(iv) COMBINED-ages table applies (set pensionCombinedAgesAtStart).",
|
|
24426
|
+
default: {
|
|
24427
|
+
value: false,
|
|
24428
|
+
rationale: "Assumed a single-life annuity absent contrary input"
|
|
24429
|
+
}
|
|
24430
|
+
},
|
|
24431
|
+
{
|
|
24432
|
+
id: "pensionCombinedAgesAtStart",
|
|
24433
|
+
type: "int",
|
|
24434
|
+
min: "0",
|
|
24435
|
+
max: "240",
|
|
24436
|
+
description: "Combined ages of both annuitants at the annuity starting date, for joint-and-survivor annuities (\xA7 72(d)(1)(B)(iv): \u2264110: 410, 111-120: 360, 121-130: 310, 131-140: 260, 141+: 210).",
|
|
24437
|
+
default: {
|
|
24438
|
+
value: "0",
|
|
24439
|
+
rationale: "Unset; the simplified-method rule refuses rather than assume a band"
|
|
24440
|
+
}
|
|
24441
|
+
},
|
|
24442
|
+
{
|
|
24443
|
+
id: "pensionMonthsThisYear",
|
|
24444
|
+
type: "int",
|
|
24445
|
+
min: "1",
|
|
24446
|
+
max: "12",
|
|
24447
|
+
description: "Number of monthly annuity payments received this tax year (Simplified Method Worksheet line 4 multiplier). 12 for a full year.",
|
|
24448
|
+
default: {
|
|
24449
|
+
value: "12",
|
|
24450
|
+
rationale: "Assumed a full year of monthly payments absent contrary input"
|
|
24451
|
+
}
|
|
24452
|
+
},
|
|
24453
|
+
{
|
|
24454
|
+
id: "pensionBasisPreviouslyRecovered",
|
|
24455
|
+
type: "money",
|
|
24456
|
+
min: "0",
|
|
24457
|
+
description: "Cost basis already recovered tax-free in prior years (Simplified Method Worksheet line 6) \u2014 lifetime recovery cannot exceed pensionCostBasis for post-1986 starting dates. In dollars.",
|
|
24458
|
+
default: {
|
|
24459
|
+
value: "0",
|
|
24460
|
+
rationale: "Assumed the first year of the annuity absent contrary input"
|
|
24461
|
+
}
|
|
24462
|
+
},
|
|
24463
|
+
{
|
|
24464
|
+
id: "rentalDepreciableBasis",
|
|
24465
|
+
type: "money",
|
|
24466
|
+
min: "0",
|
|
24467
|
+
description: "Depreciable basis of residential rental BUILDING(S) \u2014 cost basis excluding land (land never depreciates, \xA7 167; Pub 527 ch. 2). Drives the \xA7 168 GDS 27.5-year straight-line mid-month depreciation (Pub 946 Table A-6). Set rentalPlacedInServiceMonth for a first-year property; leave it 0 for property in service the whole year. In dollars.",
|
|
24468
|
+
default: {
|
|
24469
|
+
value: "0",
|
|
24470
|
+
rationale: "Assumed no depreciable rental property absent contrary input"
|
|
24471
|
+
}
|
|
24472
|
+
},
|
|
24473
|
+
{
|
|
24474
|
+
id: "rentalPlacedInServiceMonth",
|
|
24475
|
+
type: "int",
|
|
24476
|
+
min: "0",
|
|
24477
|
+
max: "12",
|
|
24478
|
+
description: "Month (1-12) the residential rental property was placed in service THIS tax year \u2014 first-year depreciation uses the \xA7 168(d)(2) mid-month convention (Pub 946 Table A-6 row 1). 0 (the default) = placed in service in a PRIOR year: the steady-state 3.636% year applies.",
|
|
24479
|
+
default: {
|
|
24480
|
+
value: "0",
|
|
24481
|
+
rationale: "Assumed the property was in service before this year (steady-state 3.636% year) absent contrary input"
|
|
24482
|
+
}
|
|
24483
|
+
},
|
|
24484
|
+
{
|
|
24485
|
+
id: "rentalIncomeBeforeDepreciation",
|
|
24486
|
+
type: "money",
|
|
24487
|
+
min: "0",
|
|
24488
|
+
description: "Schedule E rental result BEFORE depreciation: rents received minus cash operating expenses (management, repairs, insurance, taxes, mortgage interest\u2026). The engine subtracts the computed \xA7 168 depreciation to reach net rental income. Use scheduleENetIncome instead when you already have the after-depreciation net; the two inputs add (separate properties). In dollars.",
|
|
24489
|
+
default: {
|
|
24490
|
+
value: "0",
|
|
24491
|
+
rationale: "Assumed no rental activity reported through the depreciation path absent contrary input"
|
|
24492
|
+
}
|
|
24493
|
+
},
|
|
24039
24494
|
{
|
|
24040
24495
|
id: "businessMilesDriven",
|
|
24041
24496
|
type: "int",
|
|
@@ -24320,7 +24775,7 @@ var facts = [
|
|
|
24320
24775
|
id: "hsaQualifiedMedicalExpenses",
|
|
24321
24776
|
type: "money",
|
|
24322
24777
|
min: "0",
|
|
24323
|
-
description: "Unreimbursed qualified medical expenses paid from HSA distributions (Form 8889 line 15) \u2014 ONLY the amount the intake EXPLICITLY attests was spent on qualified medical expenses. Absent an explicit attestation, leave this 0 and the full distribution is taxable (\xA7 223(f)) \u2014 never assume qualified use from silence
|
|
24778
|
+
description: "Unreimbursed qualified medical expenses paid from HSA distributions (Form 8889 line 15) \u2014 ONLY the amount the intake EXPLICITLY attests was spent on qualified medical expenses. Absent an explicit attestation, leave this 0 and the full distribution is taxable (\xA7 223(f)) \u2014 never assume qualified use from silence; the taxpayer bears the burden of substantiating qualified use. In dollars.",
|
|
24324
24779
|
default: { value: "0", rationale: "Assumed distributions were not for qualified medical expenses absent contrary input (conservative: taxable)" }
|
|
24325
24780
|
},
|
|
24326
24781
|
{
|
|
@@ -24381,7 +24836,7 @@ var facts = [
|
|
|
24381
24836
|
id: "iraContribution",
|
|
24382
24837
|
type: "money",
|
|
24383
24838
|
min: "0",
|
|
24384
|
-
description: "Traditional IRA contributions for the year (26 U.S.C. \xA7 219). Roth contributions are not deductible and do not belong here. In dollars. GATE-vs-DOLLAR
|
|
24839
|
+
description: "Traditional IRA contributions for the year (26 U.S.C. \xA7 219). Roth contributions are not deductible and do not belong here. In dollars. GATE-vs-DOLLAR: an intake screener like 'Made contributions to a traditional or Roth IRA = false' does NOT nullify a populated traditional-IRA dollar field \u2014 IRA contributions are a DEDUCTION item, so the documented dollar controls (transcribe it; any \xA7 219(g) phase-out still applies, including the \xA7 219(g)(4) lived-apart-all-year MFS carve-out). The boolean-gate-controls rule is for PAYMENT fields only.",
|
|
24385
24840
|
default: {
|
|
24386
24841
|
value: "0",
|
|
24387
24842
|
rationale: "Assumed no traditional IRA contributions absent contrary input"
|
|
@@ -25349,6 +25804,133 @@ var facts = [
|
|
|
25349
25804
|
type: "bool",
|
|
25350
25805
|
description: "Spouse itemizes deductions on a separate return (26 U.S.C. \xA7 63(c)(6)(A)) \u2014 if so, an MFS filer's standard deduction is zero."
|
|
25351
25806
|
// no default — only demanded for married-filing-separately evaluations
|
|
25807
|
+
},
|
|
25808
|
+
// ---- Pennsylvania deep pack (state-pa.ts) -------------------------------
|
|
25809
|
+
// PA classes are TRANSCRIBED separately from the federal facts: PA
|
|
25810
|
+
// classification diverges (capital-gain distributions are dividends,
|
|
25811
|
+
// 401(k) deferrals are taxable compensation, eligible retirement income is
|
|
25812
|
+
// exempt), so nothing here is auto-mapped from taxableInterest etc.
|
|
25813
|
+
{
|
|
25814
|
+
id: "paCompensationAdjustment",
|
|
25815
|
+
type: "money",
|
|
25816
|
+
description: "PA compensation adjustment: W-2 Box 16 total MINUS Box 1 total (PA taxes 401(k)/elective deferrals as compensation; active-duty pay outside PA and other exempt items reduce it). May be negative. Default 0 assumes Box 16 = Box 1. Taxable early retirement-plan/IRA distributions under PA's cost-recovery method also go here. In dollars.",
|
|
25817
|
+
default: { value: "0", rationale: "PA W-2 Box 16 assumed equal to federal Box 1 wages absent contrary input" }
|
|
25818
|
+
},
|
|
25819
|
+
{
|
|
25820
|
+
id: "paUnreimbursedBusinessExpenses",
|
|
25821
|
+
type: "money",
|
|
25822
|
+
min: "0",
|
|
25823
|
+
description: "PA Schedule UE unreimbursed employee business expenses (PA-40 line 1b) \u2014 a compensation-class expense, not a line-10 deduction. In dollars.",
|
|
25824
|
+
default: { value: "0", rationale: "No unreimbursed employee business expenses claimed absent contrary input" }
|
|
25825
|
+
},
|
|
25826
|
+
{
|
|
25827
|
+
id: "paInterestIncome",
|
|
25828
|
+
type: "money",
|
|
25829
|
+
min: "0",
|
|
25830
|
+
description: "PA-taxable interest income (PA-40 line 2, gross class \u2014 no expenses; forfeited-interest penalties may offset within the class before entry). Includes commercial-annuity income taxable as PA interest. In dollars.",
|
|
25831
|
+
default: { value: "0", rationale: "No PA interest income absent contrary input" }
|
|
25832
|
+
},
|
|
25833
|
+
{
|
|
25834
|
+
id: "paDividendIncome",
|
|
25835
|
+
type: "money",
|
|
25836
|
+
min: "0",
|
|
25837
|
+
description: "PA-taxable dividend income (PA-40 line 3, gross class) INCLUDING mutual-fund capital gains distributions, which PA classifies as dividends. In dollars.",
|
|
25838
|
+
default: { value: "0", rationale: "No PA dividend income absent contrary input" }
|
|
25839
|
+
},
|
|
25840
|
+
{
|
|
25841
|
+
id: "paBusinessNetIncome",
|
|
25842
|
+
type: "money",
|
|
25843
|
+
description: "Taxpayer's own net income or LOSS from business/profession/farm (PA-40 line 4 class, after within-class netting of the taxpayer's own activities). Negative allowed; a loss never offsets other classes or the spouse. In dollars.",
|
|
25844
|
+
default: { value: "0", rationale: "No business income or loss absent contrary input" }
|
|
25845
|
+
},
|
|
25846
|
+
{
|
|
25847
|
+
id: "paSpouseBusinessNetIncome",
|
|
25848
|
+
type: "money",
|
|
25849
|
+
description: "Spouse's own net income or LOSS from business/profession/farm (PA-40 line 4 class). Negative allowed; never netted against the taxpayer's. In dollars.",
|
|
25850
|
+
default: { value: "0", rationale: "No spouse business income or loss absent contrary input" }
|
|
25851
|
+
},
|
|
25852
|
+
{
|
|
25853
|
+
id: "paPropertyGainNet",
|
|
25854
|
+
type: "money",
|
|
25855
|
+
description: "Taxpayer's own net gain or LOSS from sale/exchange/disposition of property (PA-40 line 5 class). Negative allowed; no carryover, no cross-class or spousal offset. In dollars.",
|
|
25856
|
+
default: { value: "0", rationale: "No property gain or loss absent contrary input" }
|
|
25857
|
+
},
|
|
25858
|
+
{
|
|
25859
|
+
id: "paSpousePropertyGainNet",
|
|
25860
|
+
type: "money",
|
|
25861
|
+
description: "Spouse's own net gain or LOSS from disposition of property (PA-40 line 5 class). Negative allowed; never netted against the taxpayer's. In dollars.",
|
|
25862
|
+
default: { value: "0", rationale: "No spouse property gain or loss absent contrary input" }
|
|
25863
|
+
},
|
|
25864
|
+
{
|
|
25865
|
+
id: "paRentRoyaltyNet",
|
|
25866
|
+
type: "money",
|
|
25867
|
+
description: "Taxpayer's own net income or LOSS from rents/royalties/patents/copyrights (PA-40 line 6 class). Negative allowed; short-term rentals (<30 days) belong in the business class instead. In dollars.",
|
|
25868
|
+
default: { value: "0", rationale: "No rent/royalty income or loss absent contrary input" }
|
|
25869
|
+
},
|
|
25870
|
+
{
|
|
25871
|
+
id: "paSpouseRentRoyaltyNet",
|
|
25872
|
+
type: "money",
|
|
25873
|
+
description: "Spouse's own net income or LOSS from rents/royalties/patents/copyrights (PA-40 line 6 class). Negative allowed; never netted against the taxpayer's. In dollars.",
|
|
25874
|
+
default: { value: "0", rationale: "No spouse rent/royalty income or loss absent contrary input" }
|
|
25875
|
+
},
|
|
25876
|
+
{
|
|
25877
|
+
id: "paEstateTrustIncome",
|
|
25878
|
+
type: "money",
|
|
25879
|
+
min: "0",
|
|
25880
|
+
description: "PA-taxable income from estates or trusts (PA-40 line 7, Schedule J) \u2014 an estate or trust cannot distribute a loss, so this is never negative. In dollars.",
|
|
25881
|
+
default: { value: "0", rationale: "No estate/trust income absent contrary input" }
|
|
25882
|
+
},
|
|
25883
|
+
{
|
|
25884
|
+
id: "paGamblingWinnings",
|
|
25885
|
+
type: "money",
|
|
25886
|
+
min: "0",
|
|
25887
|
+
description: "PA-taxable gambling and lottery winnings (PA-40 line 8, Schedule T) net of wager costs (PA Lottery ticket costs deductible only for tickets bought on/after 1/1/2016); noncash PA Lottery prizes are exempt. In dollars.",
|
|
25888
|
+
default: { value: "0", rationale: "No gambling winnings absent contrary input" }
|
|
25889
|
+
},
|
|
25890
|
+
{
|
|
25891
|
+
id: "pa529Contributions",
|
|
25892
|
+
type: "money",
|
|
25893
|
+
min: "0",
|
|
25894
|
+
description: "PA Schedule O \xA7 529 tuition-program contribution deduction, ALREADY capped by the caller at $19,000 per beneficiary per taxpayer-spouse (2025) \u2014 the corpus cannot see per-beneficiary detail. No deduction for 529-to-529 rollovers or beneficiary changes. In dollars.",
|
|
25895
|
+
default: { value: "0", rationale: "No 529 contributions claimed absent contrary input" }
|
|
25896
|
+
},
|
|
25897
|
+
{
|
|
25898
|
+
id: "paAbleContributions",
|
|
25899
|
+
type: "money",
|
|
25900
|
+
min: "0",
|
|
25901
|
+
description: "PA Schedule O \xA7 529A ABLE contribution deduction, capped by the caller at the annual federal gift-tax exclusion ($19,000 for 2025). In dollars.",
|
|
25902
|
+
default: { value: "0", rationale: "No ABLE contributions claimed absent contrary input" }
|
|
25903
|
+
},
|
|
25904
|
+
{
|
|
25905
|
+
id: "paMsaHsaContributions",
|
|
25906
|
+
type: "money",
|
|
25907
|
+
min: "0",
|
|
25908
|
+
description: "PA Schedule O Medical Savings Account + Health Savings Account contribution deductions \u2014 limited to the amounts allowed for FEDERAL income tax purposes (supply the federal-allowed total). In dollars.",
|
|
25909
|
+
default: { value: "0", rationale: "No MSA/HSA deduction claimed absent contrary input" }
|
|
25910
|
+
},
|
|
25911
|
+
{
|
|
25912
|
+
id: "paSpDependentChildren",
|
|
25913
|
+
type: "int",
|
|
25914
|
+
min: "0",
|
|
25915
|
+
description: "PA Schedule SP dependent CHILDREN count (natural/adopted/step; grandchild of a grandparent; foster child of a foster parent \u2014 never aunts/uncles/unrelated persons) claimable as federal dependents; adult qualifying children count. Raises the Tax Forgiveness eligibility-income threshold $9,500 each.",
|
|
25916
|
+
default: {
|
|
25917
|
+
value: "0",
|
|
25918
|
+
rationale: "Conservative: no SP dependent children assumed \u2014 understates the forgiveness threshold, never overstates the credit"
|
|
25919
|
+
}
|
|
25920
|
+
},
|
|
25921
|
+
{
|
|
25922
|
+
id: "paEligibilityIncomeAddbacks",
|
|
25923
|
+
type: "money",
|
|
25924
|
+
min: "0",
|
|
25925
|
+
description: "PA Schedule SP Section III nontaxable add-backs to eligibility income: nontaxable interest/dividends/gains, alimony received, insurance proceeds and inheritances (incl. 1099-R code-4 box 1), gifts/awards/prizes (incl. noncash PA Lottery), non-PA income, nontaxable military pay (not combat), excluded home-sale gain, nontaxable educational assistance, outside cash support. Do NOT add Social Security/RRB, eligible retirement benefits, child support, military pensions, workers' comp, personal-injury damages, or sick/disability pay. In dollars.",
|
|
25926
|
+
default: { value: "0", rationale: "No nontaxable eligibility-income add-backs absent contrary input" }
|
|
25927
|
+
},
|
|
25928
|
+
{
|
|
25929
|
+
id: "paResidentCredit",
|
|
25930
|
+
type: "money",
|
|
25931
|
+
min: "0",
|
|
25932
|
+
description: "PA resident credit for income tax paid to other states (PA-40 line 22, Schedule G-L; not allowed for reciprocal-state compensation: IN, MD, NJ, OH, VA, WV). Subtracts from tax BEFORE Tax Forgiveness per Schedule SP Section IV. In dollars.",
|
|
25933
|
+
default: { value: "0", rationale: "No other-state tax credit claimed absent contrary input" }
|
|
25352
25934
|
}
|
|
25353
25935
|
];
|
|
25354
25936
|
|
|
@@ -25856,8 +26438,8 @@ function ordinaryRateTaxExpr(base, statusFactId, year) {
|
|
|
25856
26438
|
var incomeTaxRules = [
|
|
25857
26439
|
{
|
|
25858
26440
|
id: "us.federal.ordinary_taxable_income",
|
|
25859
|
-
version:
|
|
25860
|
-
//
|
|
26441
|
+
version: 3,
|
|
26442
|
+
// v2 read the raw longTermCapitalGains fact; the § 1(h) amount is now the § 1222(11) netted result from schedule_d
|
|
25861
26443
|
jurisdiction: "us.federal",
|
|
25862
26444
|
title: "Taxable income taxed at ordinary rates (excludes preferential gains and qualified dividends)",
|
|
25863
26445
|
citation: {
|
|
@@ -25874,7 +26456,10 @@ var incomeTaxRules = [
|
|
|
25874
26456
|
arg: {
|
|
25875
26457
|
kind: "sub",
|
|
25876
26458
|
left: taxable,
|
|
25877
|
-
right: {
|
|
26459
|
+
right: {
|
|
26460
|
+
kind: "add",
|
|
26461
|
+
args: [ruleRef2("us.federal.schedule_d.preferential_lt_gain"), fact2("qualifiedDividends")]
|
|
26462
|
+
}
|
|
25878
26463
|
}
|
|
25879
26464
|
}
|
|
25880
26465
|
},
|
|
@@ -26126,6 +26711,8 @@ var OUT_OF_SCOPE = {
|
|
|
26126
26711
|
gt0(fact4("taxableInterest")),
|
|
26127
26712
|
gt0(fact4("longTermCapitalGains")),
|
|
26128
26713
|
gt0(fact4("netCapitalLoss")),
|
|
26714
|
+
gt0(fact4("shortTermCapitalLoss")),
|
|
26715
|
+
gt0(fact4("longTermCapitalLoss")),
|
|
26129
26716
|
gt0(fact4("qualifiedTips")),
|
|
26130
26717
|
gt0(fact4("qualifiedOvertimePremium")),
|
|
26131
26718
|
gt0(fact4("carLoanInterest")),
|
|
@@ -26274,7 +26861,8 @@ function quarterRule(q, year, from, to, proratedLimitCents, proratedLabel, withQ
|
|
|
26274
26861
|
};
|
|
26275
26862
|
return {
|
|
26276
26863
|
id: `us.federal.estimated.annualized_installment_q${q.n}`,
|
|
26277
|
-
version: year === "2025" ?
|
|
26864
|
+
version: year === "2025" ? 3 : 4,
|
|
26865
|
+
// v1/v2 predated the per-bucket capital-loss facts in the scope guard
|
|
26278
26866
|
jurisdiction: J2,
|
|
26279
26867
|
title: `Annualized-income required installment #${q.n} (\xA7 6654(d)(2), TY${year})`,
|
|
26280
26868
|
citation: {
|
|
@@ -26321,14 +26909,14 @@ var CITE = {
|
|
|
26321
26909
|
var capitalLossRules = [
|
|
26322
26910
|
{
|
|
26323
26911
|
id: "us.federal.capital_loss_ordinary_offset",
|
|
26324
|
-
version:
|
|
26325
|
-
//
|
|
26912
|
+
version: 3,
|
|
26913
|
+
// v2 predated schedule_d netting: the loss is now the § 1222(10) netted result (legacy netCapitalLoss passes through; mixed-bucket years compute), and the contradiction refusal lives in the schedule_d rules
|
|
26326
26914
|
jurisdiction: J3,
|
|
26327
26915
|
title: "Capital loss allowed against ordinary income ($3,000 / $1,500 MFS limit)",
|
|
26328
26916
|
citation: {
|
|
26329
26917
|
...CITE,
|
|
26330
26918
|
section: "\xA7 1211(b)",
|
|
26331
|
-
excerpt: "\u2026losses from sales or exchanges of capital assets shall be allowed only to the extent of the gains from such sales or exchanges, plus (if such losses exceed such gains) the lower of $3,000 ($1,500 in the case of a married individual filing a separate return), or the excess of such losses over such gains. [The
|
|
26919
|
+
excerpt: "\u2026losses from sales or exchanges of capital assets shall be allowed only to the extent of the gains from such sales or exchanges, plus (if such losses exceed such gains) the lower of $3,000 ($1,500 in the case of a married individual filing a separate return), or the excess of such losses over such gains. [The loss here is the \xA7 1222(10) net capital loss after Schedule D netting \u2014 computed by us.federal.schedule_d.net_loss from the per-bucket facts, or passed through from the legacy overall netCapitalLoss fact; contradictory fact combinations refuse there.]"
|
|
26332
26920
|
},
|
|
26333
26921
|
...FROM,
|
|
26334
26922
|
output: { type: "money" },
|
|
@@ -26339,45 +26927,27 @@ var capitalLossRules = [
|
|
|
26339
26927
|
// $1,500
|
|
26340
26928
|
},
|
|
26341
26929
|
formula: {
|
|
26342
|
-
kind: "
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
reason: "both a net capital gain and a net capital loss were entered \u2014 provide the single NET result of Schedule D netting"
|
|
26356
|
-
},
|
|
26357
|
-
else: {
|
|
26358
|
-
kind: "min",
|
|
26359
|
-
args: [
|
|
26360
|
-
fact5("netCapitalLoss"),
|
|
26361
|
-
{
|
|
26362
|
-
kind: "if",
|
|
26363
|
-
cond: {
|
|
26364
|
-
kind: "cmp",
|
|
26365
|
-
op: "eq",
|
|
26366
|
-
left: fact5("filingStatus"),
|
|
26367
|
-
right: { kind: "enum", value: "mfs" }
|
|
26368
|
-
},
|
|
26369
|
-
then: param3("limitMfs"),
|
|
26370
|
-
else: param3("limit")
|
|
26371
|
-
}
|
|
26372
|
-
]
|
|
26930
|
+
kind: "min",
|
|
26931
|
+
args: [
|
|
26932
|
+
ruleRef5("us.federal.schedule_d.net_loss"),
|
|
26933
|
+
{
|
|
26934
|
+
kind: "if",
|
|
26935
|
+
cond: {
|
|
26936
|
+
kind: "cmp",
|
|
26937
|
+
op: "eq",
|
|
26938
|
+
left: fact5("filingStatus"),
|
|
26939
|
+
right: { kind: "enum", value: "mfs" }
|
|
26940
|
+
},
|
|
26941
|
+
then: param3("limitMfs"),
|
|
26942
|
+
else: param3("limit")
|
|
26373
26943
|
}
|
|
26374
|
-
|
|
26375
|
-
else: zero3
|
|
26944
|
+
]
|
|
26376
26945
|
}
|
|
26377
26946
|
},
|
|
26378
26947
|
{
|
|
26379
26948
|
id: "us.federal.capital_loss_carryover",
|
|
26380
|
-
version:
|
|
26949
|
+
version: 2,
|
|
26950
|
+
// v1 read the legacy netCapitalLoss fact directly; the netted schedule_d result also covers mixed-bucket years
|
|
26381
26951
|
jurisdiction: J3,
|
|
26382
26952
|
title: "Capital loss carryover to the following year (simplified)",
|
|
26383
26953
|
citation: {
|
|
@@ -26391,7 +26961,7 @@ var capitalLossRules = [
|
|
|
26391
26961
|
kind: "max0",
|
|
26392
26962
|
arg: {
|
|
26393
26963
|
kind: "sub",
|
|
26394
|
-
left:
|
|
26964
|
+
left: ruleRef5("us.federal.schedule_d.net_loss"),
|
|
26395
26965
|
right: ruleRef5("us.federal.capital_loss_ordinary_offset")
|
|
26396
26966
|
}
|
|
26397
26967
|
}
|
|
@@ -29061,9 +29631,9 @@ function eitcRule(version2, effectiveFrom, effectiveTo, p, yearLabel, revProc, o
|
|
|
29061
29631
|
kind: "add",
|
|
29062
29632
|
args: [
|
|
29063
29633
|
fact17("taxableInterest"),
|
|
29064
|
-
|
|
29634
|
+
ruleRef14("us.federal.schedule_d.preferential_lt_gain"),
|
|
29065
29635
|
fact17("qualifiedDividends"),
|
|
29066
|
-
|
|
29636
|
+
ruleRef14("us.federal.schedule_d.ordinary_st_gain"),
|
|
29067
29637
|
fact17("ordinaryDividends")
|
|
29068
29638
|
]
|
|
29069
29639
|
},
|
|
@@ -29171,19 +29741,21 @@ var eitcRules = [
|
|
|
29171
29741
|
// v5/v6: table method default (§ 32(f)) + eitcAdditionalQualifyingChildren
|
|
29172
29742
|
// v8/v9: § 32(i) investment-income limit also counts the standalone qualifiedDividends fact
|
|
29173
29743
|
// v10/v11: § 32(i) investment-income limit also counts shortTermCapitalGains/ordinaryDividends
|
|
29174
|
-
|
|
29175
|
-
|
|
29744
|
+
// v12/v13 read the raw gain facts; the § 32(i) test now takes the schedule_d
|
|
29745
|
+
// netted results ("capital gain net income", § 32(i)(1)(C))
|
|
29746
|
+
eitcRule(14, "2025-01-01", "2026-01-01", TY20252, "2025", "Rev. Proc. 2024-40 \xA7 2.06"),
|
|
29747
|
+
eitcRule(15, "2026-01-01", "2027-01-01", TY20262, "2026", "Rev. Proc. 2025-32 \xA7 4.06"),
|
|
29176
29748
|
// us.federal.eitc_no_age_gate (new): the same § 32(a)-(b),(f),(i) formula with
|
|
29177
29749
|
// the childless § 32(c)(1)(A)(ii) age gate removed, for state EITC statutes
|
|
29178
29750
|
// that decouple from it (35 ILCS 5/212(b-5)/(b-10), IL P.A. 102-700) — see
|
|
29179
29751
|
// us.il.eitc, which is 20% of THIS rule's answer, not us.federal.eitc's.
|
|
29180
|
-
eitcRule(
|
|
29752
|
+
eitcRule(3, "2025-01-01", "2026-01-01", TY20252, "2025", "Rev. Proc. 2024-40 \xA7 2.06", {
|
|
29181
29753
|
ruleId: "us.federal.eitc_no_age_gate",
|
|
29182
29754
|
ageGate: false,
|
|
29183
29755
|
titleSuffix: "Earned income credit, \xA7 32(c)(1)(A)(ii) childless age gate REMOVED (TY2025; for state decoupling statutes only)",
|
|
29184
29756
|
extraExcerpt: NO_AGE_GATE_EXCERPT
|
|
29185
29757
|
}),
|
|
29186
|
-
eitcRule(
|
|
29758
|
+
eitcRule(4, "2026-01-01", "2027-01-01", TY20262, "2026", "Rev. Proc. 2025-32 \xA7 4.06", {
|
|
29187
29759
|
ruleId: "us.federal.eitc_no_age_gate",
|
|
29188
29760
|
ageGate: false,
|
|
29189
29761
|
titleSuffix: "Earned income credit, \xA7 32(c)(1)(A)(ii) childless age gate REMOVED (TY2026; for state decoupling statutes only)",
|
|
@@ -30108,8 +30680,8 @@ var FROM10 = { effectiveFrom: "2025-01-01" };
|
|
|
30108
30680
|
var incomeRules = [
|
|
30109
30681
|
{
|
|
30110
30682
|
id: "us.federal.gross_income",
|
|
30111
|
-
version:
|
|
30112
|
-
//
|
|
30683
|
+
version: 15,
|
|
30684
|
+
// v14 read the raw gain facts; capital gains now enter via schedule_d netting (mixed gain/loss years compute instead of refusing), and rental income can enter pre-depreciation via us.federal.rental.net_income
|
|
30113
30685
|
jurisdiction: J16,
|
|
30114
30686
|
title: "Gross income (simplified: wages + interest + capital gains (long/short-term) + qualified/ordinary dividends + SE net profit + K-1 pass-through income \u2212 allowed capital loss \u2212 \xA7 911 exclusion)",
|
|
30115
30687
|
citation: {
|
|
@@ -30127,14 +30699,15 @@ var incomeRules = [
|
|
|
30127
30699
|
args: [
|
|
30128
30700
|
fact24("wages"),
|
|
30129
30701
|
fact24("taxableInterest"),
|
|
30130
|
-
|
|
30702
|
+
ruleRef19("us.federal.schedule_d.preferential_lt_gain"),
|
|
30131
30703
|
fact24("qualifiedDividends"),
|
|
30132
|
-
|
|
30704
|
+
ruleRef19("us.federal.schedule_d.ordinary_st_gain"),
|
|
30133
30705
|
fact24("ordinaryDividends"),
|
|
30134
30706
|
fact24("selfEmploymentNetProfit"),
|
|
30135
30707
|
fact24("k1OrdinaryBusinessIncome"),
|
|
30136
30708
|
fact24("taxableIraDistributions"),
|
|
30137
30709
|
fact24("taxablePensionsAndAnnuities"),
|
|
30710
|
+
ruleRef19("us.federal.pension.simplified_method_taxable"),
|
|
30138
30711
|
fact24("unemploymentCompensation"),
|
|
30139
30712
|
fact24("otherOrdinaryIncome"),
|
|
30140
30713
|
fact24("alimonyReceivedPre2019"),
|
|
@@ -30148,6 +30721,7 @@ var incomeRules = [
|
|
|
30148
30721
|
}
|
|
30149
30722
|
},
|
|
30150
30723
|
fact24("scheduleENetIncome"),
|
|
30724
|
+
ruleRef19("us.federal.rental.net_income"),
|
|
30151
30725
|
ruleRef19("us.federal.taxable_social_security")
|
|
30152
30726
|
]
|
|
30153
30727
|
},
|
|
@@ -30172,7 +30746,7 @@ var incomeRules = [
|
|
|
30172
30746
|
source: "26 U.S.C. \xA7 62(a)(1), (a)(2)(D), (a)(6), (a)(9), (a)(17), (a)(19)",
|
|
30173
30747
|
section: "\xA7 62(a)",
|
|
30174
30748
|
url: "https://www.law.cornell.edu/uscode/text/26/62",
|
|
30175
|
-
excerpt: "adjusted gross income means gross income minus\u2026 the deduction allowed by section 164(f) [half of SE tax]\u2026 the deduction allowed by section 223 [HSA]\u2026 the deductions allowed by section 404 [self-employed retirement plans]\u2026 the deduction allowed by section 162(l) [self-employed health insurance]\u2026 the deductions\u2026 [for] expenses\u2026 of eligible educators (\xA7 62(a)(2)(D) \u2014 $300 cap PER ELIGIBLE EDUCATOR for 2025 (an MFJ couple of two educators deducts up to $600: use educatorExpenses for the taxpayer and spouseEducatorExpenses for the spouse, each capped separately), the indexed amount
|
|
30749
|
+
excerpt: "adjusted gross income means gross income minus\u2026 the deduction allowed by section 164(f) [half of SE tax]\u2026 the deduction allowed by section 223 [HSA]\u2026 the deductions allowed by section 404 [self-employed retirement plans]\u2026 the deduction allowed by section 162(l) [self-employed health insurance]\u2026 the deductions\u2026 [for] expenses\u2026 of eligible educators (\xA7 62(a)(2)(D) \u2014 $300 cap PER ELIGIBLE EDUCATOR for 2025 (an MFJ couple of two educators deducts up to $600: use educatorExpenses for the taxpayer and spouseEducatorExpenses for the spouse, each capped separately), the 2025 indexed amount)\u2026 [and] penalties forfeited because of premature withdrawal of funds from time savings accounts or deposits (\xA7 62(a)(9), uncapped)."
|
|
30176
30750
|
},
|
|
30177
30751
|
...FROM10,
|
|
30178
30752
|
output: { type: "money" },
|
|
@@ -30739,8 +31313,8 @@ var amThreshold = {
|
|
|
30739
31313
|
var investmentTaxRules = [
|
|
30740
31314
|
{
|
|
30741
31315
|
id: "us.federal.niit",
|
|
30742
|
-
version:
|
|
30743
|
-
//
|
|
31316
|
+
version: 6,
|
|
31317
|
+
// v5 read the raw gain facts; § 1411 net investment income now takes the schedule_d netted results (losses reduce gains before NIIT)
|
|
30744
31318
|
jurisdiction: "us.federal",
|
|
30745
31319
|
title: "Net investment income tax (investment income = interest + capital gains (LT/ST) + dividends (qualified/ordinary) \u2212 allocable deductions; MAGI \u2248 AGI)",
|
|
30746
31320
|
citation: {
|
|
@@ -30765,9 +31339,9 @@ var investmentTaxRules = [
|
|
|
30765
31339
|
kind: "add",
|
|
30766
31340
|
args: [
|
|
30767
31341
|
fact26("taxableInterest"),
|
|
30768
|
-
|
|
31342
|
+
ruleRef21("us.federal.schedule_d.preferential_lt_gain"),
|
|
30769
31343
|
fact26("qualifiedDividends"),
|
|
30770
|
-
|
|
31344
|
+
ruleRef21("us.federal.schedule_d.ordinary_st_gain"),
|
|
30771
31345
|
fact26("ordinaryDividends")
|
|
30772
31346
|
]
|
|
30773
31347
|
},
|
|
@@ -31640,12 +32214,15 @@ function kiddieTaxRule(version2, from, to, year) {
|
|
|
31640
32214
|
{ kind: "cmp", op: "gt", left: fact30("longTermCapitalGains"), right: zero18 },
|
|
31641
32215
|
{ kind: "cmp", op: "gt", left: fact30("qualifiedDividends"), right: zero18 },
|
|
31642
32216
|
{ kind: "cmp", op: "gt", left: fact30("shortTermCapitalGains"), right: zero18 },
|
|
31643
|
-
{ kind: "cmp", op: "gt", left: fact30("ordinaryDividends"), right: zero18 }
|
|
32217
|
+
{ kind: "cmp", op: "gt", left: fact30("ordinaryDividends"), right: zero18 },
|
|
32218
|
+
{ kind: "cmp", op: "gt", left: fact30("shortTermCapitalLoss"), right: zero18 },
|
|
32219
|
+
{ kind: "cmp", op: "gt", left: fact30("longTermCapitalLoss"), right: zero18 },
|
|
32220
|
+
{ kind: "cmp", op: "gt", left: fact30("netCapitalLoss"), right: zero18 }
|
|
31644
32221
|
]
|
|
31645
32222
|
},
|
|
31646
32223
|
then: {
|
|
31647
32224
|
kind: "unsupported",
|
|
31648
|
-
reason: "a \xA7 1(g) child with capital gains, qualified dividends, or ordinary dividends needs the Form 8615 capital-gains worksheets, and/or is unearned income this corpus's 'unearned income \u2248 taxable interest' approximation would silently drop \u2014 not modeled"
|
|
32225
|
+
reason: "a \xA7 1(g) child with capital gains/losses, qualified dividends, or ordinary dividends needs the Form 8615 capital-gains worksheets, and/or is unearned income this corpus's 'unearned income \u2248 taxable interest' approximation would silently drop \u2014 not modeled"
|
|
31649
32226
|
},
|
|
31650
32227
|
else: {
|
|
31651
32228
|
kind: "if",
|
|
@@ -31705,8 +32282,9 @@ var kiddieRules = [
|
|
|
31705
32282
|
// v3/v4: the § 1(g) capital-gains refusal guard also catches the standalone qualifiedDividends fact
|
|
31706
32283
|
// v5/v6: the refusal guard also catches shortTermCapitalGains/ordinaryDividends (unearned income
|
|
31707
32284
|
// the "unearned income ≈ taxable interest" approximation in net_unearned_income would otherwise drop)
|
|
31708
|
-
|
|
31709
|
-
kiddieTaxRule(
|
|
32285
|
+
// v5/v6: the refusal guard predated the per-bucket capital-loss facts and netCapitalLoss
|
|
32286
|
+
kiddieTaxRule(7, "2025-01-01", "2026-01-01", "2025"),
|
|
32287
|
+
kiddieTaxRule(8, "2026-01-01", "2027-01-01", "2026"),
|
|
31710
32288
|
{
|
|
31711
32289
|
id: "us.federal.income_tax_before_credits.kiddie",
|
|
31712
32290
|
version: 1,
|
|
@@ -31923,7 +32501,7 @@ function mileageRule(version2, from, to, rateNum, rateLabel, noticeLabel) {
|
|
|
31923
32501
|
source: `${noticeLabel}; Rev. Proc. 2019-46`,
|
|
31924
32502
|
section: `${noticeLabel} \xA7 3`,
|
|
31925
32503
|
url: "https://www.irs.gov/pub/irs-drop/n-26-10.pdf",
|
|
31926
|
-
excerpt: `The standard mileage rate for transportation or travel expenses is ${rateLabel} per mile for all miles of business use (${noticeLabel}, verbatim rate). [A STANDALONE determination in lieu of actual vehicle expenses (Rev. Proc. 2019-46 rules attested): subtract the result from your Schedule C profit yourself \u2014 it changes SE tax and QBI. Depreciation-component basis tracking and the actual-expense method are not modeled.] ADD-ON EXPENSES (Pub. 463 ch. 4
|
|
32504
|
+
excerpt: `The standard mileage rate for transportation or travel expenses is ${rateLabel} per mile for all miles of business use (${noticeLabel}, verbatim rate). [A STANDALONE determination in lieu of actual vehicle expenses (Rev. Proc. 2019-46 rules attested): subtract the result from your Schedule C profit yourself \u2014 it changes SE tax and QBI. Depreciation-component basis tracking and the actual-expense method are not modeled.] ADD-ON EXPENSES (Pub. 463 ch. 4): the mileage rate covers depreciation/gas/repairs ONLY. PARKING FEES and TOLLS for business trips deduct IN FULL in addition. The vehicle's PERSONAL PROPERTY TAX and (for the self-employed) CAR LOAN INTEREST deduct in addition but ONLY at the BUSINESS-USE PERCENTAGE = business miles \xF7 TOTAL miles driven (business + commuting + other/personal), computed and rounded (half-up to the dollar) PER VEHICLE: per-vehicle deduction = miles \xD7 rate + parking + tolls + businessPct \xD7 (property tax + loan interest). A vehicle with no commuting/other miles listed is 100% business use (add-ons in full).`
|
|
31927
32505
|
},
|
|
31928
32506
|
effectiveFrom: from,
|
|
31929
32507
|
effectiveTo: to,
|
|
@@ -31942,57 +32520,376 @@ var mileageRules = [
|
|
|
31942
32520
|
mileageRule(2, "2026-01-01", "2027-01-01", "725", "72.5 cents", "Notice 2026-10")
|
|
31943
32521
|
];
|
|
31944
32522
|
|
|
31945
|
-
// ../corpus-us-federal/dist/rules/
|
|
32523
|
+
// ../corpus-us-federal/dist/rules/pension.js
|
|
31946
32524
|
var J23 = "us.federal";
|
|
32525
|
+
var FROM13 = { effectiveFrom: "2025-01-01" };
|
|
31947
32526
|
var fact32 = (factId) => ({ kind: "fact", factId });
|
|
31948
32527
|
var money29 = (cents) => ({ kind: "money", cents });
|
|
32528
|
+
var int2 = (value) => ({ kind: "int", value });
|
|
31949
32529
|
var ruleRef27 = (ruleId) => ({ kind: "rule", ruleId });
|
|
31950
32530
|
var zero20 = money29("0");
|
|
32531
|
+
var cost = fact32("pensionCostBasis");
|
|
32532
|
+
var months = fact32("pensionMonthsThisYear");
|
|
32533
|
+
var annualAt = (n) => ({
|
|
32534
|
+
kind: "mulInt",
|
|
32535
|
+
base: { kind: "mulRate", base: cost, rate: { num: "1", den: n }, round: "half-up" },
|
|
32536
|
+
count: months
|
|
32537
|
+
});
|
|
32538
|
+
var ladder = (on, bands, last) => {
|
|
32539
|
+
let expr = annualAt(last);
|
|
32540
|
+
for (let i = bands.length - 1; i >= 0; i--) {
|
|
32541
|
+
const [limit, n] = bands[i];
|
|
32542
|
+
expr = {
|
|
32543
|
+
kind: "if",
|
|
32544
|
+
cond: { kind: "cmp", op: "le", left: on, right: int2(limit) },
|
|
32545
|
+
then: annualAt(n),
|
|
32546
|
+
else: expr
|
|
32547
|
+
};
|
|
32548
|
+
}
|
|
32549
|
+
return expr;
|
|
32550
|
+
};
|
|
32551
|
+
var needsAge = (ageFact) => ({
|
|
32552
|
+
kind: "cmp",
|
|
32553
|
+
op: "eq",
|
|
32554
|
+
left: fact32(ageFact),
|
|
32555
|
+
right: int2("0")
|
|
32556
|
+
});
|
|
32557
|
+
var pensionRules = [
|
|
32558
|
+
{
|
|
32559
|
+
id: "us.federal.pension.simplified_method_exclusion",
|
|
32560
|
+
version: 1,
|
|
32561
|
+
jurisdiction: J23,
|
|
32562
|
+
title: "Simplified Method tax-free portion (\xA7 72(d): cost \xF7 anticipated payments \xD7 months, capped at unrecovered cost)",
|
|
32563
|
+
citation: {
|
|
32564
|
+
source: "26 U.S.C. \xA7 72(d); IRS Pub 575 (Simplified Method Worksheet, Tables 1-2)",
|
|
32565
|
+
section: "\xA7 72(d)(1)(B)(iii)-(iv)",
|
|
32566
|
+
url: "https://www.law.cornell.edu/uscode/text/26/72",
|
|
32567
|
+
excerpt: "Gross income shall not include so much of any monthly annuity payment\u2026 as does not exceed the amount obtained by dividing (I) the investment in the contract (as of the annuity starting date), by (II) the number of anticipated payments\u2026 Age at annuity starting date: not more than 55 \u2192 360; more than 55 but not more than 60 \u2192 310; more than 60 but not more than 65 \u2192 260; more than 65 but not more than 70 \u2192 210; more than 70 \u2192 160. Combined ages (more than one life): not more than 110 \u2192 410; \u2026120 \u2192 360; \u2026130 \u2192 310; \u2026140 \u2192 260; more than 140 \u2192 210. [Lifetime recovery capped at total cost (Pub 575); monthly division keeps cents per the Pub 575 worked example. The rule refuses when the basis is set but the required age fact is not.]"
|
|
32568
|
+
},
|
|
32569
|
+
...FROM13,
|
|
32570
|
+
output: { type: "money" },
|
|
32571
|
+
formula: {
|
|
32572
|
+
kind: "if",
|
|
32573
|
+
cond: { kind: "cmp", op: "eq", left: cost, right: zero20 },
|
|
32574
|
+
then: zero20,
|
|
32575
|
+
else: {
|
|
32576
|
+
kind: "if",
|
|
32577
|
+
cond: fact32("pensionIsJointAndSurvivor"),
|
|
32578
|
+
then: {
|
|
32579
|
+
kind: "if",
|
|
32580
|
+
cond: needsAge("pensionCombinedAgesAtStart"),
|
|
32581
|
+
then: {
|
|
32582
|
+
kind: "unsupported",
|
|
32583
|
+
reason: "a joint-and-survivor simplified-method annuity needs pensionCombinedAgesAtStart (combined ages at the annuity starting date) \u2014 refusing rather than assume a \xA7 72(d)(1)(B)(iv) band"
|
|
32584
|
+
},
|
|
32585
|
+
else: {
|
|
32586
|
+
kind: "min",
|
|
32587
|
+
args: [
|
|
32588
|
+
ladder(fact32("pensionCombinedAgesAtStart"), [
|
|
32589
|
+
["110", "410"],
|
|
32590
|
+
["120", "360"],
|
|
32591
|
+
["130", "310"],
|
|
32592
|
+
["140", "260"]
|
|
32593
|
+
], "210"),
|
|
32594
|
+
{ kind: "max0", arg: { kind: "sub", left: cost, right: fact32("pensionBasisPreviouslyRecovered") } }
|
|
32595
|
+
]
|
|
32596
|
+
}
|
|
32597
|
+
},
|
|
32598
|
+
else: {
|
|
32599
|
+
kind: "if",
|
|
32600
|
+
cond: needsAge("pensionAgeAtStart"),
|
|
32601
|
+
then: {
|
|
32602
|
+
kind: "unsupported",
|
|
32603
|
+
reason: "a simplified-method annuity needs pensionAgeAtStart (age at the annuity starting date, NOT current age) \u2014 refusing rather than assume a \xA7 72(d)(1)(B)(iii) band"
|
|
32604
|
+
},
|
|
32605
|
+
else: {
|
|
32606
|
+
kind: "min",
|
|
32607
|
+
args: [
|
|
32608
|
+
ladder(fact32("pensionAgeAtStart"), [
|
|
32609
|
+
["55", "360"],
|
|
32610
|
+
["60", "310"],
|
|
32611
|
+
["65", "260"],
|
|
32612
|
+
["70", "210"]
|
|
32613
|
+
], "160"),
|
|
32614
|
+
{ kind: "max0", arg: { kind: "sub", left: cost, right: fact32("pensionBasisPreviouslyRecovered") } }
|
|
32615
|
+
]
|
|
32616
|
+
}
|
|
32617
|
+
}
|
|
32618
|
+
}
|
|
32619
|
+
}
|
|
32620
|
+
},
|
|
32621
|
+
{
|
|
32622
|
+
id: "us.federal.pension.simplified_method_taxable",
|
|
32623
|
+
version: 1,
|
|
32624
|
+
jurisdiction: J23,
|
|
32625
|
+
title: "Taxable pension under the Simplified Method (gross payments minus the \xA7 72(d) exclusion)",
|
|
32626
|
+
citation: {
|
|
32627
|
+
source: "26 U.S.C. \xA7 72(a), (d); IRS Pub 575",
|
|
32628
|
+
section: "\xA7 72(a), (d)(1)(B)(i)",
|
|
32629
|
+
url: "https://www.law.cornell.edu/uscode/text/26/72",
|
|
32630
|
+
excerpt: "Except as otherwise provided in this chapter, gross income includes any amount received as an annuity\u2026 gross income shall not include so much of any monthly annuity payment\u2026 as does not exceed [the \xA7 72(d) exclusion]. [Feeds gross income alongside the already-taxable taxablePensionsAndAnnuities fact \u2014 the two inputs are additive for separate pensions.]"
|
|
32631
|
+
},
|
|
32632
|
+
...FROM13,
|
|
32633
|
+
output: { type: "money" },
|
|
32634
|
+
formula: {
|
|
32635
|
+
kind: "max0",
|
|
32636
|
+
arg: {
|
|
32637
|
+
kind: "sub",
|
|
32638
|
+
left: fact32("pensionGrossPayments"),
|
|
32639
|
+
right: ruleRef27("us.federal.pension.simplified_method_exclusion")
|
|
32640
|
+
}
|
|
32641
|
+
}
|
|
32642
|
+
}
|
|
32643
|
+
];
|
|
32644
|
+
|
|
32645
|
+
// ../corpus-us-federal/dist/rules/rental.js
|
|
32646
|
+
var J24 = "us.federal";
|
|
32647
|
+
var FROM14 = { effectiveFrom: "2025-01-01" };
|
|
32648
|
+
var fact33 = (factId) => ({ kind: "fact", factId });
|
|
32649
|
+
var money30 = (cents) => ({ kind: "money", cents });
|
|
32650
|
+
var int3 = (value) => ({ kind: "int", value });
|
|
32651
|
+
var ruleRef28 = (ruleId) => ({ kind: "rule", ruleId });
|
|
32652
|
+
var zero21 = money30("0");
|
|
32653
|
+
var YEAR1_RATE_PER_100K = [
|
|
32654
|
+
"3485",
|
|
32655
|
+
// Jan
|
|
32656
|
+
"3182",
|
|
32657
|
+
// Feb
|
|
32658
|
+
"2879",
|
|
32659
|
+
// Mar
|
|
32660
|
+
"2576",
|
|
32661
|
+
// Apr
|
|
32662
|
+
"2273",
|
|
32663
|
+
// May
|
|
32664
|
+
"1970",
|
|
32665
|
+
// Jun
|
|
32666
|
+
"1667",
|
|
32667
|
+
// Jul
|
|
32668
|
+
"1364",
|
|
32669
|
+
// Aug
|
|
32670
|
+
"1061",
|
|
32671
|
+
// Sep
|
|
32672
|
+
"758",
|
|
32673
|
+
// Oct
|
|
32674
|
+
"455",
|
|
32675
|
+
// Nov
|
|
32676
|
+
"152"
|
|
32677
|
+
// Dec
|
|
32678
|
+
];
|
|
32679
|
+
var STEADY_RATE_PER_100K = "3636";
|
|
32680
|
+
var rateOf = (per100k) => ({
|
|
32681
|
+
kind: "mulRate",
|
|
32682
|
+
base: fact33("rentalDepreciableBasis"),
|
|
32683
|
+
rate: { num: per100k, den: "100000" },
|
|
32684
|
+
round: "half-up"
|
|
32685
|
+
});
|
|
32686
|
+
var monthTable = () => {
|
|
32687
|
+
let expr = rateOf(STEADY_RATE_PER_100K);
|
|
32688
|
+
for (let m = 12; m >= 1; m--) {
|
|
32689
|
+
expr = {
|
|
32690
|
+
kind: "if",
|
|
32691
|
+
cond: {
|
|
32692
|
+
kind: "cmp",
|
|
32693
|
+
op: "eq",
|
|
32694
|
+
left: fact33("rentalPlacedInServiceMonth"),
|
|
32695
|
+
right: int3(String(m))
|
|
32696
|
+
},
|
|
32697
|
+
then: rateOf(YEAR1_RATE_PER_100K[m - 1]),
|
|
32698
|
+
else: expr
|
|
32699
|
+
};
|
|
32700
|
+
}
|
|
32701
|
+
return expr;
|
|
32702
|
+
};
|
|
32703
|
+
var CITE_BASE = {
|
|
32704
|
+
source: "26 U.S.C. \xA7 168; IRS Pub 946 Table A-6; IRS Pub 527 ch. 2",
|
|
32705
|
+
url: "https://www.irs.gov/publications/p946"
|
|
32706
|
+
};
|
|
32707
|
+
var rentalRules = [
|
|
32708
|
+
{
|
|
32709
|
+
id: "us.federal.rental.depreciation",
|
|
32710
|
+
version: 1,
|
|
32711
|
+
jurisdiction: J24,
|
|
32712
|
+
title: "Residential rental depreciation (\xA7 168 GDS: 27.5-yr straight line, mid-month)",
|
|
32713
|
+
citation: {
|
|
32714
|
+
...CITE_BASE,
|
|
32715
|
+
section: "\xA7 168(b)(3)(B), (c), (d)(2)",
|
|
32716
|
+
excerpt: "In the case of residential rental property\u2026 the applicable depreciation method is the straight line method\u2026 the applicable recovery period is 27.5 years\u2026 the applicable convention is the mid-month convention. [Pub 946 Table A-6 year-1 percentages by placed-in-service month: 3.485, 3.182, 2.879, 2.576, 2.273, 1.970, 1.667, 1.364, 1.061, 0.758, 0.455, 0.152; full years thereafter 3.636 \u2014 the printed three-decimal table, which is what filed returns use. Basis is the BUILDING only (land never depreciates, \xA7 167). Not modeled, disclosed: ADS elections, separately-dated improvements, the final recovery year's stub, and mid-year dispositions.]"
|
|
32717
|
+
},
|
|
32718
|
+
...FROM14,
|
|
32719
|
+
output: { type: "money" },
|
|
32720
|
+
formula: {
|
|
32721
|
+
kind: "if",
|
|
32722
|
+
cond: { kind: "cmp", op: "eq", left: fact33("rentalDepreciableBasis"), right: zero21 },
|
|
32723
|
+
then: zero21,
|
|
32724
|
+
else: monthTable()
|
|
32725
|
+
}
|
|
32726
|
+
},
|
|
32727
|
+
{
|
|
32728
|
+
id: "us.federal.rental.net_income",
|
|
32729
|
+
version: 1,
|
|
32730
|
+
jurisdiction: J24,
|
|
32731
|
+
title: "Net rental income: Schedule E result before depreciation minus \xA7 168 depreciation",
|
|
32732
|
+
citation: {
|
|
32733
|
+
...CITE_BASE,
|
|
32734
|
+
section: "\xA7 62(a)(4); Schedule E (Form 1040) line 18",
|
|
32735
|
+
excerpt: "Deductions attributable to rents\u2026 are allowed in arriving at adjusted gross income (\xA7 62(a)(4)); Schedule E line 18: 'Depreciation expense or depletion.' [When the computed depreciation EXCEEDS the pre-depreciation rental result, the year is a rental LOSS governed by the \xA7 469 passive-activity rules \u2014 enter it through rentalActiveParticipationLosses (the $25,000 allowance machinery) instead; this rule refuses rather than route a loss around \xA7 469.]"
|
|
32736
|
+
},
|
|
32737
|
+
...FROM14,
|
|
32738
|
+
output: { type: "money" },
|
|
32739
|
+
formula: {
|
|
32740
|
+
kind: "if",
|
|
32741
|
+
cond: {
|
|
32742
|
+
kind: "cmp",
|
|
32743
|
+
op: "gt",
|
|
32744
|
+
left: ruleRef28("us.federal.rental.depreciation"),
|
|
32745
|
+
right: fact33("rentalIncomeBeforeDepreciation")
|
|
32746
|
+
},
|
|
32747
|
+
then: {
|
|
32748
|
+
kind: "if",
|
|
32749
|
+
cond: { kind: "cmp", op: "eq", left: fact33("rentalIncomeBeforeDepreciation"), right: zero21 },
|
|
32750
|
+
then: zero21,
|
|
32751
|
+
// depreciation facts present but no income routed through this path: nothing to net
|
|
32752
|
+
else: {
|
|
32753
|
+
kind: "unsupported",
|
|
32754
|
+
reason: "depreciation exceeds the pre-depreciation rental result \u2014 this year is a rental LOSS subject to the \xA7 469 passive-activity limits: enter it via rentalActiveParticipationLosses (the $25,000 active-participation allowance applies) rather than through this income path"
|
|
32755
|
+
}
|
|
32756
|
+
},
|
|
32757
|
+
else: {
|
|
32758
|
+
kind: "sub",
|
|
32759
|
+
left: fact33("rentalIncomeBeforeDepreciation"),
|
|
32760
|
+
right: ruleRef28("us.federal.rental.depreciation")
|
|
32761
|
+
}
|
|
32762
|
+
}
|
|
32763
|
+
}
|
|
32764
|
+
];
|
|
32765
|
+
|
|
32766
|
+
// ../corpus-us-federal/dist/rules/schedule-d.js
|
|
32767
|
+
var J25 = "us.federal";
|
|
32768
|
+
var FROM15 = { effectiveFrom: "2025-01-01" };
|
|
32769
|
+
var fact34 = (factId) => ({ kind: "fact", factId });
|
|
32770
|
+
var money31 = (cents) => ({ kind: "money", cents });
|
|
32771
|
+
var zero22 = money31("0");
|
|
32772
|
+
var gt09 = (e) => ({ kind: "cmp", op: "gt", left: e, right: zero22 });
|
|
32773
|
+
var max03 = (arg) => ({ kind: "max0", arg });
|
|
32774
|
+
var sub = (left, right) => ({ kind: "sub", left, right });
|
|
32775
|
+
var add = (...args) => ({ kind: "add", args });
|
|
32776
|
+
var and = (...args) => ({ kind: "and", args });
|
|
32777
|
+
var or = (...args) => ({ kind: "or", args });
|
|
32778
|
+
var stG = fact34("shortTermCapitalGains");
|
|
32779
|
+
var stL = fact34("shortTermCapitalLoss");
|
|
32780
|
+
var ltG = fact34("longTermCapitalGains");
|
|
32781
|
+
var ltL = fact34("longTermCapitalLoss");
|
|
32782
|
+
var legacyLoss = fact34("netCapitalLoss");
|
|
32783
|
+
var CONTRADICTORY = or(and(gt09(stG), gt09(stL)), and(gt09(ltG), gt09(ltL)), and(gt09(legacyLoss), or(gt09(stG), gt09(ltG), gt09(stL), gt09(ltL))));
|
|
32784
|
+
var refuse = (computation) => ({
|
|
32785
|
+
kind: "if",
|
|
32786
|
+
cond: CONTRADICTORY,
|
|
32787
|
+
then: {
|
|
32788
|
+
kind: "unsupported",
|
|
32789
|
+
reason: "contradictory Schedule D facts \u2014 each capital fact is a NET result: a bucket takes a gain OR a loss (never both), and the overall netCapitalLoss cannot be combined with per-bucket facts"
|
|
32790
|
+
},
|
|
32791
|
+
else: computation
|
|
32792
|
+
});
|
|
32793
|
+
var CITE5 = {
|
|
32794
|
+
source: "26 U.S.C. \xA7 1222; 2025 Schedule D (Form 1040)",
|
|
32795
|
+
url: "https://www.law.cornell.edu/uscode/text/26/1222"
|
|
32796
|
+
};
|
|
32797
|
+
var scheduleDRules = [
|
|
32798
|
+
{
|
|
32799
|
+
id: "us.federal.schedule_d.preferential_lt_gain",
|
|
32800
|
+
version: 1,
|
|
32801
|
+
jurisdiction: J25,
|
|
32802
|
+
title: "Net capital gain (\xA7 1222(11)): net LT gain reduced by any net ST loss",
|
|
32803
|
+
citation: {
|
|
32804
|
+
...CITE5,
|
|
32805
|
+
section: "\xA7 1222(11)",
|
|
32806
|
+
excerpt: "The term 'net capital gain' means the excess of the net long-term capital gain for the taxable year over the net short-term capital loss for such year. [Schedule D: line 16 combines line 7 (net short-term) and line 15 (net long-term); when line 7 is a loss and line 15 a gain, the remainder keeps long-term character and prices through the \xA7 1(h) stack. Inputs are per-bucket NET results; contradictory combinations refuse.]"
|
|
32807
|
+
},
|
|
32808
|
+
...FROM15,
|
|
32809
|
+
output: { type: "money" },
|
|
32810
|
+
formula: refuse(max03(sub(sub(ltG, ltL), max03(sub(stL, stG)))))
|
|
32811
|
+
},
|
|
32812
|
+
{
|
|
32813
|
+
id: "us.federal.schedule_d.ordinary_st_gain",
|
|
32814
|
+
version: 1,
|
|
32815
|
+
jurisdiction: J25,
|
|
32816
|
+
title: "Net short-term capital gain surviving netting (\xA7 1222(5), (9)): ordinary-rate",
|
|
32817
|
+
citation: {
|
|
32818
|
+
...CITE5,
|
|
32819
|
+
section: "\xA7 1222(5), (9)",
|
|
32820
|
+
excerpt: "'Net short-term capital gain' means the excess of short-term capital gains for the taxable year over the short-term capital losses for such year\u2026 'net long-term capital loss' means the excess of long-term capital losses\u2026 over the long-term capital gains. [Schedule D line 16: a net LT loss reduces the net ST gain; whatever short-term gain survives is ordinary income \u2014 \xA7 1(h) prices only the \xA7 1222(11) net capital gain.]"
|
|
32821
|
+
},
|
|
32822
|
+
...FROM15,
|
|
32823
|
+
output: { type: "money" },
|
|
32824
|
+
formula: refuse(max03(sub(sub(stG, stL), max03(sub(ltL, ltG)))))
|
|
32825
|
+
},
|
|
32826
|
+
{
|
|
32827
|
+
id: "us.federal.schedule_d.net_loss",
|
|
32828
|
+
version: 1,
|
|
32829
|
+
jurisdiction: J25,
|
|
32830
|
+
title: "Net capital loss after netting (\xA7 1222(10)), feeding the \xA7 1211(b) offset",
|
|
32831
|
+
citation: {
|
|
32832
|
+
...CITE5,
|
|
32833
|
+
section: "\xA7 1222(10)",
|
|
32834
|
+
excerpt: "The term 'net capital loss' means the excess of the losses from sales or exchanges of capital assets over the sum allowed under section 1211. [Computed as the overall negative of Schedule D line 16, entered here positive: the legacy netCapitalLoss fact passes through unchanged, or the per-bucket facts net to a loss when bucket losses exceed bucket gains. \xA7 1211(b) caps the ordinary-income offset; \xA7 1212(b) carries the rest forward.]"
|
|
32835
|
+
},
|
|
32836
|
+
...FROM15,
|
|
32837
|
+
output: { type: "money" },
|
|
32838
|
+
formula: refuse(add(legacyLoss, max03(sub(add(stL, ltL), add(stG, ltG)))))
|
|
32839
|
+
}
|
|
32840
|
+
];
|
|
32841
|
+
|
|
32842
|
+
// ../corpus-us-federal/dist/rules/schedule-r.js
|
|
32843
|
+
var J26 = "us.federal";
|
|
32844
|
+
var fact35 = (factId) => ({ kind: "fact", factId });
|
|
32845
|
+
var money32 = (cents) => ({ kind: "money", cents });
|
|
32846
|
+
var ruleRef29 = (ruleId) => ({ kind: "rule", ruleId });
|
|
32847
|
+
var zero23 = money32("0");
|
|
31951
32848
|
var isStatus9 = (v) => ({
|
|
31952
32849
|
kind: "cmp",
|
|
31953
32850
|
op: "eq",
|
|
31954
|
-
left:
|
|
32851
|
+
left: fact35("filingStatus"),
|
|
31955
32852
|
right: { kind: "enum", value: v }
|
|
31956
32853
|
});
|
|
31957
32854
|
var qualified = {
|
|
31958
32855
|
kind: "or",
|
|
31959
|
-
args: [
|
|
32856
|
+
args: [fact35("isAge65OrOlder"), fact35("isRetiredOnTotalDisability")]
|
|
31960
32857
|
};
|
|
31961
32858
|
var initialByStatus = {
|
|
31962
32859
|
kind: "if",
|
|
31963
32860
|
cond: isStatus9("mfs"),
|
|
31964
|
-
then:
|
|
32861
|
+
then: money32("375000"),
|
|
31965
32862
|
else: {
|
|
31966
32863
|
kind: "if",
|
|
31967
32864
|
cond: {
|
|
31968
32865
|
kind: "and",
|
|
31969
32866
|
args: [
|
|
31970
32867
|
isStatus9("mfj"),
|
|
31971
|
-
|
|
32868
|
+
fact35("spouseIsAge65OrOlder")
|
|
31972
32869
|
// spouse-disabled qualification not modeled — disclosed
|
|
31973
32870
|
]
|
|
31974
32871
|
},
|
|
31975
|
-
then:
|
|
31976
|
-
else:
|
|
32872
|
+
then: money32("750000"),
|
|
32873
|
+
else: money32("500000")
|
|
31977
32874
|
}
|
|
31978
32875
|
};
|
|
31979
32876
|
var initial = {
|
|
31980
32877
|
kind: "if",
|
|
31981
|
-
cond:
|
|
32878
|
+
cond: fact35("isAge65OrOlder"),
|
|
31982
32879
|
then: initialByStatus,
|
|
31983
|
-
else: { kind: "min", args: [initialByStatus,
|
|
32880
|
+
else: { kind: "min", args: [initialByStatus, fact35("scheduleRDisabilityIncome")] }
|
|
31984
32881
|
};
|
|
31985
32882
|
var agiThreshold = {
|
|
31986
32883
|
kind: "if",
|
|
31987
32884
|
cond: isStatus9("mfj"),
|
|
31988
|
-
then:
|
|
32885
|
+
then: money32("1000000"),
|
|
31989
32886
|
// $10,000
|
|
31990
32887
|
else: {
|
|
31991
32888
|
kind: "if",
|
|
31992
32889
|
cond: isStatus9("mfs"),
|
|
31993
|
-
then:
|
|
32890
|
+
then: money32("500000"),
|
|
31994
32891
|
// $5,000
|
|
31995
|
-
else:
|
|
32892
|
+
else: money32("750000")
|
|
31996
32893
|
// $7,500
|
|
31997
32894
|
}
|
|
31998
32895
|
};
|
|
@@ -32004,12 +32901,12 @@ var section22Amount = {
|
|
|
32004
32901
|
right: {
|
|
32005
32902
|
kind: "add",
|
|
32006
32903
|
args: [
|
|
32007
|
-
|
|
32904
|
+
fact35("nontaxableBenefitsForScheduleR"),
|
|
32008
32905
|
{
|
|
32009
32906
|
kind: "mulRate",
|
|
32010
32907
|
base: {
|
|
32011
32908
|
kind: "max0",
|
|
32012
|
-
arg: { kind: "sub", left:
|
|
32909
|
+
arg: { kind: "sub", left: ruleRef29("us.federal.agi"), right: agiThreshold }
|
|
32013
32910
|
},
|
|
32014
32911
|
rate: { num: "50", den: "100" },
|
|
32015
32912
|
round: "half-up"
|
|
@@ -32028,7 +32925,7 @@ var scheduleRRules = [
|
|
|
32028
32925
|
{
|
|
32029
32926
|
id: "us.federal.schedule_r.tentative",
|
|
32030
32927
|
version: 1,
|
|
32031
|
-
jurisdiction:
|
|
32928
|
+
jurisdiction: J26,
|
|
32032
32929
|
title: "Credit for the elderly or the disabled \u2014 tentative (\xA7 22, Schedule R)",
|
|
32033
32930
|
citation: CITATION2,
|
|
32034
32931
|
effectiveFrom: "2025-01-01",
|
|
@@ -32042,7 +32939,7 @@ var scheduleRRules = [
|
|
|
32042
32939
|
qualified,
|
|
32043
32940
|
{
|
|
32044
32941
|
kind: "or",
|
|
32045
|
-
args: [{ kind: "not", arg: isStatus9("mfs") },
|
|
32942
|
+
args: [{ kind: "not", arg: isStatus9("mfs") }, fact35("mfsLivedApartAllYear")]
|
|
32046
32943
|
}
|
|
32047
32944
|
]
|
|
32048
32945
|
},
|
|
@@ -32056,13 +32953,13 @@ var scheduleRRules = [
|
|
|
32056
32953
|
},
|
|
32057
32954
|
mode: "half-up"
|
|
32058
32955
|
},
|
|
32059
|
-
else:
|
|
32956
|
+
else: zero23
|
|
32060
32957
|
}
|
|
32061
32958
|
},
|
|
32062
32959
|
{
|
|
32063
32960
|
id: "us.federal.schedule_r_credit",
|
|
32064
32961
|
version: 1,
|
|
32065
|
-
jurisdiction:
|
|
32962
|
+
jurisdiction: J26,
|
|
32066
32963
|
title: "Schedule R credit, limited to remaining tax (\xA7 26(a) after CDCC + education + saver's + adoption)",
|
|
32067
32964
|
citation: CITATION2,
|
|
32068
32965
|
effectiveFrom: "2025-01-01",
|
|
@@ -32070,7 +32967,7 @@ var scheduleRRules = [
|
|
|
32070
32967
|
formula: {
|
|
32071
32968
|
kind: "min",
|
|
32072
32969
|
args: [
|
|
32073
|
-
|
|
32970
|
+
ruleRef29("us.federal.schedule_r.tentative"),
|
|
32074
32971
|
{
|
|
32075
32972
|
kind: "max0",
|
|
32076
32973
|
arg: {
|
|
@@ -32078,17 +32975,17 @@ var scheduleRRules = [
|
|
|
32078
32975
|
left: {
|
|
32079
32976
|
kind: "add",
|
|
32080
32977
|
args: [
|
|
32081
|
-
|
|
32082
|
-
|
|
32978
|
+
ruleRef29("us.federal.income_tax_before_credits"),
|
|
32979
|
+
ruleRef29("us.federal.amt")
|
|
32083
32980
|
]
|
|
32084
32981
|
},
|
|
32085
32982
|
right: {
|
|
32086
32983
|
kind: "add",
|
|
32087
32984
|
args: [
|
|
32088
|
-
|
|
32089
|
-
|
|
32090
|
-
|
|
32091
|
-
|
|
32985
|
+
ruleRef29("us.federal.cdcc"),
|
|
32986
|
+
ruleRef29("us.federal.education.nonrefundable"),
|
|
32987
|
+
ruleRef29("us.federal.savers_credit"),
|
|
32988
|
+
ruleRef29("us.federal.adoption.nonrefundable")
|
|
32092
32989
|
]
|
|
32093
32990
|
}
|
|
32094
32991
|
}
|
|
@@ -32099,23 +32996,23 @@ var scheduleRRules = [
|
|
|
32099
32996
|
];
|
|
32100
32997
|
|
|
32101
32998
|
// ../corpus-us-federal/dist/rules/state-helpers.js
|
|
32102
|
-
var
|
|
32103
|
-
var
|
|
32104
|
-
var
|
|
32999
|
+
var fact36 = (factId) => ({ kind: "fact", factId });
|
|
33000
|
+
var money33 = (cents) => ({ kind: "money", cents });
|
|
33001
|
+
var ruleRef30 = (ruleId) => ({ kind: "rule", ruleId });
|
|
32105
33002
|
var isStatus10 = (v) => ({
|
|
32106
33003
|
kind: "cmp",
|
|
32107
33004
|
op: "eq",
|
|
32108
|
-
left:
|
|
33005
|
+
left: fact36("filingStatus"),
|
|
32109
33006
|
right: { kind: "enum", value: v }
|
|
32110
33007
|
});
|
|
32111
33008
|
var printedSchedule = (base, rows) => {
|
|
32112
33009
|
const rowExpr = (r) => ({
|
|
32113
33010
|
kind: "add",
|
|
32114
33011
|
args: [
|
|
32115
|
-
|
|
33012
|
+
money33(r.fixedCents),
|
|
32116
33013
|
{
|
|
32117
33014
|
kind: "mulRate",
|
|
32118
|
-
base: { kind: "sub", left: base, right:
|
|
33015
|
+
base: { kind: "sub", left: base, right: money33(r.thresholdCents) },
|
|
32119
33016
|
rate: r.rate,
|
|
32120
33017
|
round: "half-up"
|
|
32121
33018
|
}
|
|
@@ -32125,7 +33022,7 @@ var printedSchedule = (base, rows) => {
|
|
|
32125
33022
|
for (let i = rows.length - 2; i >= 0; i--) {
|
|
32126
33023
|
expr = {
|
|
32127
33024
|
kind: "if",
|
|
32128
|
-
cond: { kind: "cmp", op: "le", left: base, right:
|
|
33025
|
+
cond: { kind: "cmp", op: "le", left: base, right: money33(rows[i + 1].thresholdCents) },
|
|
32129
33026
|
then: rowExpr(rows[i]),
|
|
32130
33027
|
else: expr
|
|
32131
33028
|
};
|
|
@@ -32145,7 +33042,7 @@ var ilRules = [
|
|
|
32145
33042
|
source: "35 ILCS 5/201(b)(5.4); 2025 Form IL-1040 instructions",
|
|
32146
33043
|
section: "IL-1040 line 12",
|
|
32147
33044
|
url: "https://tax.illinois.gov/content/dam/soi/en/web/tax/forms/incometax/documents/currentyear/individual/il-1040-instr.pdf",
|
|
32148
|
-
excerpt: "Residents: Multiply Line 11 by 4.95% (.0495). Cannot be less than zero (2025 IL-1040 line 12, verbatim). LINE FLOORS (2025 instructions): line 9 base income AND line 11 net income 'may not be less than zero \u2014 if the result is a negative number, enter 0'. Related 2025 parameters (IDOR 2025 IL-1040 + Schedule ICR + Schedule 1299-C instructions + Pub-132, web-verified July 2026): exemption allowance $2,850 per exemption PLUS $1,000 per age-65+ box and $1,000 per blind box (taxpayer/spouse, per box \u2014 65 AND blind = $2,000); the ENTIRE line 10 allowance is $0 if federal AGI exceeds $500,000 MFJ / $250,000 all others. Schedule ICR (nonrefundable, total cannot exceed tax due, same AGI caps): property tax credit = 5% of IL property tax on the principal residence (exclude business-use portion); K-12 education expense credit = 25% of qualified expenses OVER $250, max $750 \u2014 HOME-SCHOOL expenses are NOT qualified (35 ILCS 5/201(m): public/nonpublic school tuition, book and lab fees only; exclude school_type home entries before summing). Schedule 1299-C K-12 Instructional Materials & Supplies credit (900+ hour educators): up to $500 ($1,000 MFJ both educators). Illinois EITC = 20% of the federal EITC (refundable) \u2014 BUT for a taxpayer age 18-24 or 65+ with NO qualifying children, 35 ILCS 5/212(b-5)/(b-10) (P.A. 102-700) decouples from the federal \xA7 32(c)(1)(A)(ii) childless age floor: use the us.il.eitc target (20% of us.federal.eitc_no_age_gate) instead of 20% x the ordinary federal EITC, which is correctly $0 for that population and would understate line 29. Illinois Child Tax Credit (TY2025) = 40% of the Illinois EITC for taxpayers with a dependent child under 12. [Input: IL net income (line 11 \u2014 base income minus exemptions), computed by the preparer;
|
|
33045
|
+
excerpt: "Residents: Multiply Line 11 by 4.95% (.0495). Cannot be less than zero (2025 IL-1040 line 12, verbatim). LINE FLOORS (2025 instructions): line 9 base income AND line 11 net income 'may not be less than zero \u2014 if the result is a negative number, enter 0'. Related 2025 parameters (IDOR 2025 IL-1040 + Schedule ICR + Schedule 1299-C instructions + Pub-132, web-verified July 2026): exemption allowance $2,850 per exemption PLUS $1,000 per age-65+ box and $1,000 per blind box (taxpayer/spouse, per box \u2014 65 AND blind = $2,000); the ENTIRE line 10 allowance is $0 if federal AGI exceeds $500,000 MFJ / $250,000 all others. Schedule ICR (nonrefundable, total cannot exceed tax due, same AGI caps): property tax credit = 5% of IL property tax on the principal residence (exclude business-use portion); K-12 education expense credit = 25% of qualified expenses OVER $250, max $750 \u2014 HOME-SCHOOL expenses are NOT qualified (35 ILCS 5/201(m): public/nonpublic school tuition, book and lab fees only; exclude school_type home entries before summing). Schedule 1299-C K-12 Instructional Materials & Supplies credit (900+ hour educators): up to $500 ($1,000 MFJ both educators). Illinois EITC = 20% of the federal EITC (refundable) \u2014 BUT for a taxpayer age 18-24 or 65+ with NO qualifying children, 35 ILCS 5/212(b-5)/(b-10) (P.A. 102-700) decouples from the federal \xA7 32(c)(1)(A)(ii) childless age floor: use the us.il.eitc target (20% of us.federal.eitc_no_age_gate) instead of 20% x the ordinary federal EITC, which is correctly $0 for that population and would understate line 29. Illinois Child Tax Credit (TY2025) = 40% of the Illinois EITC for taxpayers with a dependent child under 12. [Input: IL net income (line 11 \u2014 base income minus exemptions), computed by the preparer; worked example: 30,485 \xD7 4.95% = 1,509.] ESTIMATED TAX (IL-2210, 2025): required if IL tax after withholding/credits will exceed $1,000. SCHEDULE NR: part-year and nonresidents apportion base income on Schedule NR \u2014 this target computes RESIDENT tax on already-apportioned net income only; the Schedule NR apportionment worksheet itself is not modeled (refuses/agent-composed)."
|
|
32149
33046
|
},
|
|
32150
33047
|
effectiveFrom: "2025-01-01",
|
|
32151
33048
|
effectiveTo: "2026-01-01",
|
|
@@ -32176,7 +33073,7 @@ var ilRules = [
|
|
|
32176
33073
|
},
|
|
32177
33074
|
formula: {
|
|
32178
33075
|
kind: "mulRate",
|
|
32179
|
-
base: { kind: "max0", arg:
|
|
33076
|
+
base: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32180
33077
|
rate: { num: "495", den: "10000" },
|
|
32181
33078
|
round: "half-up"
|
|
32182
33079
|
}
|
|
@@ -32202,7 +33099,7 @@ var ilRules = [
|
|
|
32202
33099
|
kind: "roundToDollar",
|
|
32203
33100
|
value: {
|
|
32204
33101
|
kind: "mulRate",
|
|
32205
|
-
base:
|
|
33102
|
+
base: ruleRef30("us.federal.eitc_no_age_gate"),
|
|
32206
33103
|
rate: { num: "20", den: "100" },
|
|
32207
33104
|
round: "half-up"
|
|
32208
33105
|
},
|
|
@@ -32274,35 +33171,35 @@ var ilRules = [
|
|
|
32274
33171
|
},
|
|
32275
33172
|
formula: {
|
|
32276
33173
|
kind: "if",
|
|
32277
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33174
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier1Max" } },
|
|
32278
33175
|
then: { kind: "param", name: "tier1Tax" },
|
|
32279
33176
|
else: {
|
|
32280
33177
|
kind: "if",
|
|
32281
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33178
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier2Max" } },
|
|
32282
33179
|
then: { kind: "param", name: "tier2Tax" },
|
|
32283
33180
|
else: {
|
|
32284
33181
|
kind: "if",
|
|
32285
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33182
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier3Max" } },
|
|
32286
33183
|
then: { kind: "param", name: "tier3Tax" },
|
|
32287
33184
|
else: {
|
|
32288
33185
|
kind: "if",
|
|
32289
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33186
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier4Max" } },
|
|
32290
33187
|
then: { kind: "param", name: "tier4Tax" },
|
|
32291
33188
|
else: {
|
|
32292
33189
|
kind: "if",
|
|
32293
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33190
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier5Max" } },
|
|
32294
33191
|
then: { kind: "param", name: "tier5Tax" },
|
|
32295
33192
|
else: {
|
|
32296
33193
|
kind: "if",
|
|
32297
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33194
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier6Max" } },
|
|
32298
33195
|
then: { kind: "param", name: "tier6Tax" },
|
|
32299
33196
|
else: {
|
|
32300
33197
|
kind: "if",
|
|
32301
|
-
cond: { kind: "cmp", op: "le", left:
|
|
33198
|
+
cond: { kind: "cmp", op: "le", left: ruleRef30("us.federal.agi"), right: { kind: "param", name: "tier7Max" } },
|
|
32302
33199
|
then: { kind: "param", name: "tier7Tax" },
|
|
32303
33200
|
else: {
|
|
32304
33201
|
kind: "roundToDollar",
|
|
32305
|
-
value: { kind: "mulRate", base:
|
|
33202
|
+
value: { kind: "mulRate", base: ruleRef30("us.federal.agi"), rate: { num: "5", den: "10000" }, round: "half-up" },
|
|
32306
33203
|
mode: "half-up"
|
|
32307
33204
|
}
|
|
32308
33205
|
}
|
|
@@ -32327,7 +33224,7 @@ var vaRules = [
|
|
|
32327
33224
|
source: "Va. Code \xA7 58.1-320; 2025 Form 760 instructions (tax.virginia.gov PDF)",
|
|
32328
33225
|
section: "\xA7 58.1-320; Form 760 line 16",
|
|
32329
33226
|
url: "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2025-760-instructions.pdf",
|
|
32330
|
-
excerpt: "2% on the first $3,000; 3% over $3,000 to $5,000; 5% over $5,000 to $17,000; 5.75% over $17,000 (Va. Code \xA7 58.1-320, single schedule for all statuses). THE TABLE CONVENTION (this rule's default): the published VA Tax Table's rows are back-solved from clean whole-dollar tax increments \u2014 $2 per row above $5,000 on an ODD-dollar grid \u2014 so table tax = the nearest odd dollar to the rate-schedule value (ties up); verified against the printed row 44,026\u201344,061 \u2192 $2,275 (2024/2023 booklets; the 2025 booklet points to the same online table). useFormulaMethod=true gives the raw schedule with half-up rounding instead (the 2025 instructions' own $90,000 example: 4,917.50 \u2192 4,918). Related 2025 parameters (2025 Form 760 instructions, web-verified July 2026): standard deduction $8,750 single/MFS / $17,500 MFJ (TY2025\u20132026, see us.va.standard_deduction for the enacted TY2027+ windows); exemption $930/person + $800 each age-65-or-blind (taxpayer/spouse only); AGE DEDUCTION (Va. Code \xA7 58.1-322.03(2)): $12,000 per person 65+, UNREDUCED if born on/before 1/1/1939, else reduced $1-for-$1 by AFAGI over $50,000 single / $75,000 married (joint AFAGI); Credit for Low Income Individuals: $300 \xD7 (personal + dependent exemptions, EXCLUDING 65/blind extras), nonrefundable, gated on family VAGI \u2264 poverty guideline (2025: $15,650 for 1 + $5,500 each additional person) \u2014 mutually exclusive with the Virginia EITC; Virginia EITC 2025: 20% of federal EITC, REFUNDABLE at 20% for TY2025\u20132029 (Va. Code \xA7 58.1-339.8, as amended by the 2026 Appropriation Act \u2014 the refundable 20% rate was extended through TY2029, superseding the earlier TY2026 sunset; do not use the stale 15% or a TY2026 cutoff) or nonrefundable 20%. VA has no HOH status: federal HOH files as single-column. NONRESIDENT/PART-YEAR RETURNS: Form 763 (nonresident) and 760PY (part-year) compute Virginia-source-income allocation on their own worksheets \u2014 NOT modeled here (refuses/agent-composed); this target is the RESIDENT Form 760 computation on already-determined Virginia taxable income only. [Input: Virginia taxable income (Form 760 line 15);
|
|
33227
|
+
excerpt: "2% on the first $3,000; 3% over $3,000 to $5,000; 5% over $5,000 to $17,000; 5.75% over $17,000 (Va. Code \xA7 58.1-320, single schedule for all statuses). THE TABLE CONVENTION (this rule's default): the published VA Tax Table's rows are back-solved from clean whole-dollar tax increments \u2014 $2 per row above $5,000 on an ODD-dollar grid \u2014 so table tax = the nearest odd dollar to the rate-schedule value (ties up); verified against the printed row 44,026\u201344,061 \u2192 $2,275 (2024/2023 booklets; the 2025 booklet points to the same online table). useFormulaMethod=true gives the raw schedule with half-up rounding instead (the 2025 instructions' own $90,000 example: 4,917.50 \u2192 4,918). Related 2025 parameters (2025 Form 760 instructions, web-verified July 2026): standard deduction $8,750 single/MFS / $17,500 MFJ (TY2025\u20132026, see us.va.standard_deduction for the enacted TY2027+ windows); exemption $930/person + $800 each age-65-or-blind (taxpayer/spouse only); AGE DEDUCTION (Va. Code \xA7 58.1-322.03(2)): $12,000 per person 65+, UNREDUCED if born on/before 1/1/1939, else reduced $1-for-$1 by AFAGI over $50,000 single / $75,000 married (joint AFAGI); Credit for Low Income Individuals: $300 \xD7 (personal + dependent exemptions, EXCLUDING 65/blind extras), nonrefundable, gated on family VAGI \u2264 poverty guideline (2025: $15,650 for 1 + $5,500 each additional person) \u2014 mutually exclusive with the Virginia EITC; Virginia EITC 2025: 20% of federal EITC, REFUNDABLE at 20% for TY2025\u20132029 (Va. Code \xA7 58.1-339.8, as amended by the 2026 Appropriation Act \u2014 the refundable 20% rate was extended through TY2029, superseding the earlier TY2026 sunset; do not use the stale 15% or a TY2026 cutoff) or nonrefundable 20%. VA has no HOH status: federal HOH files as single-column. NONRESIDENT/PART-YEAR RETURNS: Form 763 (nonresident) and 760PY (part-year) compute Virginia-source-income allocation on their own worksheets \u2014 NOT modeled here (refuses/agent-composed); this target is the RESIDENT Form 760 computation on already-determined Virginia taxable income only. [Input: Virginia taxable income (Form 760 line 15); worked examples: tax(44,060) = 2,275 (table); tax(34,030) = 1,699 (both methods agree).]"
|
|
32331
33228
|
},
|
|
32332
33229
|
effectiveFrom: "2025-01-01",
|
|
32333
33230
|
effectiveTo: "2026-01-01",
|
|
@@ -32359,12 +33256,12 @@ var vaRules = [
|
|
|
32359
33256
|
},
|
|
32360
33257
|
formula: {
|
|
32361
33258
|
kind: "if",
|
|
32362
|
-
cond:
|
|
33259
|
+
cond: fact36("useFormulaMethod"),
|
|
32363
33260
|
then: {
|
|
32364
33261
|
kind: "roundToDollar",
|
|
32365
33262
|
value: {
|
|
32366
33263
|
kind: "brackets",
|
|
32367
|
-
base: { kind: "max0", arg:
|
|
33264
|
+
base: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32368
33265
|
table: [
|
|
32369
33266
|
{ threshold: "0", rate: { num: "2", den: "100" } },
|
|
32370
33267
|
{ threshold: "300000", rate: { num: "3", den: "100" } },
|
|
@@ -32380,14 +33277,14 @@ var vaRules = [
|
|
|
32380
33277
|
cond: {
|
|
32381
33278
|
kind: "cmp",
|
|
32382
33279
|
op: "le",
|
|
32383
|
-
left: { kind: "max0", arg:
|
|
33280
|
+
left: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32384
33281
|
right: { kind: "money", cents: "500000" }
|
|
32385
33282
|
},
|
|
32386
33283
|
then: {
|
|
32387
33284
|
kind: "roundToDollar",
|
|
32388
33285
|
value: {
|
|
32389
33286
|
kind: "brackets",
|
|
32390
|
-
base: { kind: "max0", arg:
|
|
33287
|
+
base: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32391
33288
|
table: [
|
|
32392
33289
|
{ threshold: "0", rate: { num: "2", den: "100" } },
|
|
32393
33290
|
{ threshold: "300000", rate: { num: "3", den: "100" } }
|
|
@@ -32397,14 +33294,14 @@ var vaRules = [
|
|
|
32397
33294
|
},
|
|
32398
33295
|
// the printed VA Tax Table ends at $100,000 taxable income — at or
|
|
32399
33296
|
// above it the form's own instruction is the RATE SCHEDULE, exact and
|
|
32400
|
-
// rounded to the nearest dollar (
|
|
33297
|
+
// rounded to the nearest dollar (worked example: 440,857 →
|
|
32401
33298
|
// 25,092, the half-up schedule value, NOT the $2-grid's 25,091)
|
|
32402
33299
|
else: {
|
|
32403
33300
|
kind: "if",
|
|
32404
33301
|
cond: {
|
|
32405
33302
|
kind: "cmp",
|
|
32406
33303
|
op: "ge",
|
|
32407
|
-
left: { kind: "max0", arg:
|
|
33304
|
+
left: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32408
33305
|
right: { kind: "money", cents: "10000000" }
|
|
32409
33306
|
// $100,000
|
|
32410
33307
|
},
|
|
@@ -32412,7 +33309,7 @@ var vaRules = [
|
|
|
32412
33309
|
kind: "roundToDollar",
|
|
32413
33310
|
value: {
|
|
32414
33311
|
kind: "brackets",
|
|
32415
|
-
base: { kind: "max0", arg:
|
|
33312
|
+
base: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32416
33313
|
table: [
|
|
32417
33314
|
{ threshold: "0", rate: { num: "2", den: "100" } },
|
|
32418
33315
|
{ threshold: "300000", rate: { num: "3", den: "100" } },
|
|
@@ -32434,7 +33331,7 @@ var vaRules = [
|
|
|
32434
33331
|
kind: "stepUnits",
|
|
32435
33332
|
value: {
|
|
32436
33333
|
kind: "brackets",
|
|
32437
|
-
base: { kind: "max0", arg:
|
|
33334
|
+
base: { kind: "max0", arg: fact36("stateTaxableIncome") },
|
|
32438
33335
|
table: [
|
|
32439
33336
|
{ threshold: "0", rate: { num: "2", den: "100" } },
|
|
32440
33337
|
{ threshold: "300000", rate: { num: "3", den: "100" } },
|
|
@@ -32493,7 +33390,7 @@ var vaRules = [
|
|
|
32493
33390
|
source: "Va. Code \xA7 58.1-320; 2025 Form 760 instructions, Spouse Tax Adjustment worksheet (p.12-13)",
|
|
32494
33391
|
section: "Form 760 line 17",
|
|
32495
33392
|
url: "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2025-760-instructions.pdf",
|
|
32496
|
-
excerpt: "2025 Spouse Tax Adjustment (STA): available ONLY on a joint (MFJ) return where BOTH spouses have income and their COMBINED Virginia taxable income exceeds $3,000; maximum credit $259 (footer statement + Line 5 shortcut, both confirmed in the TY2025 booklet). NARRATIVE (p.13, verbatim): 'Couples filing jointly under Filing Status 2 may reduce their tax by up to $259 with the STA if both have taxable income to report and their combined taxable income on Line 15 is more than $3,000 ... Using the STA, couples filing joint returns will not pay higher taxes than if they had filed separate returns.' HOW IT WORKS (verbatim): 'Virginia tax rates increase with income: 2% up to $3,000; 3% from $3,001 to $5,000; 5% from $5,001 to $17,000; and 5.75% for income over $17,000. The STA lets both incomes reported on jointly filed returns benefit from the lower tax rates.'\n\nTHE ALLOCATION IS PRESCRIBED, NOT DISCRETIONARY \u2014 built on income-SOURCE attribution, never a proportional wage-ratio or a benefit-maximizing choice. VERBATIM WORKSHEET, PART 1: SEPARATE YOUR INCOME AND EXEMPTIONS (per spouse): '1. Enter the portion of the Virginia Adjusted Gross Income (VAGI) on Line 9 of Form 760 that is related to each spouse. Use the worksheet at the bottom of the page to compute the separate VAGI for each spouse.' '2. Enter separate personal exemption amounts. Enter a 1 in the boxes that apply and multiply the total by $800. Add $930 to the total to compute the personal exemptions for you and spouse. You: 65 or over + Blind = Total x $800 + $930 = ___. Spouse: 65 or over + Blind = Total x $800 + $930 = ___.' (i.e., PER SPOUSE: $930 + $800 x that spouse's own 65-or-over/blind box count \u2014 a fixed formula, never proportional to income.) '3. Subtract Line 2 from Line 1. If either amount is 0 or less, stop here; you do not qualify for this credit.'\n\nVERBATIM WORKSHEET, PART 2: CALCULATE YOUR TAX ADJUSTMENT: '4. Enter the taxable income from Line 15 on Form 760.' '5. Enter the smaller amount from Line 3 above. If this amount is larger than $17,000 and Line 4 is larger than $34,000, skip to Line 12 and enter $259 as the credit.' '6. Subtract Line 5 from Line 4 (if $0 or less, enter $0).' '7. Divide the amount on Line 4 by 2.' '8. Enter the tax on the smaller amount from Line 5 or Line 7. Refer to the tax table or rate schedule.' '9. Enter the tax on the larger amount from Line 6 or Line 7. Refer to the tax table or rate schedule.' (Lines 8-9 use us.va.income_tax's own rate schedule/table on the stated amount \u2014 NOT a fresh computation.) '10. Add Lines 8 and 9.' '11. Enter the tax from Line 16 on Form 760.' '12. TAX ADJUSTMENT: Subtract Line 10 from Line 11. Enter this amount on Line 17 of Form 760.' 'The Spouse Tax Adjustment cannot exceed $259.'\n\nVERBATIM 'WORKSHEET FOR DETERMINING SEPARATE VIRGINIA ADJUSTED GROSS INCOME' (bottom of p.12) \u2014 feeds Part 1 Line 1 above: STEP 1 - Determine Separate Federal Adjusted Gross Income (You / Spouse columns, LITERAL income-source attribution \u2014 each spouse's OWN items, never a ratio): '1. Wages, salaries, etc. 2. Taxable interest and dividend income. 3. Taxable refunds, adjustments, or offsets of state and local income tax. 4. Business income. 5. Capital gains/losses and other gains/losses. 6. Taxable pensions, annuities, and IRA distributions. 7. Rents, royalties, partnerships, estates, trusts, etc. 8. Other income (farm income, taxable social security, etc.). 9. Gross income - add Lines 1 through 8. 10. Adjustments to gross income. 11. FAGI - subtract Line 10 from Line 9. (The total of both columns should equal your joint FAGI reported on your 1040)' STEP 2 - Determine Separate Virginia Adjusted Gross Income: '12. Total additions to FAGI (Form 760, Line 2). 13. Subtotal - add Lines 11 and 12. 14. Age Deduction (Form 760, Line 4). 15. Social Security Act and Tier 1 Railroad Retirement Act Benefits (Form 760, Line 5). 16. State income tax refund or overpayment credit reported as income on your federal return (Form 760, Line 6). 17. Other Subtractions (Form 760, Line 7). 18. Total Subtractions from FAGI - add Lines 14, 15, 16, and 17. 19. Subtract Line 18 from Line 13. These are your separate VAGI amounts to be used on Line 1 of the Spouse Tax Adjustment Worksheet. (The total of both columns should equal your combined VAGI reported on Line 9 of your 760)'\n\nAGE DEDUCTION 50/50 SPLIT RULE (feeds Step 2 Line 14 above \u2014 the ONE genuinely joint item on this worksheet, verbatim): 'If both spouses are claiming an income-based age deduction, regardless of whether filing jointly or separately, the married taxpayers must compute a joint age deduction first, then allocate half of the joint deduction to each spouse.' (Compute the joint age deduction once via us.va.income_tax's ageDeductionBase/threshold parameters on COMBINED AFAGI, then split it exactly 50/50 \u2014 not proportional to either spouse's income or age-deduction eligibility share.)\n\nOWNERSHIP ATTRIBUTION FOR STEP 1
|
|
33393
|
+
excerpt: "2025 Spouse Tax Adjustment (STA): available ONLY on a joint (MFJ) return where BOTH spouses have income and their COMBINED Virginia taxable income exceeds $3,000; maximum credit $259 (footer statement + Line 5 shortcut, both confirmed in the TY2025 booklet). NARRATIVE (p.13, verbatim): 'Couples filing jointly under Filing Status 2 may reduce their tax by up to $259 with the STA if both have taxable income to report and their combined taxable income on Line 15 is more than $3,000 ... Using the STA, couples filing joint returns will not pay higher taxes than if they had filed separate returns.' HOW IT WORKS (verbatim): 'Virginia tax rates increase with income: 2% up to $3,000; 3% from $3,001 to $5,000; 5% from $5,001 to $17,000; and 5.75% for income over $17,000. The STA lets both incomes reported on jointly filed returns benefit from the lower tax rates.'\n\nTHE ALLOCATION IS PRESCRIBED, NOT DISCRETIONARY \u2014 built on income-SOURCE attribution, never a proportional wage-ratio or a benefit-maximizing choice. VERBATIM WORKSHEET, PART 1: SEPARATE YOUR INCOME AND EXEMPTIONS (per spouse): '1. Enter the portion of the Virginia Adjusted Gross Income (VAGI) on Line 9 of Form 760 that is related to each spouse. Use the worksheet at the bottom of the page to compute the separate VAGI for each spouse.' '2. Enter separate personal exemption amounts. Enter a 1 in the boxes that apply and multiply the total by $800. Add $930 to the total to compute the personal exemptions for you and spouse. You: 65 or over + Blind = Total x $800 + $930 = ___. Spouse: 65 or over + Blind = Total x $800 + $930 = ___.' (i.e., PER SPOUSE: $930 + $800 x that spouse's own 65-or-over/blind box count \u2014 a fixed formula, never proportional to income.) '3. Subtract Line 2 from Line 1. If either amount is 0 or less, stop here; you do not qualify for this credit.'\n\nVERBATIM WORKSHEET, PART 2: CALCULATE YOUR TAX ADJUSTMENT: '4. Enter the taxable income from Line 15 on Form 760.' '5. Enter the smaller amount from Line 3 above. If this amount is larger than $17,000 and Line 4 is larger than $34,000, skip to Line 12 and enter $259 as the credit.' '6. Subtract Line 5 from Line 4 (if $0 or less, enter $0).' '7. Divide the amount on Line 4 by 2.' '8. Enter the tax on the smaller amount from Line 5 or Line 7. Refer to the tax table or rate schedule.' '9. Enter the tax on the larger amount from Line 6 or Line 7. Refer to the tax table or rate schedule.' (Lines 8-9 use us.va.income_tax's own rate schedule/table on the stated amount \u2014 NOT a fresh computation.) '10. Add Lines 8 and 9.' '11. Enter the tax from Line 16 on Form 760.' '12. TAX ADJUSTMENT: Subtract Line 10 from Line 11. Enter this amount on Line 17 of Form 760.' 'The Spouse Tax Adjustment cannot exceed $259.'\n\nVERBATIM 'WORKSHEET FOR DETERMINING SEPARATE VIRGINIA ADJUSTED GROSS INCOME' (bottom of p.12) \u2014 feeds Part 1 Line 1 above: STEP 1 - Determine Separate Federal Adjusted Gross Income (You / Spouse columns, LITERAL income-source attribution \u2014 each spouse's OWN items, never a ratio): '1. Wages, salaries, etc. 2. Taxable interest and dividend income. 3. Taxable refunds, adjustments, or offsets of state and local income tax. 4. Business income. 5. Capital gains/losses and other gains/losses. 6. Taxable pensions, annuities, and IRA distributions. 7. Rents, royalties, partnerships, estates, trusts, etc. 8. Other income (farm income, taxable social security, etc.). 9. Gross income - add Lines 1 through 8. 10. Adjustments to gross income. 11. FAGI - subtract Line 10 from Line 9. (The total of both columns should equal your joint FAGI reported on your 1040)' STEP 2 - Determine Separate Virginia Adjusted Gross Income: '12. Total additions to FAGI (Form 760, Line 2). 13. Subtotal - add Lines 11 and 12. 14. Age Deduction (Form 760, Line 4). 15. Social Security Act and Tier 1 Railroad Retirement Act Benefits (Form 760, Line 5). 16. State income tax refund or overpayment credit reported as income on your federal return (Form 760, Line 6). 17. Other Subtractions (Form 760, Line 7). 18. Total Subtractions from FAGI - add Lines 14, 15, 16, and 17. 19. Subtract Line 18 from Line 13. These are your separate VAGI amounts to be used on Line 1 of the Spouse Tax Adjustment Worksheet. (The total of both columns should equal your combined VAGI reported on Line 9 of your 760)'\n\nAGE DEDUCTION 50/50 SPLIT RULE (feeds Step 2 Line 14 above \u2014 the ONE genuinely joint item on this worksheet, verbatim): 'If both spouses are claiming an income-based age deduction, regardless of whether filing jointly or separately, the married taxpayers must compute a joint age deduction first, then allocate half of the joint deduction to each spouse.' (Compute the joint age deduction once via us.va.income_tax's ageDeductionBase/threshold parameters on COMBINED AFAGI, then split it exactly 50/50 \u2014 not proportional to either spouse's income or age-deduction eligibility share.)\n\nOWNERSHIP ATTRIBUTION FOR STEP 1: a document belongs to the spouse the STRUCTURED INTAKE's ownership tag names (who_applies_to / whose-account fields: taxpayer | spouse | joint) \u2014 the intake tag CONTROLS over the name printed on the PDF (a 1099-B may print only the primary's name yet be intake-tagged joint). taxpayer/spouse-tagged items go WHOLE to that spouse; joint-tagged items (interest, dividends INCLUDING their capital-gain distributions, jointly-tagged 1099-Bs) split 50/50 with the ODD DOLLAR TO THE PRIMARY. Adjustments follow their owner: each spouse's own \xBD-SE-tax (from their own Schedule SE), own educator cap, and the student-loan interest to the named 1098-E borrower. (Worked example: primary = SchC 16,391 + own crypto LT 999 + joint splits 2,778 + 426 + 44 + 425 \u2212 \xBDSE 1,158 \u2212 SL 2,500 \u2212 educator 300 = 17,105; spouse = 55,123; STA = 245.)\n\nSUMMARY OF WHAT IS AND ISN'T DISCRETIONARY: (1) Separate-VAGI Step 1 items are attributed by literal ownership (each spouse's own wages/interest/business/capital-gain/pension/rent/other-income line, cross-checked to equal the joint FAGI). (2) Personal exemptions use the fixed $930+$800-per-box formula, per spouse \u2014 NOT proportional to income. (3) The age deduction (a genuinely joint item) is computed once, jointly, then split EXACTLY 50/50 \u2014 not proportional to either spouse's income or share of the joint deduction. (4) The standard/itemized deduction is NEVER split on this worksheet \u2014 VAGI is computed pre-deduction, and Part 2's Lines 4-12 arithmetic (min/max sequence below) handles the deduction's effect implicitly through the FIXED sequence: Line 5 = smaller of the two Line-3 (exemption-adjusted VAGI) amounts; Line 6 = Line 4 minus Line 5 (floored at 0); Line 7 = Line 4 divided by 2; tax is computed on min(Line 5, Line 7) and max(Line 6, Line 7) via us.va.income_tax and summed (Line 10), then subtracted from the actual joint tax (Line 11 = Form 760 Line 16) for the STA (Line 12), capped at $259. There is NO discretionary maximization step anywhere in this sequence \u2014 given the source-attributed Step-1/Step-2 inputs, the worksheet produces exactly one number. DISCLOSED SCOPE: this rule still does not COMPUTE the worksheet end-to-end (the per-spouse Separate-VAGI Step-1 income-source figures are themselves agent-transcribed facts, not derived by this corpus) \u2014 it now states the COMPLETE prescription (every line, every allocation rule) rather than only the gating conditions and cap, so an agent following it mechanically reaches the one correct number instead of inventing a wage-ratio or benefit-maximizing allocation."
|
|
32497
33394
|
},
|
|
32498
33395
|
effectiveFrom: "2025-01-01",
|
|
32499
33396
|
effectiveTo: "2026-01-01",
|
|
@@ -32537,8 +33434,8 @@ var vaRules = [
|
|
|
32537
33434
|
cond: {
|
|
32538
33435
|
kind: "or",
|
|
32539
33436
|
args: [
|
|
32540
|
-
{ kind: "cmp", op: "eq", left:
|
|
32541
|
-
{ kind: "cmp", op: "eq", left:
|
|
33437
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "mfj" } },
|
|
33438
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "qss" } }
|
|
32542
33439
|
]
|
|
32543
33440
|
},
|
|
32544
33441
|
then: { kind: "money", cents: "1750000" },
|
|
@@ -32566,8 +33463,8 @@ var vaRules = [
|
|
|
32566
33463
|
cond: {
|
|
32567
33464
|
kind: "or",
|
|
32568
33465
|
args: [
|
|
32569
|
-
{ kind: "cmp", op: "eq", left:
|
|
32570
|
-
{ kind: "cmp", op: "eq", left:
|
|
33466
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "mfj" } },
|
|
33467
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "qss" } }
|
|
32571
33468
|
]
|
|
32572
33469
|
},
|
|
32573
33470
|
then: { kind: "money", cents: "1840000" },
|
|
@@ -32595,8 +33492,8 @@ var vaRules = [
|
|
|
32595
33492
|
cond: {
|
|
32596
33493
|
kind: "or",
|
|
32597
33494
|
args: [
|
|
32598
|
-
{ kind: "cmp", op: "eq", left:
|
|
32599
|
-
{ kind: "cmp", op: "eq", left:
|
|
33495
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "mfj" } },
|
|
33496
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "qss" } }
|
|
32600
33497
|
]
|
|
32601
33498
|
},
|
|
32602
33499
|
then: { kind: "money", cents: "1860000" },
|
|
@@ -32619,7 +33516,7 @@ var caRules = [
|
|
|
32619
33516
|
source: "2025 California Tax Rate Schedules (FTB); 2025 California Tax Table (FTB 540 booklet); Rev. & Tax. Code \xA7 17041",
|
|
32620
33517
|
section: "FTB 2025 Schedule X / Schedule Y / Schedule Z; 2025 California Tax Table",
|
|
32621
33518
|
url: "https://www.ftb.ca.gov/forms/2025/2025-540-tax-rate-schedules.pdf",
|
|
32622
|
-
excerpt: `2025 Schedule Z (head of household), FTB: 1% to $22,173; 2% to $52,530; 4% to $67,716; 6% to $83,805; 8% to $98,990; 9.3% to $505,208 \u2014 income above $505,208 REFUSES here (higher 10.3/11.3/12.3% tiers and the 1% MHSA surcharge not yet encoded from source). Schedule X (single/MFS), 2025 FTB, web-verified July 2026: 1% to $11,079; 2% to $26,264; 4% to $41,452; 6% to $57,542; 8% to $72,724; 9.3% above (same $505,208 shared refusal boundary as this rule's HOH branch, applied before differentiating single from HOH) \u2014 corrected in v5 from stale/mistyped thresholds that broke the Schedule-Y-is-2x-Schedule-X identity below. SCHEDULE Y (married filing jointly / qualifying surviving spouse, 2025, FTB): thresholds are EXACTLY 2\xD7 Schedule X's \u2014 $0/$22,158/$52,528/$82,904/$115,084/$145,448/$742,958 at 1/2/4/6/8/9.3/10.3/11.3/12.3% \u2014 encoded PER-BRACKET (marginal accumulation over the doubled thresholds) rather than by doubling Schedule X's own printed (already-rounded) fixed-dollar constants, because independently rounding at each schedule drifts by pennies in the upper brackets; income above $742,958 REFUSES here on the same basis as X/Z (10.3%+ tiers not yet encoded). CALIFORNIA TAX TABLE CONVENTION (2025 FTB 540 booklet, 'California Tax Table', pp.68-75; DEFAULT method for CA taxable income under $100,000, per Form 540/540NR line 19 instructions: 'Read down the column labeled "If Your Taxable Income Is..." to find the range that includes your taxable income... Read across the columns labeled "The Tax For Filing Status"...'): rows are $1-$50 as a SPECIAL FIRST ROW (tax $0 for every filing status), then $100-WIDE brackets thereafter (51-150, 151-250, ..., 99,951-100,000 \u2014 the last row is only $50 wide because the table truncates exactly at $100,000). Table tax = apply the Schedule X/Y/Z marginal formula above at the bracket's MIDPOINT, rounded to the nearest dollar (the SAME midpoint-of-bracket convention already used for the VA and NY tax tables \u2014 decisively confirmed for CA too, not merely assumed). Verified 9-for-9 exact against three incomes \xD7 three filing-status columns (FTB 2025 tax table + rate schedules, web-verified July 2026): row 40,051-40,150 (mid $40,100.50) \u2192 Single $968 / MFJ $580 / HOH $580; row 70,051-70,150 (mid $70,100.50) \u2192 Single $2,992 / MFJ $1,532 / HOH $1,579; row 95,051-95,150 (mid $95,100.50) \u2192 Single $5,283 / MFJ $2,776 / HOH $3,305. The truncated last row (99,951-100,000) uses its OWN true midpoint $99,975.50 (not the phantom $100,000.50 the general $100-bracket formula would otherwise produce) \u2014 verified: Single at that row = $5,736. CA taxable income of $100,000 OR ABOVE uses the RATE SCHEDULE directly (unrounded to the cent, per the schedule's own marginal math) \u2014 the table stops at exactly $100,000. useFormulaMethod=true bypasses the table entirely and uses the raw rate schedule at every income level (matches the pre-v5 default behavior). Related 2025 parameters (FTB, web-verified July 2026): standard deduction $5,706 single/MFS, $11,412 MFJ/HOH/QSS; personal exemption CREDIT $153; dependent exemption credit $475
|
|
33519
|
+
excerpt: `2025 Schedule Z (head of household), FTB: 1% to $22,173; 2% to $52,530; 4% to $67,716; 6% to $83,805; 8% to $98,990; 9.3% to $505,208 \u2014 income above $505,208 REFUSES here (higher 10.3/11.3/12.3% tiers and the 1% MHSA surcharge not yet encoded from source). Schedule X (single/MFS), 2025 FTB, web-verified July 2026: 1% to $11,079; 2% to $26,264; 4% to $41,452; 6% to $57,542; 8% to $72,724; 9.3% above (same $505,208 shared refusal boundary as this rule's HOH branch, applied before differentiating single from HOH) \u2014 corrected in v5 from stale/mistyped thresholds that broke the Schedule-Y-is-2x-Schedule-X identity below. SCHEDULE Y (married filing jointly / qualifying surviving spouse, 2025, FTB): thresholds are EXACTLY 2\xD7 Schedule X's \u2014 $0/$22,158/$52,528/$82,904/$115,084/$145,448/$742,958 at 1/2/4/6/8/9.3/10.3/11.3/12.3% \u2014 encoded PER-BRACKET (marginal accumulation over the doubled thresholds) rather than by doubling Schedule X's own printed (already-rounded) fixed-dollar constants, because independently rounding at each schedule drifts by pennies in the upper brackets; income above $742,958 REFUSES here on the same basis as X/Z (10.3%+ tiers not yet encoded). CALIFORNIA TAX TABLE CONVENTION (2025 FTB 540 booklet, 'California Tax Table', pp.68-75; DEFAULT method for CA taxable income under $100,000, per Form 540/540NR line 19 instructions: 'Read down the column labeled "If Your Taxable Income Is..." to find the range that includes your taxable income... Read across the columns labeled "The Tax For Filing Status"...'): rows are $1-$50 as a SPECIAL FIRST ROW (tax $0 for every filing status), then $100-WIDE brackets thereafter (51-150, 151-250, ..., 99,951-100,000 \u2014 the last row is only $50 wide because the table truncates exactly at $100,000). Table tax = apply the Schedule X/Y/Z marginal formula above at the bracket's MIDPOINT, rounded to the nearest dollar (the SAME midpoint-of-bracket convention already used for the VA and NY tax tables \u2014 decisively confirmed for CA too, not merely assumed). Verified 9-for-9 exact against three incomes \xD7 three filing-status columns (FTB 2025 tax table + rate schedules, web-verified July 2026): row 40,051-40,150 (mid $40,100.50) \u2192 Single $968 / MFJ $580 / HOH $580; row 70,051-70,150 (mid $70,100.50) \u2192 Single $2,992 / MFJ $1,532 / HOH $1,579; row 95,051-95,150 (mid $95,100.50) \u2192 Single $5,283 / MFJ $2,776 / HOH $3,305. The truncated last row (99,951-100,000) uses its OWN true midpoint $99,975.50 (not the phantom $100,000.50 the general $100-bracket formula would otherwise produce) \u2014 verified: Single at that row = $5,736. CA taxable income of $100,000 OR ABOVE uses the RATE SCHEDULE directly (unrounded to the cent, per the schedule's own marginal math) \u2014 the table stops at exactly $100,000. useFormulaMethod=true bypasses the table entirely and uses the raw rate schedule at every income level (matches the pre-v5 default behavior). Related 2025 parameters (FTB, web-verified July 2026): standard deduction $5,706 single/MFS, $11,412 MFJ/HOH/QSS; personal exemption CREDIT $153; dependent exemption credit $475. EXEMPTION CREDIT PHASEOUT (R&TC \xA7 17054(a), (b); 2025 FTB Schedule CA/540 instructions): credits (personal + dependent, but NOT senior) phase out $6 for each $2,500 (or fraction thereof \u2014 the quotient is ROUNDED UP) of federal AGI over $252,203 single/MFS ($1,250 divisor increments instead of $2,500 for MFS \u2014 i.e. the SAME $6 reduction per HALF the increment), $504,411 MFJ/QSS, $378,310 HOH; fully phased out credits are not reduced below $0. [Input: California taxable income (Form 540 line 19). SCHEDULE CA (2025 instructions, line 7): California EXCLUDES unemployment compensation (and CA PFL benefits) from taxable income \u2014 subtract it from federal AGI when composing CA AGI. SCHEDULE CA (2025 instructions, line 6, column B): California ALSO EXCLUDES social security benefits \u2014 the federally-taxable amount (the \xA7 86 includible amount, us.federal.taxable_social_security) is likewise subtracted from federal AGI when composing CA AGI. CalEITC \u2192 us.ca.caleitc; Young Child Tax Credit \u2192 us.ca.yctc; renter's credit \u2192 us.ca.renters_credit; CDCC \u2192 us.ca.cdcc; misc. parameters (use tax, SDI) \u2192 us.ca.parameters.]`
|
|
32623
33520
|
},
|
|
32624
33521
|
effectiveFrom: "2025-01-01",
|
|
32625
33522
|
effectiveTo: "2026-01-01",
|
|
@@ -32664,7 +33561,7 @@ var caRules = [
|
|
|
32664
33561
|
kind: "cmp",
|
|
32665
33562
|
op: "gt",
|
|
32666
33563
|
left: base2,
|
|
32667
|
-
right:
|
|
33564
|
+
right: money33("74295800")
|
|
32668
33565
|
// $742,958 — Schedule Y's 9.3% band ends here
|
|
32669
33566
|
},
|
|
32670
33567
|
then: {
|
|
@@ -32691,7 +33588,7 @@ var caRules = [
|
|
|
32691
33588
|
kind: "cmp",
|
|
32692
33589
|
op: "gt",
|
|
32693
33590
|
left: base2,
|
|
32694
|
-
right:
|
|
33591
|
+
right: money33("50520800")
|
|
32695
33592
|
// $505,208 — encoded bands end here
|
|
32696
33593
|
},
|
|
32697
33594
|
then: {
|
|
@@ -32735,26 +33632,26 @@ var caRules = [
|
|
|
32735
33632
|
}
|
|
32736
33633
|
}
|
|
32737
33634
|
});
|
|
32738
|
-
const base = { kind: "max0", arg:
|
|
33635
|
+
const base = { kind: "max0", arg: fact36("stateTaxableIncome") };
|
|
32739
33636
|
const lower = {
|
|
32740
33637
|
kind: "add",
|
|
32741
33638
|
args: [
|
|
32742
|
-
|
|
33639
|
+
money33("5100"),
|
|
32743
33640
|
// $51
|
|
32744
33641
|
{
|
|
32745
33642
|
kind: "mulInt",
|
|
32746
|
-
base:
|
|
33643
|
+
base: money33("10000"),
|
|
32747
33644
|
// $100
|
|
32748
33645
|
count: {
|
|
32749
33646
|
kind: "stepUnits",
|
|
32750
|
-
value: { kind: "sub", left: base, right:
|
|
33647
|
+
value: { kind: "sub", left: base, right: money33("5100") },
|
|
32751
33648
|
unitCents: "10000",
|
|
32752
33649
|
mode: "floor"
|
|
32753
33650
|
}
|
|
32754
33651
|
}
|
|
32755
33652
|
]
|
|
32756
33653
|
};
|
|
32757
|
-
const generalMidpoint = { kind: "add", args: [lower,
|
|
33654
|
+
const generalMidpoint = { kind: "add", args: [lower, money33("4950")] };
|
|
32758
33655
|
const midpoint = {
|
|
32759
33656
|
kind: "if",
|
|
32760
33657
|
cond: {
|
|
@@ -32773,7 +33670,7 @@ var caRules = [
|
|
|
32773
33670
|
cond: {
|
|
32774
33671
|
kind: "or",
|
|
32775
33672
|
args: [
|
|
32776
|
-
|
|
33673
|
+
fact36("useFormulaMethod"),
|
|
32777
33674
|
{
|
|
32778
33675
|
kind: "cmp",
|
|
32779
33676
|
op: "ge",
|
|
@@ -32793,7 +33690,7 @@ var caRules = [
|
|
|
32793
33690
|
right: { kind: "param", name: "tableFirstRowCeiling" }
|
|
32794
33691
|
// <= $50
|
|
32795
33692
|
},
|
|
32796
|
-
then:
|
|
33693
|
+
then: money33("0"),
|
|
32797
33694
|
else: { kind: "roundToDollar", value: scheduleFor(midpoint), mode: "half-up" }
|
|
32798
33695
|
}
|
|
32799
33696
|
};
|
|
@@ -32809,7 +33706,7 @@ var caRules = [
|
|
|
32809
33706
|
source: "R&TC \xA7 17052; 2025 FTB 3514 booklet (EITC table + General Information)",
|
|
32810
33707
|
section: "R&TC \xA7 17052",
|
|
32811
33708
|
url: "https://www.ftb.ca.gov/forms/2025/2025-3514-booklet.pdf",
|
|
32812
|
-
excerpt: "2025 CalEITC: income ceiling $32,900 (all family sizes, both earned income AND federal AGI must be under $32,901 or the credit is $0); maximum credit $302 (0 kids, peak bracket $4,651-$4,700) / $2,016 (1 kid, peak $6,951-$7,000) / $3,339 (2 kids, peak $9,801-$9,850) / $3,756 (3+ kids, same $9,801-$9,850 peak). DECODED STRUCTURE (July 2026 web-verification: the printed table was parsed in full, all 658 $50-wide brackets from $1-$32,900, `pdftotext -layout`, and formula-fit): no plateau \u2014 every column peaks at a single $50 bracket and immediately begins declining. The decline is NOT one straight line: it has a KINK where the phase-out slope changes from steep to gentle, at 0 kids $5,450/$5,451, 1 kid $11,800/$11,801, 2 kids $17,800/$17,801, 3+ kids $18,000/$18,001 \u2014 confirmed by row-to-row deltas dropping from roughly $17-19 per $50 bracket (steep) to roughly $2-3 per bracket (gentle) exactly at each boundary. THIS RULE NOW COMPUTES ALL THREE SEGMENTS: (1) PHASE-IN (unchanged from v1): credit = round(bracket-midpoint \xD7 combined rate), combined rate = federal \xA7 32(b) phase-in rate \xD7 the 85% \xA7 17052(c) adjustment factor (7.65/34/40/45% \xD7 85% = 6.5025/28.90/34.00/38.25%), using the floor-aligned $50-bracket midpoint \u2014 verified against the printed row 2,201-2,250 \u2192 851 for 3+ children (2,225.50 \xD7 45% \xD7 85% = 851.25 \u2192 851). (2) STEEP PHASE-OUT (peak to kink): the printed table declines at (within rounding) the SAME rate magnitude as the phase-in line (a symmetric V through the peak) \u2014 credit = round(anchor value \u2212 combined phase-in rate \xD7 (bracket-midpoint \u2212 anchor-bracket-midpoint)), using the TRUE (upper-bound-aligned) $50-bracket midpoint. For 1/2/3+ kids the line is anchored to an actual printed table row (1 kid: bracket 9,451-9,500, $1,306; 2 kids and 3+ kids: bracket 9,951-10,000, $3,288/$3,699) rather than the theoretical peak itself \u2014 the peak POINT sits fractionally off this line for 1 kid specifically (a peak-anchored construction understates by roughly $13 there), so a real table row is used everywhere data exists. Verified EXACT against every printed row in this zone for 1/2/3+ kids (e.g. 2 kids: $10,000 \u2192 3,288 exact, $16,500 \u2192 1,078 exact, $17,500 \u2192 738 exact; 3+ kids: $16,500 \u2192 1,213 exact; 1 kid: $9,500 \u2192 1,306 exact, $11,000 \u2192 873 exact). (3) GENTLE PHASE-OUT (kink to ceiling): a SEPARATE, shallower line anchored to a printed table row just past the kink, at the independently-fit rate (0 kids = \u22120.9%/$1 i.e. \u22129/1000 per $1 of midpoint, CONFIRMED against the printed 2025 FTB 3514 booklet row $12,951-$13,000 \u2192 $183 exact, web-verified July 2026 \u2014 corrects a prior unverified anchor that produced $184 for that row; 1 kid \u2248 \u22123.01%/$1 i.e. \u2212541/18000, 2 kids \u2248 \u22124.19%/$1 i.e. \u221225/596, 3+ kids \u2248 \u22124.26%/$1 i.e. \u2212613/14400) \u2014 verified within \u2264$1 across every printed row from the kink to $32,900 (e.g. 0 kids: $17,000 \u2192 147 exact; 2 kids: $18,000 \u2192 626 exact, $25,000 \u2192 333 vs. computed 332, $30,000 \u2192 123 vs. computed 123). Max residual across all 658 rows, all four columns: \u2264 $2 pre-rounding (a single pre-existing phase-in-region anomaly just below the 2/3+-kid peak, at $9,500, runs to $17-19 \u2014 inherited from the v1 floor-aligned phase-in formula, unchanged here per the phase-in region's own design, and confined to that one boundary row). The 0-kid STEEP segment (peak to $5,450) has no printed table data in the range this session could source ($9,500+) to verify against; it uses the peak-anchor construction (disclosed as unverified-but-consistent with the confirmed symmetric-V structure elsewhere) \u2014 only the GENTLE segment's 0-kid anchor is table-confirmed as of v5. Earned income here = wages (line 1z, INCLUDING any taxable dependent-care benefits on line 1e) + max(0, SE profit \u2212 \xBDSE tax) \u2212 Schedule C loss, same composition as the federal \xA7 32(c)(2) input. CAUTION
|
|
33709
|
+
excerpt: "2025 CalEITC: income ceiling $32,900 (all family sizes, both earned income AND federal AGI must be under $32,901 or the credit is $0); maximum credit $302 (0 kids, peak bracket $4,651-$4,700) / $2,016 (1 kid, peak $6,951-$7,000) / $3,339 (2 kids, peak $9,801-$9,850) / $3,756 (3+ kids, same $9,801-$9,850 peak). DECODED STRUCTURE (July 2026 web-verification: the printed table was parsed in full, all 658 $50-wide brackets from $1-$32,900, `pdftotext -layout`, and formula-fit): no plateau \u2014 every column peaks at a single $50 bracket and immediately begins declining. The decline is NOT one straight line: it has a KINK where the phase-out slope changes from steep to gentle, at 0 kids $5,450/$5,451, 1 kid $11,800/$11,801, 2 kids $17,800/$17,801, 3+ kids $18,000/$18,001 \u2014 confirmed by row-to-row deltas dropping from roughly $17-19 per $50 bracket (steep) to roughly $2-3 per bracket (gentle) exactly at each boundary. THIS RULE NOW COMPUTES ALL THREE SEGMENTS: (1) PHASE-IN (unchanged from v1): credit = round(bracket-midpoint \xD7 combined rate), combined rate = federal \xA7 32(b) phase-in rate \xD7 the 85% \xA7 17052(c) adjustment factor (7.65/34/40/45% \xD7 85% = 6.5025/28.90/34.00/38.25%), using the floor-aligned $50-bracket midpoint \u2014 verified against the printed row 2,201-2,250 \u2192 851 for 3+ children (2,225.50 \xD7 45% \xD7 85% = 851.25 \u2192 851). (2) STEEP PHASE-OUT (peak to kink): the printed table declines at (within rounding) the SAME rate magnitude as the phase-in line (a symmetric V through the peak) \u2014 credit = round(anchor value \u2212 combined phase-in rate \xD7 (bracket-midpoint \u2212 anchor-bracket-midpoint)), using the TRUE (upper-bound-aligned) $50-bracket midpoint. For 1/2/3+ kids the line is anchored to an actual printed table row (1 kid: bracket 9,451-9,500, $1,306; 2 kids and 3+ kids: bracket 9,951-10,000, $3,288/$3,699) rather than the theoretical peak itself \u2014 the peak POINT sits fractionally off this line for 1 kid specifically (a peak-anchored construction understates by roughly $13 there), so a real table row is used everywhere data exists. Verified EXACT against every printed row in this zone for 1/2/3+ kids (e.g. 2 kids: $10,000 \u2192 3,288 exact, $16,500 \u2192 1,078 exact, $17,500 \u2192 738 exact; 3+ kids: $16,500 \u2192 1,213 exact; 1 kid: $9,500 \u2192 1,306 exact, $11,000 \u2192 873 exact). (3) GENTLE PHASE-OUT (kink to ceiling): a SEPARATE, shallower line anchored to a printed table row just past the kink, at the independently-fit rate (0 kids = \u22120.9%/$1 i.e. \u22129/1000 per $1 of midpoint, CONFIRMED against the printed 2025 FTB 3514 booklet row $12,951-$13,000 \u2192 $183 exact, web-verified July 2026 \u2014 corrects a prior unverified anchor that produced $184 for that row; 1 kid \u2248 \u22123.01%/$1 i.e. \u2212541/18000, 2 kids \u2248 \u22124.19%/$1 i.e. \u221225/596, 3+ kids \u2248 \u22124.26%/$1 i.e. \u2212613/14400) \u2014 verified within \u2264$1 across every printed row from the kink to $32,900 (e.g. 0 kids: $17,000 \u2192 147 exact; 2 kids: $18,000 \u2192 626 exact, $25,000 \u2192 333 vs. computed 332, $30,000 \u2192 123 vs. computed 123). Max residual across all 658 rows, all four columns: \u2264 $2 pre-rounding (a single pre-existing phase-in-region anomaly just below the 2/3+-kid peak, at $9,500, runs to $17-19 \u2014 inherited from the v1 floor-aligned phase-in formula, unchanged here per the phase-in region's own design, and confined to that one boundary row). The 0-kid STEEP segment (peak to $5,450) has no printed table data in the range this session could source ($9,500+) to verify against; it uses the peak-anchor construction (disclosed as unverified-but-consistent with the confirmed symmetric-V structure elsewhere) \u2014 only the GENTLE segment's 0-kid anchor is table-confirmed as of v5. Earned income here = wages (line 1z, INCLUDING any taxable dependent-care benefits on line 1e) + max(0, SE profit \u2212 \xBDSE tax) \u2212 Schedule C loss, same composition as the federal \xA7 32(c)(2) input. CAUTION: when a spouse's own Schedule C loss makes that spouse's Form 2441 earned income zero or negative, the \xA7 129 exclusion for W-2 box 10 dependent care benefits is $0 \u2014 the FULL box-10 amount becomes taxable wages (line 1e), raising both federal AGI and this credit's earned income (federal earned 10,500 + 6,000 fully-taxable DCB \u2192 CA earned 16,500 \u2192 credit 1,078 from the printed row). Kids = qualifyingChildren + eitcAdditionalQualifyingChildren."
|
|
32813
33710
|
},
|
|
32814
33711
|
effectiveFrom: "2025-01-01",
|
|
32815
33712
|
effectiveTo: "2026-01-01",
|
|
@@ -32894,33 +33791,33 @@ var caRules = [
|
|
|
32894
33791
|
left: {
|
|
32895
33792
|
kind: "add",
|
|
32896
33793
|
args: [
|
|
32897
|
-
|
|
33794
|
+
fact36("wages"),
|
|
32898
33795
|
{
|
|
32899
33796
|
kind: "max0",
|
|
32900
33797
|
arg: {
|
|
32901
33798
|
kind: "sub",
|
|
32902
|
-
left:
|
|
33799
|
+
left: fact36("selfEmploymentNetProfit"),
|
|
32903
33800
|
right: { kind: "rule", ruleId: "us.federal.se_tax_half_deduction" }
|
|
32904
33801
|
}
|
|
32905
33802
|
}
|
|
32906
33803
|
]
|
|
32907
33804
|
},
|
|
32908
|
-
right:
|
|
33805
|
+
right: fact36("scheduleCNetLoss")
|
|
32909
33806
|
}
|
|
32910
33807
|
};
|
|
32911
33808
|
const midCeil = {
|
|
32912
33809
|
kind: "sub",
|
|
32913
33810
|
left: {
|
|
32914
33811
|
kind: "mulInt",
|
|
32915
|
-
base:
|
|
33812
|
+
base: money33("5000"),
|
|
32916
33813
|
count: { kind: "stepUnits", value: earned2, unitCents: "5000", mode: "ceil" }
|
|
32917
33814
|
},
|
|
32918
|
-
right:
|
|
33815
|
+
right: money33("2450")
|
|
32919
33816
|
// $24.50
|
|
32920
33817
|
};
|
|
32921
33818
|
const kids = {
|
|
32922
33819
|
kind: "add",
|
|
32923
|
-
args: [
|
|
33820
|
+
args: [fact36("qualifyingChildren"), fact36("eitcAdditionalQualifyingChildren")]
|
|
32924
33821
|
};
|
|
32925
33822
|
const kidsEq = (n) => ({
|
|
32926
33823
|
kind: "cmp",
|
|
@@ -33022,12 +33919,12 @@ var caRules = [
|
|
|
33022
33919
|
{
|
|
33023
33920
|
kind: "cmp",
|
|
33024
33921
|
op: "ge",
|
|
33025
|
-
left:
|
|
33922
|
+
left: ruleRef30("us.federal.agi"),
|
|
33026
33923
|
right: { kind: "param", name: "ceiling" }
|
|
33027
33924
|
}
|
|
33028
33925
|
]
|
|
33029
33926
|
},
|
|
33030
|
-
then:
|
|
33927
|
+
then: money33("0"),
|
|
33031
33928
|
else: {
|
|
33032
33929
|
kind: "if",
|
|
33033
33930
|
cond: kidsEq("0"),
|
|
@@ -33077,28 +33974,28 @@ var caRules = [
|
|
|
33077
33974
|
left: {
|
|
33078
33975
|
kind: "add",
|
|
33079
33976
|
args: [
|
|
33080
|
-
|
|
33977
|
+
fact36("wages"),
|
|
33081
33978
|
{
|
|
33082
33979
|
kind: "max0",
|
|
33083
33980
|
arg: {
|
|
33084
33981
|
kind: "sub",
|
|
33085
|
-
left:
|
|
33982
|
+
left: fact36("selfEmploymentNetProfit"),
|
|
33086
33983
|
right: { kind: "rule", ruleId: "us.federal.se_tax_half_deduction" }
|
|
33087
33984
|
}
|
|
33088
33985
|
}
|
|
33089
33986
|
]
|
|
33090
33987
|
},
|
|
33091
|
-
right:
|
|
33988
|
+
right: fact36("scheduleCNetLoss")
|
|
33092
33989
|
}
|
|
33093
33990
|
};
|
|
33094
33991
|
return {
|
|
33095
33992
|
kind: "if",
|
|
33096
|
-
cond: { kind: "not", arg:
|
|
33097
|
-
then:
|
|
33993
|
+
cond: { kind: "not", arg: fact36("hasChildUnderSix") },
|
|
33994
|
+
then: money33("0"),
|
|
33098
33995
|
else: {
|
|
33099
33996
|
kind: "if",
|
|
33100
33997
|
cond: { kind: "cmp", op: "ge", left: earned2, right: { kind: "param", name: "phaseOutComplete" } },
|
|
33101
|
-
then:
|
|
33998
|
+
then: money33("0"),
|
|
33102
33999
|
else: {
|
|
33103
34000
|
kind: "if",
|
|
33104
34001
|
cond: { kind: "cmp", op: "le", left: earned2, right: { kind: "param", name: "phaseOutStart" } },
|
|
@@ -33138,8 +34035,8 @@ var caRules = [
|
|
|
33138
34035
|
},
|
|
33139
34036
|
formula: {
|
|
33140
34037
|
kind: "if",
|
|
33141
|
-
cond: { kind: "not", arg:
|
|
33142
|
-
then:
|
|
34038
|
+
cond: { kind: "not", arg: fact36("caRentedPrincipalResidence") },
|
|
34039
|
+
then: money33("0"),
|
|
33143
34040
|
else: {
|
|
33144
34041
|
kind: "if",
|
|
33145
34042
|
cond: {
|
|
@@ -33151,22 +34048,22 @@ var caRules = [
|
|
|
33151
34048
|
cond: {
|
|
33152
34049
|
kind: "cmp",
|
|
33153
34050
|
op: "le",
|
|
33154
|
-
left:
|
|
34051
|
+
left: ruleRef30("us.federal.agi"),
|
|
33155
34052
|
right: { kind: "param", name: "agiCeilingJointHohQss" }
|
|
33156
34053
|
},
|
|
33157
34054
|
then: { kind: "param", name: "creditJointHohQss" },
|
|
33158
|
-
else:
|
|
34055
|
+
else: money33("0")
|
|
33159
34056
|
},
|
|
33160
34057
|
else: {
|
|
33161
34058
|
kind: "if",
|
|
33162
34059
|
cond: {
|
|
33163
34060
|
kind: "cmp",
|
|
33164
34061
|
op: "le",
|
|
33165
|
-
left:
|
|
34062
|
+
left: ruleRef30("us.federal.agi"),
|
|
33166
34063
|
right: { kind: "param", name: "agiCeilingSingleMfs" }
|
|
33167
34064
|
},
|
|
33168
34065
|
then: { kind: "param", name: "creditSingleMfs" },
|
|
33169
|
-
else:
|
|
34066
|
+
else: money33("0")
|
|
33170
34067
|
}
|
|
33171
34068
|
}
|
|
33172
34069
|
}
|
|
@@ -33210,7 +34107,7 @@ var caRules = [
|
|
|
33210
34107
|
source: "R&TC \xA7\xA7 17062, 17062.1; 2025 FTB Schedule P (540) instructions",
|
|
33211
34108
|
section: "R&TC \xA7 17062(a)-(e); \xA7 17062.1",
|
|
33212
34109
|
url: "https://www.ftb.ca.gov/forms/2025/2025-540-p-instructions.html",
|
|
33213
|
-
excerpt: "THIS RULE TAKES caAmti AS AN INPUT \u2014 it applies the exemption/TMT/comparison mechanics only; it does not construct AMTI itself. CONSTRUCTION OF caAmti (disclosed on the fact, repeated here): California individual AMT is FROZEN to IRC \xA7\xA7 55-59 as they read on January 1, 2015 (R&TC \xA7 17062.1, re-enacted by SB 711 (Stats. 2025, Ch. 231) WITHOUT moving the freeze forward, even though SB 711 rolled CA's general IRC conformity date to January 1, 2025 for other provisions). Starting from CA taxable income (Form 540 line 19), the STANDARD DEDUCTION IS ADDED BACK (R&TC \xA7 17062(c)(1), denying \xA7 56(b)(1)(E)). The ISO exercise spread follows \xA7 56(b)(3) as modified by R&TC \xA7 17062(c)(2) (plus the California Qualified Stock Option addback, R&TC \xA7 17502). There is NO tax-exempt private-activity-bond-interest preference (R&TC \xA7 17062(d) turns off \xA7 57(a)(5)) and NO AMT foreign tax credit (R&TC \xA7 17062(e) turns off \xA7 59(a)). A taxpayer with aggregate trade/business gross receipts under $1,000,000 (a flat threshold, NOT doubled for MFJ) excludes ALL trade/business income, adjustments, and preferences from AMTI (R&TC \xA7 17062(b)(4), the CA small-business AMTI exclusion). 2025 exemption/phase-out amounts (Exemption Worksheet, 2025 FTB Schedule P (540) instructions, web-verified July 2026): exemption $92,749 single/HOH, $123,667 MFJ/QSS, $61,830 MFS; phased out 25 cents per dollar of caAmti over $347,808 single/HOH, $463,745 MFJ/QSS, $231,868 MFS, floored at $0. Rate \u2014 7% (R&TC \xA7 17062(b)(3)(A)(iv)) of caAmti minus the exemption = the tentative minimum tax. AMT (R&TC \xA7 17062(a)) = the excess, if any, of the tentative minimum tax over the regular CA tax (caRegularTax \u2014 Schedule P (540) line 25: the Form 540 LINE 31 regular tax BEFORE exemption credits;
|
|
34110
|
+
excerpt: "THIS RULE TAKES caAmti AS AN INPUT \u2014 it applies the exemption/TMT/comparison mechanics only; it does not construct AMTI itself. CONSTRUCTION OF caAmti (disclosed on the fact, repeated here): California individual AMT is FROZEN to IRC \xA7\xA7 55-59 as they read on January 1, 2015 (R&TC \xA7 17062.1, re-enacted by SB 711 (Stats. 2025, Ch. 231) WITHOUT moving the freeze forward, even though SB 711 rolled CA's general IRC conformity date to January 1, 2025 for other provisions). Starting from CA taxable income (Form 540 line 19), the STANDARD DEDUCTION IS ADDED BACK (R&TC \xA7 17062(c)(1), denying \xA7 56(b)(1)(E)). The ISO exercise spread follows \xA7 56(b)(3) as modified by R&TC \xA7 17062(c)(2) (plus the California Qualified Stock Option addback, R&TC \xA7 17502). There is NO tax-exempt private-activity-bond-interest preference (R&TC \xA7 17062(d) turns off \xA7 57(a)(5)) and NO AMT foreign tax credit (R&TC \xA7 17062(e) turns off \xA7 59(a)). A taxpayer with aggregate trade/business gross receipts under $1,000,000 (a flat threshold, NOT doubled for MFJ) excludes ALL trade/business income, adjustments, and preferences from AMTI (R&TC \xA7 17062(b)(4), the CA small-business AMTI exclusion). 2025 exemption/phase-out amounts (Exemption Worksheet, 2025 FTB Schedule P (540) instructions, web-verified July 2026): exemption $92,749 single/HOH, $123,667 MFJ/QSS, $61,830 MFS; phased out 25 cents per dollar of caAmti over $347,808 single/HOH, $463,745 MFJ/QSS, $231,868 MFS, floored at $0. Rate \u2014 7% (R&TC \xA7 17062(b)(3)(A)(iv)) of caAmti minus the exemption = the tentative minimum tax. AMT (R&TC \xA7 17062(a)) = the excess, if any, of the tentative minimum tax over the regular CA tax (caRegularTax \u2014 Schedule P (540) line 25: the Form 540 LINE 31 regular tax BEFORE exemption credits; worked example: TMT 25,484 \u2212 line-31 12,038 = AMT 13,446 \u2014 passing the after-credits line 48 amount overstates AMT by exactly the exemption credits). MFS AMTI STACKING ADD-BACK: a caAmti above $479,188 for an MFS filer requires adding to AMTI, before this rule's own exemption/phase-out math, the lesser of 25% of the excess over $479,188 or $61,830 (Schedule P (540) 'Line 21' worksheet) \u2014 NOT modeled here; this rule REFUSES for MFS above that threshold rather than silently omitting the add-back. Also not modeled: the \xA7 59(j) certain-children-under-24 exemption cap, and Part III credit ordering (e.g. the PTE elective tax credit's below-TMT exception)."
|
|
33214
34111
|
},
|
|
33215
34112
|
effectiveFrom: "2025-01-01",
|
|
33216
34113
|
effectiveTo: "2026-01-01",
|
|
@@ -33234,7 +34131,7 @@ var caRules = [
|
|
|
33234
34131
|
formula: (() => {
|
|
33235
34132
|
const byStatus = (single, joint, mfs) => ({
|
|
33236
34133
|
kind: "match",
|
|
33237
|
-
on:
|
|
34134
|
+
on: fact36("filingStatus"),
|
|
33238
34135
|
cases: [
|
|
33239
34136
|
{ when: "single", value: { kind: "param", name: single } },
|
|
33240
34137
|
{ when: "hoh", value: { kind: "param", name: single } },
|
|
@@ -33252,7 +34149,7 @@ var caRules = [
|
|
|
33252
34149
|
left: exemption2,
|
|
33253
34150
|
right: {
|
|
33254
34151
|
kind: "mulRate",
|
|
33255
|
-
base: { kind: "max0", arg: { kind: "sub", left:
|
|
34152
|
+
base: { kind: "max0", arg: { kind: "sub", left: fact36("caAmti"), right: threshold2 } },
|
|
33256
34153
|
rate: { num: "25", den: "100" },
|
|
33257
34154
|
round: "half-up"
|
|
33258
34155
|
}
|
|
@@ -33260,11 +34157,11 @@ var caRules = [
|
|
|
33260
34157
|
};
|
|
33261
34158
|
const tmt = {
|
|
33262
34159
|
kind: "mulRate",
|
|
33263
|
-
base: { kind: "max0", arg: { kind: "sub", left:
|
|
34160
|
+
base: { kind: "max0", arg: { kind: "sub", left: fact36("caAmti"), right: exemptionPhased } },
|
|
33264
34161
|
rate: { num: "7", den: "100" },
|
|
33265
34162
|
round: "half-up"
|
|
33266
34163
|
};
|
|
33267
|
-
const amt = { kind: "max0", arg: { kind: "sub", left: tmt, right:
|
|
34164
|
+
const amt = { kind: "max0", arg: { kind: "sub", left: tmt, right: fact36("caRegularTax") } };
|
|
33268
34165
|
return {
|
|
33269
34166
|
kind: "if",
|
|
33270
34167
|
cond: {
|
|
@@ -33274,7 +34171,7 @@ var caRules = [
|
|
|
33274
34171
|
{
|
|
33275
34172
|
kind: "cmp",
|
|
33276
34173
|
op: "gt",
|
|
33277
|
-
left:
|
|
34174
|
+
left: fact36("caAmti"),
|
|
33278
34175
|
right: { kind: "param", name: "mfsStackingThreshold" }
|
|
33279
34176
|
}
|
|
33280
34177
|
]
|
|
@@ -33309,7 +34206,7 @@ var caRules = [
|
|
|
33309
34206
|
kind: "mulRate",
|
|
33310
34207
|
base: {
|
|
33311
34208
|
kind: "max0",
|
|
33312
|
-
arg: { kind: "sub", left:
|
|
34209
|
+
arg: { kind: "sub", left: fact36("stateTaxableIncome"), right: { kind: "param", name: "threshold" } }
|
|
33313
34210
|
},
|
|
33314
34211
|
rate: { num: "1", den: "100" },
|
|
33315
34212
|
round: "half-up"
|
|
@@ -33353,7 +34250,7 @@ var nyRules = [
|
|
|
33353
34250
|
source: "N.Y. Tax Law \xA7 614; 2025 Form IT-201 instructions",
|
|
33354
34251
|
section: "Tax Law \xA7 614",
|
|
33355
34252
|
url: "https://www.tax.ny.gov/pit/file/standard_deductions.htm",
|
|
33356
|
-
excerpt: "Standard deductions (Tax Law \xA7 614, 2025-confirmed): $8,000 single; $16,050 MFJ/QSS; $11,200 HOH (reference-validated against ty25-ny-002); $8,000 MFS; $3,100 dependent filer. Dependent exemptions $1,000 each (IT-201 line 36). CREDITS & MODIFICATIONS (all web-verified from the 2025 IT-201-I / IT-213-I / IT-215-I / IT-216-I / IT-214-I / IT-225-I / IT-272-I / IT-196-I): NYS EIC = 30% of federal EIC, reduced by the household credit ACTUALLY ABSORBED against NYS tax (min(line 39 tax, household credit) \u2014 when tax is $0 the reduction is $0 and the full 30% is refundable; IT-215 worksheet). NYC EIC = federal EIC \xD7 a sliding NYAGI rate: 30% \u2264 $7,500; 25% flat $7,500\u2013$15,000; phasing 25\u219220% to $17,500; 20% to $20,000; phasing 20\u219215% to $22,500; 15% to $40,000; phasing 15\u219210% to $42,500; 10% above. EMPIRE STATE CHILD CREDIT 2025 (IT-213, decoupled from federal): $1,000 per qualifying child UNDER FOUR + $330 per child 4\u201316 (TY2025; rises to $500 for the under-4 group's OLDER siblings \u2014 the ESCC ages-4-16 amount is $500 for TY2026-27 only, not yet in effect for TY2025), reduced $16.50 per $1,000 of FAGI (rounded DOWN to the nearest $1,000) over $110,000 MFJ / $75,000 single-HOH-QSS / $55,000 MFS.\n\nNYS HOUSEHOLD CREDIT (IT-201 line 40, IT-201-I p.13, keyed to FEDERAL AGI = Form IT-201 line 19) \u2014 THREE TABLES, verbatim 2025:\nTable 1 (single only): FAGI over \u2013$5,000 \u2192 $75; $5,000\u2013$6,000 \u2192 $60; $6,000\u2013$7,000 \u2192 $50; $7,000\u2013$20,000 \u2192 $45; $20,000\u2013$25,000 \u2192 $40; $25,000\u2013$28,000 \u2192 $20; over $28,000 \u2192 no credit.\nTable 2 (MFJ/HOH/QSS), by (dependents + 1 for filer + 1 for spouse if MFJ) = 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$5,000 \u2192 90/105/120/135/150/165/180/+15; $5,000\u2013$6,000 \u2192 75/90/105/120/135/150/165/+15; $6,000\u2013$7,000 \u2192 65/80/95/110/125/140/155/+15; $7,000\u2013$20,000 \u2192 60/75/90/105/120/135/150/+15; $20,000\u2013$22,000 \u2192 60/70/80/90/100/110/120/+10; $22,000\u2013$25,000 \u2192 50/60/70/80/90/100/110/+10; $25,000\u2013$28,000 \u2192 40/45/50/55/60/65/70/+5; $28,000\u2013$32,000 \u2192 20/25/30/35/40/45/50/+5; over $32,000 \u2192 no credit.\nTable 3 (MFS only, dependents from BOTH returns + 1 for self + 1 for spouse), same 1-7/+over-7 columns: FAGI \u2013$5,000 \u2192 45/53/60/68/75/83/90/+8; $5,000\u2013$6,000 \u2192 38/45/53/60/68/75/83/+8; $6,000\u2013$7,000 \u2192 33/40/48/55/63/70/78/+8; $7,000\u2013$20,000 \u2192 30/38/45/53/60/68/75/+8; $20,000\u2013$22,000 \u2192 30/35/40/45/50/55/60/+5; $22,000\u2013$25,000 \u2192 25/30/35/40/45/50/55/+5; $25,000\u2013$28,000 \u2192 20/23/25/28/30/33/35/+3; $28,000\u2013$32,000 \u2192 10/13/15/18/20/23/25/+3; over $32,000 \u2192 no credit (table 3 amounts are pre-rounded per the form's Note 5).\n\nNYC HOUSEHOLD CREDIT (line 48, IT-201-I p.14) \u2014 THREE TABLES, verbatim 2025:\nTable 4 (single only): FAGI \u2013$10,000 \u2192 $15; $10,000\u2013$12,500 \u2192 $10; over $12,500 \u2192 no credit.\nTable 5 (MFJ/HOH/QSS), 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$15,000 \u2192 30/60/90/120/150/180/210/+30; $15,000\u2013$17,500 \u2192 25/50/75/100/125/150/175/+25; $17,500\u2013$20,000 \u2192 15/30/45/60/75/90/105/+15; $20,000\u2013$22,500 \u2192 10/20/30/40/50/60/70/+10; over $22,500 \u2192 no credit.\nTable 6 (MFS only, dependents both returns + self + spouse), 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$15,000 \u2192 15/30/45/60/75/90/105/+15; $15,000\u2013$17,500 \u2192 13/25/38/50/63/75/88/+13; $17,500\u2013$20,000 \u2192 8/15/23/30/38/45/53/+8; $20,000\u2013$22,500 \u2192 5/10/15/20/25/30/35/+5; over $22,500 \u2192 no credit (pre-rounded, Note 5).\n\nIT-216 CDCC (2025 IT-216-I, verbatim factor tables \u2014 replaces the earlier '1.10 \u2264 $25k' shorthand): base = qualifying expenses NET of employer dependent-care benefits excluded on W-2 box 10. NYS LINE-13 FACTOR (NYAGI-keyed, NON-MONOTONIC SAWTOOTH, IT-216-I p.7 'credit limitation table', $200-wide rows \u2014 segment anchors verbatim): flat 1.100 for NYAGI \u2013$25,000; descends in small ($0.001\u2013$0.002-per-$200) steps from 1.099 at $25,000 down to 1.000 by $40,000; FLAT 1.000 for $40,000\u2013$50,000; JUMPS to 1.162 at $50,000 and descends to 1.000 by $52,600\u2013$52,800, continuing down to 0.601 by $59,800\u2013$60,000; JUMPS AGAIN to 1.070 at $60,000 and descends to 0.476 by $64,800\u2013$65,000; FLAT 0.600 for $65,000\u2013$150,000; FLAT 0.200 above $150,000. NYS LINE-10 FACTOR (separate table, FAGI-keyed, monotonic): .35 for FAGI \u2013$15,000, stepping down by .01 per $2,000 of FAGI to .20 at $43,000 and above (verbatim anchors: $15,000\u2013$17,000 \u2192 .34 \u2026 $41,000\u2013$43,000 \u2192 .21; $43,000+ \u2192 .20). NYC CHILD CARE CREDIT (Worksheet 2 \u2014 now a standalone computable target, us.ny.nyc_cdcc): requires a qualifying child UNDER 4 and FAGI $30,000 or less; the worksheet PRORATES THE NYS CREDIT ITSELF (IT-216 line 14 \u2192 Worksheet 2 line 1) by the under-4 expense share (line 23 \xF7 line 3a, capped at 1.0000), THEN multiplies by the line-6 FAGI factor (0.750 for FAGI \u2013$25,000, descending 0.030 per $200 to 0.000 at $30,000). NEVER prorate the raw expenses and re-apply the line-10/line-13 factors \u2014 that understates the credit; feed nyIt216StateCredit/nyIt216Under4Expenses/nyIt216TotalExpenses to us.ny.nyc_cdcc and use its answer.\n\nNYC SCHOOL TAX CREDIT fixed (line 69, income \u2264 $250,000): $63 single/MFS/HOH, $125 MFJ/QSS; plus the line 69a rate-reduction amount (\u2264 $500,000): HOH 0.171% of city taxable income to $14,400 then $25 + 0.228% of excess (single/MFS threshold $12,000/$21; MFJ $21,600/$37). IT-214 REAL PROPERTY TAX CREDIT (Form IT-214 (2025), read directly from the printed form \u2014 SECOND CORRECTION, July 2026: line 12 uses a FIXED PERCENTAGE MENU, not actual utility dollar subtraction): line 8 FAGI \u2264 $18,000 stop; line 12 adjusted rent = line 11 total rent x the printed menu \u2014 rent includes heat+gas+electricity+furnishings+board \u2192 50%; heat+gas+electricity+furnishings \u2192 75%; heat+gas+electricity \u2192 80%; heat (or heat+gas) \u2192 85%; none \u2192 100%; line 13 average monthly adjusted rent (line 12 \xF7 months paid) \u2264 $450 stop; line 14 = 25% x line 12; homeowners line 17 = property taxes + special assessments; line 18 (renters' 14 / homeowners' 17) must be positive and must EXCEED line 19 = FAGI x Table 1 rate (0.035 to 0.065 by FAGI bracket). THE CREDIT (line 20) is a FLAT Table A/B lookup by FAGI. ALL of this arithmetic is COMPUTED by us.ny.it214 \u2014 feed it nyIt214Fagi, nyIt214TotalRent, nyIt214RentPercent (50/75/80/85/100 from the checkbox menu), nyIt214MonthsPaid, nyIt214HomeownerTaxes, isAge65OrOlder (any household member 65+ routes to Table A: $375/$330/$300/$260/$230/$200/$150), and nyIt214Eligible (Step 2 attestations only). Pass its answer into refundableCredits; never hand-compute any IT-214 line.\n\nIT-272 COLLEGE TUITION CREDIT (2025 IT-272-I): full-year NYS RESIDENTS only (part-year and nonresidents cannot claim it); qualified tuition expenses CAPPED at $10,000 per eligible student; credit = the LESSER of $400 or 4% of qualified expenses PER STUDENT (so the $400 max binds at $10,000 of expenses \u2014 confirm the 4% literal rate on the current worksheet before treating it as fixed across years); taxpayers may instead claim the college tuition ITEMIZED DEDUCTION on IT-201-D \u2014 mutually exclusive per student, elect whichever is larger.\n\nIT-229 REAL PROPERTY TAX RELIEF CREDIT: EXPIRED after TY2023 \u2014 DOES NOT EXIST for TY2025 or TY2026; do not encode or claim it. IT-214 (circuit breaker: FAGI \u2264 $18,000, property value \u2264 $85,000, rent \u2264 $450/month) is the live real-property-tax credit for TY2025.\n\n529 COLLEGE SAVINGS SUBTRACTION (Tax Law \xA7 612(c)(32)): up to $5,000 single/HOH/MFS, $10,000 MFJ, of contributions to a New York 529 account per year (recapture applies to nonqualified withdrawals of previously subtracted amounts). PENSION AND ANNUITY EXCLUSION (Tax Law \xA7 612(c)(3-a)): up to $20,000 EACH for taxpayer and spouse (no spousal sharing of unused exclusion) once age 59\xBD, for private/out-of-state pensions and annuities; NYS/local government and federal government pensions are excluded IN FULL regardless of age (a separate, unlimited subtraction, not capped at $20,000).\n\n\xA7 685(c) ESTIMATED TAX: no addition to tax if the amount owed after withholding is under $300 (de minimis); safe harbor is the LESSER of 90% of the current year's tax or 100% of the prior year's tax (110% of prior year's tax if the prior year's NYAGI exceeded $150,000).\n\nIT-196 ITEMIZED CHARITABLE LIMITATION (Tax Law \xA7 615, active through TY2029 per current law): for taxpayers with NYAGI over $1,000,000, the itemized charitable contribution deduction is limited to 50% of the federal amount; over $10,000,000 NYAGI, limited to 25% of the federal amount. UNRESOLVED (do NOT encode): the broader \xA7 615(f) general itemized-deduction phase-down for high-NYAGI filers could not be reconciled against the 2025 IT-196 instructions worksheet during this web-verification \u2014 pull the actual worksheet before modeling it; only the $1M/$10M charitable-specific limitation above is confirmed active.\n\nIT-225 MODIFICATIONS: \xA7 414(h) public-employee pickup = addition on IT-201 line 21 directly (code A-104, NOT via IT-225); NYC IRC \xA7 125 flexible-benefits = addition A-101; ALIMONY (NY did NOT conform to TCJA \xA7 11051): post-2018-instrument alimony PAID = subtraction S-136, alimony RECEIVED = addition A-119. S-120 \u2014 New York Higher Education Loan Program (HELP) loan interest (2025 IT-225-I p.6 subtraction-modifications chart and detail section, applicable to IT-201/IT-203 filers, not IT-204/IT-205), quoted verbatim: chart entry 'S-120 New York Higher Education Loan Program (HELP)'; detail text 'S-120: New York Higher Education Loan Program (HELP) \u2014 Enter any interest you paid in 2025 on loans made to you under HELP.' STATUTE CITE UNRESOLVED: the IT-225-I chart/detail text does not itself cite a Tax Law \xA7 612(c) paragraph number for S-120 (unlike some neighboring codes), and a web search of the codified \xA7 612 (nysenate.gov) did not turn up a matching paragraph as of this July 2026 verification \u2014 treat the CODE as confirmed-current for TY2025 via the 2025 IT-225-I form itself (the authority cited here), with the enabling paragraph number unresolved rather than guessed. Do not confuse with the separate, prospective, general student-loan-interest deduction tracked for a future Tax Law \xA7 612(c) paragraph (TY2026+, following 26 U.S.C. \xA7 221) \u2014 that is a different, not-yet-effective provision and does not affect the TY2025 S-120 answer.\n\nTY2026 ENACTED CHANGES (not encoded this pass \u2014 informational only, so a 2026 asOf refusal has useful context): a new subtraction for tips (up to $25,000, OBBBA conformity) begins TY2026; a new 'POWER credit' applies for TY2026 ONLY; a new REFUNDABLE CDCC under \xA7 606(c-2) replaces the nonrefundable IT-216 coupling for TY2026+ (55% of federal CDCC at NYAGI \u2264 $15,000, phasing down to a 4% floor, reduced further above $750,000 NYAGI); the Empire State Child Credit's $330 per-child (age 4-16) amount rises to $500 for TY2026-2027. Brackets, IT-272, the 529 subtraction, the household-credit tables, and IT-214 are UNCHANGED for TY2026 per the sources reviewed. Tax computation \u2192 us.ny.income_tax; NYC resident tax \u2192 us.ny.nyc_income_tax."
|
|
34253
|
+
excerpt: "Standard deductions (Tax Law \xA7 614, 2025-confirmed): $8,000 single; $16,050 MFJ/QSS; $11,200 HOH; $8,000 MFS; $3,100 dependent filer. Dependent exemptions $1,000 each (IT-201 line 36). CREDITS & MODIFICATIONS (all web-verified from the 2025 IT-201-I / IT-213-I / IT-215-I / IT-216-I / IT-214-I / IT-225-I / IT-272-I / IT-196-I): NYS EIC = 30% of federal EIC, reduced by the household credit ACTUALLY ABSORBED against NYS tax (min(line 39 tax, household credit) \u2014 when tax is $0 the reduction is $0 and the full 30% is refundable; IT-215 worksheet). NYC EIC = federal EIC \xD7 a sliding NYAGI rate: 30% \u2264 $7,500; 25% flat $7,500\u2013$15,000; phasing 25\u219220% to $17,500; 20% to $20,000; phasing 20\u219215% to $22,500; 15% to $40,000; phasing 15\u219210% to $42,500; 10% above. EMPIRE STATE CHILD CREDIT 2025 (IT-213, decoupled from federal): $1,000 per qualifying child UNDER FOUR + $330 per child 4\u201316 (TY2025; rises to $500 for the under-4 group's OLDER siblings \u2014 the ESCC ages-4-16 amount is $500 for TY2026-27 only, not yet in effect for TY2025), reduced $16.50 per $1,000 of FAGI (rounded DOWN to the nearest $1,000) over $110,000 MFJ / $75,000 single-HOH-QSS / $55,000 MFS.\n\nNYS HOUSEHOLD CREDIT (IT-201 line 40, IT-201-I p.13, keyed to FEDERAL AGI = Form IT-201 line 19) \u2014 THREE TABLES, verbatim 2025:\nTable 1 (single only): FAGI over \u2013$5,000 \u2192 $75; $5,000\u2013$6,000 \u2192 $60; $6,000\u2013$7,000 \u2192 $50; $7,000\u2013$20,000 \u2192 $45; $20,000\u2013$25,000 \u2192 $40; $25,000\u2013$28,000 \u2192 $20; over $28,000 \u2192 no credit.\nTable 2 (MFJ/HOH/QSS), by (dependents + 1 for filer + 1 for spouse if MFJ) = 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$5,000 \u2192 90/105/120/135/150/165/180/+15; $5,000\u2013$6,000 \u2192 75/90/105/120/135/150/165/+15; $6,000\u2013$7,000 \u2192 65/80/95/110/125/140/155/+15; $7,000\u2013$20,000 \u2192 60/75/90/105/120/135/150/+15; $20,000\u2013$22,000 \u2192 60/70/80/90/100/110/120/+10; $22,000\u2013$25,000 \u2192 50/60/70/80/90/100/110/+10; $25,000\u2013$28,000 \u2192 40/45/50/55/60/65/70/+5; $28,000\u2013$32,000 \u2192 20/25/30/35/40/45/50/+5; over $32,000 \u2192 no credit.\nTable 3 (MFS only, dependents from BOTH returns + 1 for self + 1 for spouse), same 1-7/+over-7 columns: FAGI \u2013$5,000 \u2192 45/53/60/68/75/83/90/+8; $5,000\u2013$6,000 \u2192 38/45/53/60/68/75/83/+8; $6,000\u2013$7,000 \u2192 33/40/48/55/63/70/78/+8; $7,000\u2013$20,000 \u2192 30/38/45/53/60/68/75/+8; $20,000\u2013$22,000 \u2192 30/35/40/45/50/55/60/+5; $22,000\u2013$25,000 \u2192 25/30/35/40/45/50/55/+5; $25,000\u2013$28,000 \u2192 20/23/25/28/30/33/35/+3; $28,000\u2013$32,000 \u2192 10/13/15/18/20/23/25/+3; over $32,000 \u2192 no credit (table 3 amounts are pre-rounded per the form's Note 5).\n\nNYC HOUSEHOLD CREDIT (line 48, IT-201-I p.14) \u2014 THREE TABLES, verbatim 2025:\nTable 4 (single only): FAGI \u2013$10,000 \u2192 $15; $10,000\u2013$12,500 \u2192 $10; over $12,500 \u2192 no credit.\nTable 5 (MFJ/HOH/QSS), 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$15,000 \u2192 30/60/90/120/150/180/210/+30; $15,000\u2013$17,500 \u2192 25/50/75/100/125/150/175/+25; $17,500\u2013$20,000 \u2192 15/30/45/60/75/90/105/+15; $20,000\u2013$22,500 \u2192 10/20/30/40/50/60/70/+10; over $22,500 \u2192 no credit.\nTable 6 (MFS only, dependents both returns + self + spouse), 1/2/3/4/5/6/7/each-over-7: FAGI \u2013$15,000 \u2192 15/30/45/60/75/90/105/+15; $15,000\u2013$17,500 \u2192 13/25/38/50/63/75/88/+13; $17,500\u2013$20,000 \u2192 8/15/23/30/38/45/53/+8; $20,000\u2013$22,500 \u2192 5/10/15/20/25/30/35/+5; over $22,500 \u2192 no credit (pre-rounded, Note 5).\n\nIT-216 CDCC (2025 IT-216-I, verbatim factor tables \u2014 replaces the earlier '1.10 \u2264 $25k' shorthand): base = qualifying expenses NET of employer dependent-care benefits excluded on W-2 box 10. NYS LINE-13 FACTOR (NYAGI-keyed, NON-MONOTONIC SAWTOOTH, IT-216-I p.7 'credit limitation table', $200-wide rows \u2014 segment anchors verbatim): flat 1.100 for NYAGI \u2013$25,000; descends in small ($0.001\u2013$0.002-per-$200) steps from 1.099 at $25,000 down to 1.000 by $40,000; FLAT 1.000 for $40,000\u2013$50,000; JUMPS to 1.162 at $50,000 and descends to 1.000 by $52,600\u2013$52,800, continuing down to 0.601 by $59,800\u2013$60,000; JUMPS AGAIN to 1.070 at $60,000 and descends to 0.476 by $64,800\u2013$65,000; FLAT 0.600 for $65,000\u2013$150,000; FLAT 0.200 above $150,000. NYS LINE-10 FACTOR (separate table, FAGI-keyed, monotonic): .35 for FAGI \u2013$15,000, stepping down by .01 per $2,000 of FAGI to .20 at $43,000 and above (verbatim anchors: $15,000\u2013$17,000 \u2192 .34 \u2026 $41,000\u2013$43,000 \u2192 .21; $43,000+ \u2192 .20). NYC CHILD CARE CREDIT (Worksheet 2 \u2014 now a standalone computable target, us.ny.nyc_cdcc): requires a qualifying child UNDER 4 and FAGI $30,000 or less; the worksheet PRORATES THE NYS CREDIT ITSELF (IT-216 line 14 \u2192 Worksheet 2 line 1) by the under-4 expense share (line 23 \xF7 line 3a, capped at 1.0000), THEN multiplies by the line-6 FAGI factor (0.750 for FAGI \u2013$25,000, descending 0.030 per $200 to 0.000 at $30,000). NEVER prorate the raw expenses and re-apply the line-10/line-13 factors \u2014 that understates the credit; feed nyIt216StateCredit/nyIt216Under4Expenses/nyIt216TotalExpenses to us.ny.nyc_cdcc and use its answer.\n\nNYC SCHOOL TAX CREDIT fixed (line 69, income \u2264 $250,000): $63 single/MFS/HOH, $125 MFJ/QSS; plus the line 69a rate-reduction amount (\u2264 $500,000): HOH 0.171% of city taxable income to $14,400 then $25 + 0.228% of excess (single/MFS threshold $12,000/$21; MFJ $21,600/$37). IT-214 REAL PROPERTY TAX CREDIT (Form IT-214 (2025), read directly from the printed form \u2014 SECOND CORRECTION, July 2026: line 12 uses a FIXED PERCENTAGE MENU, not actual utility dollar subtraction): line 8 FAGI \u2264 $18,000 stop; line 12 adjusted rent = line 11 total rent x the printed menu \u2014 rent includes heat+gas+electricity+furnishings+board \u2192 50%; heat+gas+electricity+furnishings \u2192 75%; heat+gas+electricity \u2192 80%; heat (or heat+gas) \u2192 85%; none \u2192 100%; line 13 average monthly adjusted rent (line 12 \xF7 months paid) \u2264 $450 stop; line 14 = 25% x line 12; homeowners line 17 = property taxes + special assessments; line 18 (renters' 14 / homeowners' 17) must be positive and must EXCEED line 19 = FAGI x Table 1 rate (0.035 to 0.065 by FAGI bracket). THE CREDIT (line 20) is a FLAT Table A/B lookup by FAGI. ALL of this arithmetic is COMPUTED by us.ny.it214 \u2014 feed it nyIt214Fagi, nyIt214TotalRent, nyIt214RentPercent (50/75/80/85/100 from the checkbox menu), nyIt214MonthsPaid, nyIt214HomeownerTaxes, isAge65OrOlder (any household member 65+ routes to Table A: $375/$330/$300/$260/$230/$200/$150), and nyIt214Eligible (Step 2 attestations only). Pass its answer into refundableCredits; never hand-compute any IT-214 line.\n\nIT-272 COLLEGE TUITION CREDIT (2025 IT-272-I): full-year NYS RESIDENTS only (part-year and nonresidents cannot claim it); qualified tuition expenses CAPPED at $10,000 per eligible student; credit = the LESSER of $400 or 4% of qualified expenses PER STUDENT (so the $400 max binds at $10,000 of expenses \u2014 confirm the 4% literal rate on the current worksheet before treating it as fixed across years); taxpayers may instead claim the college tuition ITEMIZED DEDUCTION on IT-201-D \u2014 mutually exclusive per student, elect whichever is larger.\n\nIT-229 REAL PROPERTY TAX RELIEF CREDIT: EXPIRED after TY2023 \u2014 DOES NOT EXIST for TY2025 or TY2026; do not encode or claim it. IT-214 (circuit breaker: FAGI \u2264 $18,000, property value \u2264 $85,000, rent \u2264 $450/month) is the live real-property-tax credit for TY2025.\n\n529 COLLEGE SAVINGS SUBTRACTION (Tax Law \xA7 612(c)(32)): up to $5,000 single/HOH/MFS, $10,000 MFJ, of contributions to a New York 529 account per year (recapture applies to nonqualified withdrawals of previously subtracted amounts). PENSION AND ANNUITY EXCLUSION (Tax Law \xA7 612(c)(3-a)): up to $20,000 EACH for taxpayer and spouse (no spousal sharing of unused exclusion) once age 59\xBD, for private/out-of-state pensions and annuities; NYS/local government and federal government pensions are excluded IN FULL regardless of age (a separate, unlimited subtraction, not capped at $20,000).\n\n\xA7 685(c) ESTIMATED TAX: no addition to tax if the amount owed after withholding is under $300 (de minimis); safe harbor is the LESSER of 90% of the current year's tax or 100% of the prior year's tax (110% of prior year's tax if the prior year's NYAGI exceeded $150,000).\n\nIT-196 ITEMIZED CHARITABLE LIMITATION (Tax Law \xA7 615, active through TY2029 per current law): for taxpayers with NYAGI over $1,000,000, the itemized charitable contribution deduction is limited to 50% of the federal amount; over $10,000,000 NYAGI, limited to 25% of the federal amount. UNRESOLVED (do NOT encode): the broader \xA7 615(f) general itemized-deduction phase-down for high-NYAGI filers could not be reconciled against the 2025 IT-196 instructions worksheet during this web-verification \u2014 pull the actual worksheet before modeling it; only the $1M/$10M charitable-specific limitation above is confirmed active.\n\nIT-225 MODIFICATIONS: \xA7 414(h) public-employee pickup = addition on IT-201 line 21 directly (code A-104, NOT via IT-225); NYC IRC \xA7 125 flexible-benefits = addition A-101; ALIMONY (NY did NOT conform to TCJA \xA7 11051): post-2018-instrument alimony PAID = subtraction S-136, alimony RECEIVED = addition A-119. S-120 \u2014 New York Higher Education Loan Program (HELP) loan interest (2025 IT-225-I p.6 subtraction-modifications chart and detail section, applicable to IT-201/IT-203 filers, not IT-204/IT-205), quoted verbatim: chart entry 'S-120 New York Higher Education Loan Program (HELP)'; detail text 'S-120: New York Higher Education Loan Program (HELP) \u2014 Enter any interest you paid in 2025 on loans made to you under HELP.' STATUTE CITE UNRESOLVED: the IT-225-I chart/detail text does not itself cite a Tax Law \xA7 612(c) paragraph number for S-120 (unlike some neighboring codes), and a web search of the codified \xA7 612 (nysenate.gov) did not turn up a matching paragraph as of this July 2026 verification \u2014 treat the CODE as confirmed-current for TY2025 via the 2025 IT-225-I form itself (the authority cited here), with the enabling paragraph number unresolved rather than guessed. Do not confuse with the separate, prospective, general student-loan-interest deduction tracked for a future Tax Law \xA7 612(c) paragraph (TY2026+, following 26 U.S.C. \xA7 221) \u2014 that is a different, not-yet-effective provision and does not affect the TY2025 S-120 answer.\n\nTY2026 ENACTED CHANGES (not encoded this pass \u2014 informational only, so a 2026 asOf refusal has useful context): a new subtraction for tips (up to $25,000, OBBBA conformity) begins TY2026; a new 'POWER credit' applies for TY2026 ONLY; a new REFUNDABLE CDCC under \xA7 606(c-2) replaces the nonrefundable IT-216 coupling for TY2026+ (55% of federal CDCC at NYAGI \u2264 $15,000, phasing down to a 4% floor, reduced further above $750,000 NYAGI); the Empire State Child Credit's $330 per-child (age 4-16) amount rises to $500 for TY2026-2027. Brackets, IT-272, the 529 subtraction, the household-credit tables, and IT-214 are UNCHANGED for TY2026 per the sources reviewed. Tax computation \u2192 us.ny.income_tax; NYC resident tax \u2192 us.ny.nyc_income_tax."
|
|
33357
34254
|
},
|
|
33358
34255
|
effectiveFrom: "2025-01-01",
|
|
33359
34256
|
effectiveTo: "2026-01-01",
|
|
@@ -33485,19 +34382,19 @@ var nyRules = [
|
|
|
33485
34382
|
},
|
|
33486
34383
|
formula: (() => {
|
|
33487
34384
|
const param22 = (name) => ({ kind: "param", name });
|
|
33488
|
-
const fagi =
|
|
34385
|
+
const fagi = fact36("nyIt214Fagi");
|
|
33489
34386
|
const adjustedRent = {
|
|
33490
34387
|
kind: "mulDiv",
|
|
33491
|
-
a: { kind: "max0", arg:
|
|
33492
|
-
b: { kind: "mulInt", base:
|
|
33493
|
-
c:
|
|
34388
|
+
a: { kind: "max0", arg: fact36("nyIt214TotalRent") },
|
|
34389
|
+
b: { kind: "mulInt", base: money33("1"), count: fact36("nyIt214RentPercent") },
|
|
34390
|
+
c: money33("100"),
|
|
33494
34391
|
round: "half-up"
|
|
33495
34392
|
};
|
|
33496
34393
|
const avgMonthlyRent = {
|
|
33497
34394
|
kind: "mulDiv",
|
|
33498
34395
|
a: adjustedRent,
|
|
33499
|
-
b:
|
|
33500
|
-
c: { kind: "max", args: [
|
|
34396
|
+
b: money33("1"),
|
|
34397
|
+
c: { kind: "max", args: [money33("1"), { kind: "mulInt", base: money33("1"), count: fact36("nyIt214MonthsPaid") }] },
|
|
33501
34398
|
round: "half-up"
|
|
33502
34399
|
};
|
|
33503
34400
|
const line14 = {
|
|
@@ -33508,15 +34405,15 @@ var nyRules = [
|
|
|
33508
34405
|
};
|
|
33509
34406
|
const line18 = {
|
|
33510
34407
|
kind: "add",
|
|
33511
|
-
args: [line14, { kind: "max0", arg:
|
|
34408
|
+
args: [line14, { kind: "max0", arg: fact36("nyIt214HomeownerTaxes") }]
|
|
33512
34409
|
};
|
|
33513
34410
|
const table1Rate = (perMille, threshold2, next) => ({
|
|
33514
34411
|
kind: "if",
|
|
33515
|
-
cond: { kind: "cmp", op: "le", left: fagi, right:
|
|
33516
|
-
then: { kind: "mulDiv", a: fagi, b:
|
|
34412
|
+
cond: { kind: "cmp", op: "le", left: fagi, right: money33(threshold2) },
|
|
34413
|
+
then: { kind: "mulDiv", a: fagi, b: money33(perMille), c: money33("1000"), round: "half-up" },
|
|
33517
34414
|
else: next
|
|
33518
34415
|
});
|
|
33519
|
-
const line19 = table1Rate("35", "300000", table1Rate("40", "500000", table1Rate("45", "700000", table1Rate("50", "900000", table1Rate("55", "1100000", table1Rate("60", "1400000", { kind: "mulDiv", a: fagi, b:
|
|
34416
|
+
const line19 = table1Rate("35", "300000", table1Rate("40", "500000", table1Rate("45", "700000", table1Rate("50", "900000", table1Rate("55", "1100000", table1Rate("60", "1400000", { kind: "mulDiv", a: fagi, b: money33("65"), c: money33("1000"), round: "half-up" }))))));
|
|
33520
34417
|
const tableA = {
|
|
33521
34418
|
kind: "if",
|
|
33522
34419
|
cond: { kind: "cmp", op: "le", left: fagi, right: param22("tableAThreshold1") },
|
|
@@ -33545,7 +34442,7 @@ var nyRules = [
|
|
|
33545
34442
|
kind: "if",
|
|
33546
34443
|
cond: { kind: "cmp", op: "le", left: fagi, right: param22("fagiLimit") },
|
|
33547
34444
|
then: param22("tableAValue7"),
|
|
33548
|
-
else:
|
|
34445
|
+
else: money33("0")
|
|
33549
34446
|
}
|
|
33550
34447
|
}
|
|
33551
34448
|
}
|
|
@@ -33569,7 +34466,7 @@ var nyRules = [
|
|
|
33569
34466
|
kind: "if",
|
|
33570
34467
|
cond: { kind: "cmp", op: "le", left: fagi, right: param22("fagiLimit") },
|
|
33571
34468
|
then: param22("tableBValue4"),
|
|
33572
|
-
else:
|
|
34469
|
+
else: money33("0")
|
|
33573
34470
|
}
|
|
33574
34471
|
}
|
|
33575
34472
|
}
|
|
@@ -33579,21 +34476,21 @@ var nyRules = [
|
|
|
33579
34476
|
cond: {
|
|
33580
34477
|
kind: "or",
|
|
33581
34478
|
args: [
|
|
33582
|
-
{ kind: "not", arg:
|
|
34479
|
+
{ kind: "not", arg: fact36("nyIt214Eligible") },
|
|
33583
34480
|
{ kind: "cmp", op: "gt", left: fagi, right: param22("fagiLimit") },
|
|
33584
34481
|
// line 8 stop
|
|
33585
34482
|
{ kind: "cmp", op: "gt", left: avgMonthlyRent, right: param22("monthlyRentCap") },
|
|
33586
34483
|
// line 13 stop
|
|
33587
|
-
{ kind: "cmp", op: "le", left: line18, right:
|
|
34484
|
+
{ kind: "cmp", op: "le", left: line18, right: money33("0") },
|
|
33588
34485
|
// line 18 stop
|
|
33589
34486
|
{ kind: "cmp", op: "ge", left: line19, right: line18 }
|
|
33590
34487
|
// line 19 stop
|
|
33591
34488
|
]
|
|
33592
34489
|
},
|
|
33593
|
-
then:
|
|
34490
|
+
then: money33("0"),
|
|
33594
34491
|
else: {
|
|
33595
34492
|
kind: "if",
|
|
33596
|
-
cond:
|
|
34493
|
+
cond: fact36("isAge65OrOlder"),
|
|
33597
34494
|
then: tableA,
|
|
33598
34495
|
else: tableB
|
|
33599
34496
|
}
|
|
@@ -33629,10 +34526,10 @@ var nyRules = [
|
|
|
33629
34526
|
// 0.030 per $200 row
|
|
33630
34527
|
},
|
|
33631
34528
|
formula: (() => {
|
|
33632
|
-
const stateCredit = { kind: "max0", arg:
|
|
33633
|
-
const under4 = { kind: "max0", arg:
|
|
33634
|
-
const total = { kind: "max0", arg:
|
|
33635
|
-
const fagi =
|
|
34529
|
+
const stateCredit = { kind: "max0", arg: fact36("nyIt216StateCredit") };
|
|
34530
|
+
const under4 = { kind: "max0", arg: fact36("nyIt216Under4Expenses") };
|
|
34531
|
+
const total = { kind: "max0", arg: fact36("nyIt216TotalExpenses") };
|
|
34532
|
+
const fagi = ruleRef30("us.federal.agi");
|
|
33636
34533
|
const line5 = {
|
|
33637
34534
|
kind: "min",
|
|
33638
34535
|
args: [
|
|
@@ -33650,15 +34547,15 @@ var nyRules = [
|
|
|
33650
34547
|
kind: "max0",
|
|
33651
34548
|
arg: {
|
|
33652
34549
|
kind: "sub",
|
|
33653
|
-
left:
|
|
33654
|
-
right: { kind: "mulInt", base:
|
|
34550
|
+
left: money33("765"),
|
|
34551
|
+
right: { kind: "mulInt", base: money33("30"), count: rowIndex }
|
|
33655
34552
|
}
|
|
33656
34553
|
};
|
|
33657
34554
|
const line7 = (factor) => ({
|
|
33658
34555
|
kind: "mulDiv",
|
|
33659
34556
|
a: line5,
|
|
33660
34557
|
b: factor,
|
|
33661
|
-
c:
|
|
34558
|
+
c: money33("1000"),
|
|
33662
34559
|
round: "half-up"
|
|
33663
34560
|
});
|
|
33664
34561
|
return {
|
|
@@ -33667,18 +34564,18 @@ var nyRules = [
|
|
|
33667
34564
|
kind: "or",
|
|
33668
34565
|
args: [
|
|
33669
34566
|
{ kind: "cmp", op: "gt", left: fagi, right: { kind: "param", name: "fagiCutoff" } },
|
|
33670
|
-
{ kind: "cmp", op: "le", left: under4, right:
|
|
33671
|
-
{ kind: "cmp", op: "le", left: total, right:
|
|
34567
|
+
{ kind: "cmp", op: "le", left: under4, right: money33("0") },
|
|
34568
|
+
{ kind: "cmp", op: "le", left: total, right: money33("0") }
|
|
33672
34569
|
]
|
|
33673
34570
|
},
|
|
33674
|
-
then:
|
|
34571
|
+
then: money33("0"),
|
|
33675
34572
|
else: {
|
|
33676
34573
|
kind: "roundToDollar",
|
|
33677
34574
|
mode: "half-up",
|
|
33678
34575
|
value: {
|
|
33679
34576
|
kind: "if",
|
|
33680
34577
|
cond: { kind: "cmp", op: "le", left: fagi, right: { kind: "param", name: "fagiFlatCeiling" } },
|
|
33681
|
-
then: line7(
|
|
34578
|
+
then: line7(money33("750")),
|
|
33682
34579
|
else: line7(factorDeclining)
|
|
33683
34580
|
}
|
|
33684
34581
|
}
|
|
@@ -33707,7 +34604,7 @@ var nyRules = [
|
|
|
33707
34604
|
// $107,650 (NYAGI-keyed)
|
|
33708
34605
|
},
|
|
33709
34606
|
formula: (() => {
|
|
33710
|
-
const base = { kind: "max0", arg:
|
|
34607
|
+
const base = { kind: "max0", arg: fact36("stateTaxableIncome") };
|
|
33711
34608
|
const scheduleFor = (b, status) => {
|
|
33712
34609
|
const rows = {
|
|
33713
34610
|
single: [
|
|
@@ -33737,10 +34634,10 @@ var nyRules = [
|
|
|
33737
34634
|
args: [
|
|
33738
34635
|
{
|
|
33739
34636
|
kind: "mulInt",
|
|
33740
|
-
base:
|
|
34637
|
+
base: money33("5000"),
|
|
33741
34638
|
count: { kind: "stepUnits", value: base, unitCents: "5000", mode: "floor" }
|
|
33742
34639
|
},
|
|
33743
|
-
|
|
34640
|
+
money33("2500")
|
|
33744
34641
|
]
|
|
33745
34642
|
};
|
|
33746
34643
|
const byStatus = (b) => ({
|
|
@@ -33770,15 +34667,15 @@ var nyRules = [
|
|
|
33770
34667
|
kind: "and",
|
|
33771
34668
|
args: [
|
|
33772
34669
|
{ kind: "cmp", op: "lt", left: base, right: { kind: "param", name: "tableThreshold" } },
|
|
33773
|
-
{ kind: "not", arg:
|
|
34670
|
+
{ kind: "not", arg: fact36("useFormulaMethod") }
|
|
33774
34671
|
]
|
|
33775
34672
|
},
|
|
33776
34673
|
then: {
|
|
33777
34674
|
// printed tax-table bottom row: $0-$13 -> $0 (the generic $50-midpoint
|
|
33778
34675
|
// fiction would otherwise charge $1 on zero income)
|
|
33779
34676
|
kind: "if",
|
|
33780
|
-
cond: { kind: "cmp", op: "lt", left: base, right:
|
|
33781
|
-
then:
|
|
34677
|
+
cond: { kind: "cmp", op: "lt", left: base, right: money33("1300") },
|
|
34678
|
+
then: money33("0"),
|
|
33782
34679
|
else: { kind: "roundToDollar", value: byStatus(mid50), mode: "half-up" }
|
|
33783
34680
|
},
|
|
33784
34681
|
else: { kind: "roundToDollar", value: byStatus(base), mode: "half-up" }
|
|
@@ -33805,7 +34702,7 @@ var nyRules = [
|
|
|
33805
34702
|
// $65,000
|
|
33806
34703
|
},
|
|
33807
34704
|
formula: (() => {
|
|
33808
|
-
const base = { kind: "max0", arg:
|
|
34705
|
+
const base = { kind: "max0", arg: fact36("stateTaxableIncome") };
|
|
33809
34706
|
const bracketsFor = (b, status) => {
|
|
33810
34707
|
const rows = {
|
|
33811
34708
|
single: [
|
|
@@ -33834,10 +34731,10 @@ var nyRules = [
|
|
|
33834
34731
|
args: [
|
|
33835
34732
|
{
|
|
33836
34733
|
kind: "mulInt",
|
|
33837
|
-
base:
|
|
34734
|
+
base: money33("5000"),
|
|
33838
34735
|
count: { kind: "stepUnits", value: base, unitCents: "5000", mode: "floor" }
|
|
33839
34736
|
},
|
|
33840
|
-
|
|
34737
|
+
money33("2500")
|
|
33841
34738
|
]
|
|
33842
34739
|
};
|
|
33843
34740
|
const byStatus = (b) => ({
|
|
@@ -33880,15 +34777,225 @@ var nyRules = [
|
|
|
33880
34777
|
},
|
|
33881
34778
|
formula: {
|
|
33882
34779
|
kind: "mulRate",
|
|
33883
|
-
base: { kind: "max0", arg:
|
|
34780
|
+
base: { kind: "max0", arg: fact36("nyYonkersBase") },
|
|
33884
34781
|
rate: { num: "1675", den: "10000" },
|
|
33885
34782
|
round: "half-up"
|
|
33886
34783
|
}
|
|
33887
34784
|
}
|
|
33888
34785
|
];
|
|
33889
34786
|
|
|
34787
|
+
// ../corpus-us-federal/dist/rules/state-pa.js
|
|
34788
|
+
var max04 = (arg) => ({ kind: "max0", arg });
|
|
34789
|
+
var add2 = (...args) => ({ kind: "add", args });
|
|
34790
|
+
var sub2 = (left, right) => ({ kind: "sub", left, right });
|
|
34791
|
+
var rd2 = (value) => ({ kind: "roundToDollar", value, mode: "half-up" });
|
|
34792
|
+
var isMarried = {
|
|
34793
|
+
kind: "or",
|
|
34794
|
+
args: [
|
|
34795
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "mfj" } },
|
|
34796
|
+
{ kind: "cmp", op: "eq", left: fact36("filingStatus"), right: { kind: "enum", value: "mfs" } }
|
|
34797
|
+
]
|
|
34798
|
+
};
|
|
34799
|
+
var spForgiveness = (netTax, ei, t100) => {
|
|
34800
|
+
let expr = money33("0");
|
|
34801
|
+
for (let k = 9; k >= 0; k--) {
|
|
34802
|
+
expr = {
|
|
34803
|
+
kind: "if",
|
|
34804
|
+
cond: {
|
|
34805
|
+
kind: "cmp",
|
|
34806
|
+
op: "le",
|
|
34807
|
+
left: ei,
|
|
34808
|
+
right: k === 0 ? t100 : add2(t100, money33(String(k * 25e3)))
|
|
34809
|
+
},
|
|
34810
|
+
then: {
|
|
34811
|
+
kind: "mulRate",
|
|
34812
|
+
base: netTax,
|
|
34813
|
+
rate: { num: String(100 - 10 * k), den: "100" },
|
|
34814
|
+
round: "half-up"
|
|
34815
|
+
},
|
|
34816
|
+
else: expr
|
|
34817
|
+
};
|
|
34818
|
+
}
|
|
34819
|
+
return expr;
|
|
34820
|
+
};
|
|
34821
|
+
var taxableIncomeRule = (version2, from, to) => ({
|
|
34822
|
+
id: "us.pa.taxable_income",
|
|
34823
|
+
version: version2,
|
|
34824
|
+
jurisdiction: "us.pa",
|
|
34825
|
+
title: "Pennsylvania total taxable income (PA-40 line 9) \u2014 positive class amounts only",
|
|
34826
|
+
citation: {
|
|
34827
|
+
source: "72 P.S. \xA7 7303(a)(1)-(8); 2025 PA-40 Instructions (rev 04-25) pp. 8-20",
|
|
34828
|
+
section: "72 P.S. \xA7 7303; PA-40 lines 1a-9",
|
|
34829
|
+
url: "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2025/2025_pa-40in.pdf",
|
|
34830
|
+
excerpt: "Line 9 (2025 PA-40, verbatim): 'Add only the positive income amounts from Lines 1c, 2, 3, 4, 5, 6, 7 and 8. DO NOT ADD any losses.' Eight classes per \xA7 7303(a): compensation; interest; dividends (mutual-fund capital gains DISTRIBUTIONS are dividends, Line 3); net business/profession/farm; net gains from disposition of property; net rents/royalties/patents/copyrights; estate or trust income (cannot be negative); gambling and lottery winnings (noncash PA Lottery prizes exempt; cash prizes taxable, ticket costs for tickets bought on/after 1/1/2016 deductible from winnings). NO cross-class offset, NO carryforward/carryback, and NO spousal sharing: on a joint return, per class, one spouse's loss NEVER offsets the other's income (both-positive add; one-income-one-loss counts ONLY the income) \u2014 encoded as max0(taxpayer net) + max0(spouse net) for each loss-capable class (lines 4/5/6; PA-40 IN p.15, Mary & Ben examples). COMPENSATION: PA compensation is W-2 BOX 16, not Box 1 \u2014 401(k)/elective deferrals ARE taxable compensation; IRA contributions are NOT deductible; supply the Box16-Box1 difference via paCompensationAdjustment (default 0 assumes Box 16 = Box 1). Retirement income is NOT compensation when paid by an eligible employer plan after meeting the plan's age/years-of-service conditions AND retiring (code 7), and Social Security / RR / military pensions / Civil Service annuities / SERS / PSERS are always exempt; early distributions (codes 1-2) and pre-59\xBD IRA distributions are taxable compensation under the COST RECOVERY method (distribution minus previously-taxed contributions; PA has NO federal-style early-withdrawal exceptions) \u2014 cost-recovery basis math is agent-composed into the class facts, disclosed. Unemployment compensation, workers' comp, third-party sick pay, child support, alimony, and W-2 box-10 dependent care up to $5,000 are not taxable. APPROXIMATIONS (disclosed): compensation and its unreimbursed-expense offset (Sch UE, line 1b) are modeled at the return level, not per spouse \u2014 a spouse whose UE exceeds own compensation on a joint return would be overstated (rare; compose per-spouse and supply via paCompensationAdjustment if it matters). Reciprocal compensation states (IN, MD, NJ, OH, VA, WV) and part-year/nonresident apportionment are out of scope for this resident-return target."
|
|
34831
|
+
},
|
|
34832
|
+
effectiveFrom: from,
|
|
34833
|
+
effectiveTo: to,
|
|
34834
|
+
output: { type: "money" },
|
|
34835
|
+
formula: add2(
|
|
34836
|
+
// line 1c: net compensation (never negative on the printed form)
|
|
34837
|
+
max04(sub2(add2(fact36("wages"), fact36("paCompensationAdjustment")), fact36("paUnreimbursedBusinessExpenses"))),
|
|
34838
|
+
fact36("paInterestIncome"),
|
|
34839
|
+
// line 2 (gross class)
|
|
34840
|
+
fact36("paDividendIncome"),
|
|
34841
|
+
// line 3 (gross class)
|
|
34842
|
+
max04(fact36("paBusinessNetIncome")),
|
|
34843
|
+
// line 4, taxpayer
|
|
34844
|
+
max04(fact36("paSpouseBusinessNetIncome")),
|
|
34845
|
+
// line 4, spouse
|
|
34846
|
+
max04(fact36("paPropertyGainNet")),
|
|
34847
|
+
// line 5, taxpayer
|
|
34848
|
+
max04(fact36("paSpousePropertyGainNet")),
|
|
34849
|
+
// line 5, spouse
|
|
34850
|
+
max04(fact36("paRentRoyaltyNet")),
|
|
34851
|
+
// line 6, taxpayer
|
|
34852
|
+
max04(fact36("paSpouseRentRoyaltyNet")),
|
|
34853
|
+
// line 6, spouse
|
|
34854
|
+
fact36("paEstateTrustIncome"),
|
|
34855
|
+
// line 7 (cannot be negative)
|
|
34856
|
+
fact36("paGamblingWinnings")
|
|
34857
|
+
)
|
|
34858
|
+
});
|
|
34859
|
+
var otherDeductionsRule = (version2, from, to) => ({
|
|
34860
|
+
id: "us.pa.other_deductions",
|
|
34861
|
+
version: version2,
|
|
34862
|
+
jurisdiction: "us.pa",
|
|
34863
|
+
title: "Pennsylvania other deductions (PA-40 line 10, Schedule O) \u2014 the exhaustive five",
|
|
34864
|
+
citation: {
|
|
34865
|
+
source: "2025 PA-40 Instructions (rev 04-25) pp. 9, 20-21; 2025 PA Schedule O + instructions",
|
|
34866
|
+
section: "PA-40 line 10; Schedule O",
|
|
34867
|
+
url: "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2025/2025_pa-40o.pdf",
|
|
34868
|
+
excerpt: "'PA law does not allow standard deductions, deductions for personal exemptions, itemized deductions, or deductions for personal expenses' (PA-40 IN p.9). Exactly FIVE line-10 deductions exist for 2025: (M) Medical Savings Account contributions and (H) Health Savings Account contributions, each limited to the amount 'allowed for federal income tax purposes'; (S) STUDENT LOAN INTEREST \u2014 NEW for 2025 \u2014 up to $2,500 per taxable year (no PA income phase-out; the federal studentLoanInterest fact is reused as the interest-paid input and capped here); (T) IRC \xA7 529 qualified tuition program contributions \u2014 maximum $19,000 PER BENEFICIARY, PER TAXPAYER-SPOUSE (2025; no deduction for 529-to-529 rollovers or beneficiary changes); (A) \xA7 529A PA ABLE contributions capped at the annual federal gift-tax exclusion ($19,000 for 2025). The per-beneficiary 529 cap and the ABLE cap CANNOT be enforced without per-beneficiary detail \u2014 the caller must supply already-capped totals (disclosed). Line 10 cannot exceed line 9; the Schedule O Section III PER-SPOUSE cap (each spouse limited to own line-9 income) is approximated at the return level (disclosed). Unreimbursed employee business expenses are a line-1b class expense (Schedule UE), never a line-10 deduction. The 2026 federal gift-exclusion amount for the ABLE cap must be re-verified at 2026 encoding."
|
|
34869
|
+
},
|
|
34870
|
+
effectiveFrom: from,
|
|
34871
|
+
effectiveTo: to,
|
|
34872
|
+
output: { type: "money" },
|
|
34873
|
+
parameters: {
|
|
34874
|
+
studentLoanInterestCap: { value: "250000", type: "money" },
|
|
34875
|
+
// $2,500 (2025 PA cap)
|
|
34876
|
+
per529BeneficiaryCap: { value: "1900000", type: "money" },
|
|
34877
|
+
// $19,000 (disclosure)
|
|
34878
|
+
ableGiftExclusionCap: { value: "1900000", type: "money" }
|
|
34879
|
+
// $19,000 (2025)
|
|
34880
|
+
},
|
|
34881
|
+
formula: {
|
|
34882
|
+
kind: "min",
|
|
34883
|
+
args: [
|
|
34884
|
+
add2({ kind: "min", args: [fact36("studentLoanInterest"), { kind: "param", name: "studentLoanInterestCap" }] }, fact36("pa529Contributions"), fact36("paAbleContributions"), fact36("paMsaHsaContributions")),
|
|
34885
|
+
ruleRef30("us.pa.taxable_income")
|
|
34886
|
+
]
|
|
34887
|
+
}
|
|
34888
|
+
});
|
|
34889
|
+
var incomeTaxRule2 = (version2, from, to, yearLabel) => ({
|
|
34890
|
+
id: "us.pa.income_tax",
|
|
34891
|
+
version: version2,
|
|
34892
|
+
jurisdiction: "us.pa",
|
|
34893
|
+
title: `Pennsylvania income tax \u2014 3.07% flat on adjusted PA taxable income (PA-40 line 12, ${yearLabel})`,
|
|
34894
|
+
citation: {
|
|
34895
|
+
source: "72 P.S. \xA7 7302 (current as of Jan 1, 2026); 2025 PA-40 line 12",
|
|
34896
|
+
section: "72 P.S. \xA7 7302",
|
|
34897
|
+
url: "https://codes.findlaw.com/pa/title-72-ps-taxation-and-fiscal-affairs/pa-st-sect-72-7302/",
|
|
34898
|
+
excerpt: "\xA7 7302 imposes 'three and seven hundredths per cent' (3.07%) on residents' taxable income; PA-40 line 12: 'Multiply Line 11 by 3.07 percent (0.0307)', where line 11 = line 9 total PA taxable income minus line 10 other deductions. Rate verified unchanged for TY2026 (statute text current as of Jan 1, 2026; the Senate's proposed 2.8% cut was NOT enacted). This target computes GROSS tax (line 12); Tax Forgiveness (Schedule SP) is the separate us.pa.tax_forgiveness target, and the resident credit / WPTC / PA CDCC net against tax in the return composition. Local earned income tax (Act 32) is filed separately with local collectors and is out of scope. Rounding: PA-40 uses whole dollars (drop <$0.50, round up \u2265$0.50 = half-up). Filing threshold: PA gross taxable income over $33 (or any loss transaction) requires a return."
|
|
34899
|
+
},
|
|
34900
|
+
effectiveFrom: from,
|
|
34901
|
+
effectiveTo: to,
|
|
34902
|
+
output: { type: "money" },
|
|
34903
|
+
parameters: {
|
|
34904
|
+
filingThreshold: { value: "3300", type: "money" }
|
|
34905
|
+
// $33 gross-income filing trigger
|
|
34906
|
+
},
|
|
34907
|
+
formula: rd2({
|
|
34908
|
+
kind: "mulRate",
|
|
34909
|
+
base: max04(sub2(ruleRef30("us.pa.taxable_income"), ruleRef30("us.pa.other_deductions"))),
|
|
34910
|
+
rate: { num: "307", den: "10000" },
|
|
34911
|
+
round: "half-up"
|
|
34912
|
+
})
|
|
34913
|
+
});
|
|
34914
|
+
var paRules = [
|
|
34915
|
+
taxableIncomeRule(1, "2025-01-01", "2027-01-01"),
|
|
34916
|
+
otherDeductionsRule(1, "2025-01-01", "2027-01-01"),
|
|
34917
|
+
incomeTaxRule2(2, "2025-01-01", "2026-01-01", "TY2025"),
|
|
34918
|
+
incomeTaxRule2(3, "2026-01-01", "2027-01-01", "TY2026 \u2014 rate unchanged"),
|
|
34919
|
+
{
|
|
34920
|
+
id: "us.pa.tax_forgiveness",
|
|
34921
|
+
version: 1,
|
|
34922
|
+
jurisdiction: "us.pa",
|
|
34923
|
+
title: "Pennsylvania Tax Forgiveness credit (Schedule SP, 72 P.S. \xA7 7304) \u2014 PA-40 line 21",
|
|
34924
|
+
citation: {
|
|
34925
|
+
source: "72 P.S. \xA7 7304; 2025 PA Schedule SP + instructions (PA-40 SP IN 04-25); 2025 PA-40 Instructions p. 36-39 (eligibility income tables)",
|
|
34926
|
+
section: "Schedule SP Sections III-IV; PA-40 lines 19a-21",
|
|
34927
|
+
url: "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2025/2025_pa-40sp.pdf",
|
|
34928
|
+
excerpt: "Printed 2025 eligibility-income tables (statutory, NOT indexed \u2014 structure verified against every printed cell): unmarried/separated/deceased base $6,500 at 100% forgiveness; MARRIED (even filing separately, using JOINT eligibility income) base $13,000; PLUS $9,500 per dependent child on every column; each additional $250 of eligibility income drops the forgiveness percentage 10 points (100/90/.../10%), so income beyond base+dependents+$2,250 gets nothing. Forgiveness applies to line 12 tax MINUS the line 22 resident credit (SP Section IV: the resident credit subtracts BEFORE forgiveness), never below zero. ELIGIBILITY INCOME = PA-40 line 9 taxable income PLUS nontaxable add-backs supplied via paEligibilityIncomeAddbacks: nontaxable interest/dividends/gains, alimony received, insurance proceeds and inheritances (incl. 1099-R code-4 box 1), gifts/awards/prizes (incl. noncash PA Lottery), non-PA income, nontaxable military pay (not combat), excluded home-sale gain, nontaxable educational assistance, and outside cash support (foster payments, spousal support from outside the household, cafeteria-plan benefits). NOT added back: Social Security/RRB, eligible retirement benefits, child support, military pensions, workers' comp, personal-injury damages, sick/disability pay. DEPENDENT = a CHILD (natural/adopted/step; grandchild of a grandparent; foster child of a foster parent \u2014 never aunts/uncles/unrelated persons) claimable as a federal dependent; adult qualifying children count. A claimant who is someone else's federal dependent is ineligible unless that person also qualifies (not modeled \u2014 disclose). 'Separated' (apart the last six months of the year or under written separation agreement) uses the unmarried table \u2014 supply via filingStatus; QSS/widowed is NOT married (Table 1); MFS IS married (Table 2, joint eligibility income \u2014 return-level approximation disclosed). Deceased-claimant annualization not modeled (refuse-by-disclosure). Defaults are CONSERVATIVE: paSpDependentChildren defaults to 0 (understates the threshold, never overstates the credit)."
|
|
34929
|
+
},
|
|
34930
|
+
effectiveFrom: "2025-01-01",
|
|
34931
|
+
effectiveTo: "2027-01-01",
|
|
34932
|
+
output: { type: "money" },
|
|
34933
|
+
parameters: {
|
|
34934
|
+
baseUnmarried: { value: "650000", type: "money" },
|
|
34935
|
+
// $6,500
|
|
34936
|
+
baseMarried: { value: "1300000", type: "money" },
|
|
34937
|
+
// $13,000
|
|
34938
|
+
perDependentChild: { value: "950000", type: "money" },
|
|
34939
|
+
// $9,500
|
|
34940
|
+
stepWidth: { value: "25000", type: "money" }
|
|
34941
|
+
// $250 per 10-point drop
|
|
34942
|
+
},
|
|
34943
|
+
formula: rd2(spForgiveness(
|
|
34944
|
+
// net tax: line 12 minus resident credit, floored at zero
|
|
34945
|
+
max04(sub2(ruleRef30("us.pa.income_tax"), fact36("paResidentCredit"))),
|
|
34946
|
+
// eligibility income: line 9 + add-backs
|
|
34947
|
+
add2(ruleRef30("us.pa.taxable_income"), fact36("paEligibilityIncomeAddbacks")),
|
|
34948
|
+
// 100% threshold: base + $9,500 x dependent children
|
|
34949
|
+
add2({ kind: "if", cond: isMarried, then: { kind: "param", name: "baseMarried" }, else: { kind: "param", name: "baseUnmarried" } }, { kind: "mulInt", base: { kind: "param", name: "perDependentChild" }, count: fact36("paSpDependentChildren") })
|
|
34950
|
+
))
|
|
34951
|
+
},
|
|
34952
|
+
{
|
|
34953
|
+
id: "us.pa.cdcc",
|
|
34954
|
+
version: 1,
|
|
34955
|
+
jurisdiction: "us.pa",
|
|
34956
|
+
title: "Pennsylvania Child and Dependent Care Enhancement Tax Credit (Schedule DC) \u2014 100% of the federal \xA7 21 credit, refundable (TY2025)",
|
|
34957
|
+
citation: {
|
|
34958
|
+
source: "2025 PA Schedule DC + instructions (PA-40 DC IN 04-25); 2025 PA-40 Instructions pp. 22-23",
|
|
34959
|
+
section: "Schedule DC; PA-40 line 23",
|
|
34960
|
+
url: "https://www.pa.gov/content/dam/copapwp-pagov/en/revenue/documents/formsandpublications/formsforindividuals/pit/documents/2025/2025_pa-40dc.pdf",
|
|
34961
|
+
excerpt: "2025 Schedule DC: for PA residents receiving the federal IRC \xA7 21 credit, 'the credit is 100 percent of that amount' \u2014 the federal Form 2441 LINE 9a tentative credit (expenses capped $3,000 one / $6,000 two-or-more qualifying persons at the 35%\u219220% AGI slide), i.e. the PRE-liability-limit federal amount, because the PA credit is REFUNDABLE. Encoded as 100% of us.federal.cdcc.tentative (which already carries the \xA7 21 expense caps, earned-income limits, AGI percentage slide, and the MFS disallowance). The DC chart's printed caps ($1,050/$600 one dependent, $2,100/$1,200 two-plus at the $43,000 household-income break) are the federal formula's own outputs, not separate limits. Federal Form 2441 + Schedule 3 must be attached or DOR removes the credit (procedural, not modeled). TY2025 window only: the TY2026 federal \xA7 21 percentages change under OBBBA \xA7 70405 and PA's 100%-match treatment for 2026 was not yet verified at encoding \u2014 extend only after re-verification (refuses loudly for 2026 rather than guessing)."
|
|
34962
|
+
},
|
|
34963
|
+
effectiveFrom: "2025-01-01",
|
|
34964
|
+
effectiveTo: "2026-01-01",
|
|
34965
|
+
output: { type: "money" },
|
|
34966
|
+
formula: rd2(ruleRef30("us.federal.cdcc.tentative"))
|
|
34967
|
+
},
|
|
34968
|
+
{
|
|
34969
|
+
id: "us.pa.wptc",
|
|
34970
|
+
version: 1,
|
|
34971
|
+
jurisdiction: "us.pa",
|
|
34972
|
+
title: "Working Pennsylvanians Tax Credit \u2014 10% of the federal EITC, max $805, refundable (first PA state EITC, TY2025+)",
|
|
34973
|
+
citation: {
|
|
34974
|
+
source: "2025-26 Pennsylvania budget (signed Nov 12, 2025); PA DOR Working Pennsylvanians Tax Credit page; PA DOR press release Jan 27, 2026 (web-verified 2026-07-24)",
|
|
34975
|
+
section: "PA DOR WPTC guidance",
|
|
34976
|
+
url: "https://www.pa.gov/agencies/revenue/resources/tax-types-and-information/personal-income-tax/working-pennsylvanians-tax-credit",
|
|
34977
|
+
excerpt: "DOR: 'If you receive the federal EITC, you automatically qualify' \u2014 the credit is 10 percent of the federal Earned Income Tax Credit, maximum $805, REFUNDABLE, and 'the Department of Revenue will automatically calculate your WPTC' from the attached federal 1040. Effective beginning with the 2026 filing season (TY2025 returns, per the DOR's Jan 27, 2026 press release promoting the credit for the then-current season). NOTE: the printed 2025 PA-40 (rev 04-25, which predates the November 2025 budget) has NO WPTC line \u2014 DOR applies it in processing; composers should report it as a DOR-applied credit note. CAVEAT (disclosed): the statutory text's effective-tax-year language should be re-verified against the enrolled act when published; this encoding follows the agency's own filing-season guidance. The $805 cap can bind only via rounding in TY2025 (max federal EITC $8,046 \u2192 10% = $804.60)."
|
|
34978
|
+
},
|
|
34979
|
+
effectiveFrom: "2025-01-01",
|
|
34980
|
+
effectiveTo: "2027-01-01",
|
|
34981
|
+
output: { type: "money" },
|
|
34982
|
+
parameters: {
|
|
34983
|
+
pctOfFederal: { value: "10", type: "int" },
|
|
34984
|
+
cap: { value: "80500", type: "money" }
|
|
34985
|
+
// $805
|
|
34986
|
+
},
|
|
34987
|
+
formula: rd2({
|
|
34988
|
+
kind: "min",
|
|
34989
|
+
args: [
|
|
34990
|
+
{ kind: "mulRate", base: ruleRef30("us.federal.eitc"), rate: { num: "10", den: "100" }, round: "half-up" },
|
|
34991
|
+
{ kind: "param", name: "cap" }
|
|
34992
|
+
]
|
|
34993
|
+
})
|
|
34994
|
+
}
|
|
34995
|
+
];
|
|
34996
|
+
|
|
33890
34997
|
// ../corpus-us-federal/dist/rules/state-other.js
|
|
33891
|
-
var flatBase = { kind: "max0", arg:
|
|
34998
|
+
var flatBase = { kind: "max0", arg: fact36("stateTaxableIncome") };
|
|
33892
34999
|
function flatTax(args) {
|
|
33893
35000
|
return {
|
|
33894
35001
|
id: `us.${args.st}.income_tax`,
|
|
@@ -33969,7 +35076,7 @@ var otherStateRules = [
|
|
|
33969
35076
|
output: { type: "money" },
|
|
33970
35077
|
formula: {
|
|
33971
35078
|
kind: "if",
|
|
33972
|
-
cond: { kind: "cmp", op: "gt", left:
|
|
35079
|
+
cond: { kind: "cmp", op: "gt", left: fact36("longTermCapitalGains"), right: { kind: "money", cents: "0" } },
|
|
33973
35080
|
then: {
|
|
33974
35081
|
kind: "unsupported",
|
|
33975
35082
|
reason: "Washington taxes no wages, but long-term capital gains are present \u2014 the RCW 82.87 7% capital-gains excise (indexed ~$270k deduction, real-estate/retirement exemptions) is not modeled; compute it separately and disclose"
|
|
@@ -33978,15 +35085,8 @@ var otherStateRules = [
|
|
|
33978
35085
|
}
|
|
33979
35086
|
},
|
|
33980
35087
|
// ---- flat-rate states (TY2025, web-verified) ---------------------------
|
|
33981
|
-
|
|
33982
|
-
|
|
33983
|
-
name: "Pennsylvania",
|
|
33984
|
-
ratePctTimes100: "307",
|
|
33985
|
-
source: "72 Pa. Stat. \xA7 7302; Tax Foundation 2025 state rate survey (web-verified July 2026)",
|
|
33986
|
-
section: "72 P.S. \xA7 7302",
|
|
33987
|
-
url: "https://www.pa.gov/agencies/revenue.html",
|
|
33988
|
-
excerpt: "Pennsylvania: 3.07% flat on PA taxable income. NO standard deduction and NO personal exemption. CAUTION: PA taxes EIGHT SEPARATE INCOME CLASSES (compensation, interest, dividends, net profits, gains, rents, estates, gambling) with NO netting of a loss in one class against income in another, and retirement income (401(k)/IRA distributions after retirement age, Social Security) is fully EXEMPT. PA Tax Forgiveness (Schedule SP) can eliminate tax at low incomes \u2014 not modeled, disclose. Compose stateTaxableIncome per the class rules before calling."
|
|
33989
|
-
}),
|
|
35088
|
+
// (PA graduated to the deep pack: see state-pa.ts — us.pa.income_tax v2+
|
|
35089
|
+
// computes the class-netted base itself; the thin v1 is superseded.)
|
|
33990
35090
|
flatTax({
|
|
33991
35091
|
st: "in",
|
|
33992
35092
|
name: "Indiana",
|
|
@@ -34175,19 +35275,20 @@ var stateParameterRules = [
|
|
|
34175
35275
|
...vaRules,
|
|
34176
35276
|
...caRules,
|
|
34177
35277
|
...nyRules,
|
|
35278
|
+
...paRules,
|
|
34178
35279
|
...otherStateRules
|
|
34179
35280
|
];
|
|
34180
35281
|
|
|
34181
35282
|
// ../corpus-us-federal/dist/rules/qbi.js
|
|
34182
|
-
var
|
|
34183
|
-
var
|
|
34184
|
-
var
|
|
35283
|
+
var fact37 = (factId) => ({ kind: "fact", factId });
|
|
35284
|
+
var money34 = (cents) => ({ kind: "money", cents });
|
|
35285
|
+
var ruleRef31 = (ruleId) => ({ kind: "rule", ruleId });
|
|
34185
35286
|
var param17 = (name) => ({ kind: "param", name });
|
|
34186
|
-
var
|
|
35287
|
+
var zero24 = money34("0");
|
|
34187
35288
|
var isStatus11 = (status) => ({
|
|
34188
35289
|
kind: "cmp",
|
|
34189
35290
|
op: "eq",
|
|
34190
|
-
left:
|
|
35291
|
+
left: fact37("filingStatus"),
|
|
34191
35292
|
right: { kind: "enum", value: status }
|
|
34192
35293
|
});
|
|
34193
35294
|
var pctOf2 = (num, den, base) => ({
|
|
@@ -34207,31 +35308,31 @@ var qbi = {
|
|
|
34207
35308
|
kind: "max0",
|
|
34208
35309
|
arg: {
|
|
34209
35310
|
kind: "sub",
|
|
34210
|
-
left:
|
|
35311
|
+
left: fact37("selfEmploymentNetProfit"),
|
|
34211
35312
|
right: {
|
|
34212
35313
|
kind: "add",
|
|
34213
35314
|
args: [
|
|
34214
|
-
|
|
34215
|
-
|
|
34216
|
-
|
|
35315
|
+
ruleRef31("us.federal.se_tax_half_deduction"),
|
|
35316
|
+
ruleRef31("us.federal.sep_deduction"),
|
|
35317
|
+
ruleRef31("us.federal.sehi_deduction")
|
|
34217
35318
|
]
|
|
34218
35319
|
}
|
|
34219
35320
|
}
|
|
34220
35321
|
},
|
|
34221
|
-
|
|
35322
|
+
fact37("k1OrdinaryBusinessIncome")
|
|
34222
35323
|
]
|
|
34223
35324
|
},
|
|
34224
|
-
right:
|
|
35325
|
+
right: fact37("qbiLossOffset")
|
|
34225
35326
|
}
|
|
34226
35327
|
};
|
|
34227
35328
|
var hasBusinessIncome = {
|
|
34228
35329
|
kind: "or",
|
|
34229
35330
|
args: [
|
|
34230
|
-
{ kind: "cmp", op: "gt", left:
|
|
34231
|
-
{ kind: "cmp", op: "gt", left:
|
|
35331
|
+
{ kind: "cmp", op: "gt", left: fact37("selfEmploymentNetProfit"), right: zero24 },
|
|
35332
|
+
{ kind: "cmp", op: "gt", left: fact37("k1OrdinaryBusinessIncome"), right: zero24 }
|
|
34232
35333
|
]
|
|
34233
35334
|
};
|
|
34234
|
-
var taxableBeforeQbi =
|
|
35335
|
+
var taxableBeforeQbi = ruleRef31("us.federal.taxable_income_before_qbi");
|
|
34235
35336
|
var wageLimitOf = (w2, ubia) => ({
|
|
34236
35337
|
kind: "max",
|
|
34237
35338
|
args: [
|
|
@@ -34266,8 +35367,8 @@ function qbiRule(version2, effectiveFrom, effectiveTo, yearLabel, threshold2, ba
|
|
|
34266
35367
|
};
|
|
34267
35368
|
const excessCapped = { kind: "min", args: [excess, band] };
|
|
34268
35369
|
const tentative = pctOf2("20", "100", qbi);
|
|
34269
|
-
const w2 =
|
|
34270
|
-
const ubia =
|
|
35370
|
+
const w2 = fact37("qbiW2Wages");
|
|
35371
|
+
const ubia = fact37("qbiUBIA");
|
|
34271
35372
|
const nonSstb = {
|
|
34272
35373
|
kind: "if",
|
|
34273
35374
|
cond: { kind: "cmp", op: "ge", left: excess, right: band },
|
|
@@ -34285,22 +35386,25 @@ function qbiRule(version2, effectiveFrom, effectiveTo, yearLabel, threshold2, ba
|
|
|
34285
35386
|
const sstb = {
|
|
34286
35387
|
kind: "if",
|
|
34287
35388
|
cond: { kind: "cmp", op: "ge", left: excess, right: band },
|
|
34288
|
-
then:
|
|
35389
|
+
then: zero24,
|
|
34289
35390
|
else: phasedReduction(pctOf2("20", "100", scale(qbi)), wageLimitOf(scale(w2), scale(ubia)), excessCapped, band)
|
|
34290
35391
|
};
|
|
34291
35392
|
const businessPart = {
|
|
34292
35393
|
kind: "if",
|
|
34293
|
-
cond: { kind: "cmp", op: "eq", left: excess, right:
|
|
35394
|
+
cond: { kind: "cmp", op: "eq", left: excess, right: zero24 },
|
|
34294
35395
|
then: tentative,
|
|
34295
35396
|
// at or below the threshold: no SSTB question, no limits
|
|
34296
|
-
else: { kind: "if", cond:
|
|
35397
|
+
else: { kind: "if", cond: fact37("businessIsSSTB"), then: sstb, else: nonSstb }
|
|
34297
35398
|
};
|
|
34298
35399
|
const overall = pctOf2("20", "100", {
|
|
34299
35400
|
kind: "max0",
|
|
34300
35401
|
arg: {
|
|
34301
35402
|
kind: "sub",
|
|
34302
35403
|
left: taxableBeforeQbi,
|
|
34303
|
-
right: {
|
|
35404
|
+
right: {
|
|
35405
|
+
kind: "add",
|
|
35406
|
+
args: [ruleRef31("us.federal.schedule_d.preferential_lt_gain"), fact37("qualifiedDividends")]
|
|
35407
|
+
}
|
|
34304
35408
|
}
|
|
34305
35409
|
});
|
|
34306
35410
|
const limited = { kind: "min", args: [businessPart, overall] };
|
|
@@ -34339,23 +35443,23 @@ function qbiRule(version2, effectiveFrom, effectiveTo, yearLabel, threshold2, ba
|
|
|
34339
35443
|
kind: "if",
|
|
34340
35444
|
cond: hasBusinessIncome,
|
|
34341
35445
|
then: withFloor,
|
|
34342
|
-
else:
|
|
35446
|
+
else: zero24
|
|
34343
35447
|
}
|
|
34344
35448
|
};
|
|
34345
35449
|
}
|
|
34346
35450
|
var qbiRules = [
|
|
34347
35451
|
qbiRule(
|
|
34348
|
-
|
|
34349
|
-
//
|
|
35452
|
+
9,
|
|
35453
|
+
// v7 counted qualifiedDividends in the § 199A(a)(2)/(e)(3) overall limit; v9 takes the schedule_d netted LT gain there
|
|
34350
35454
|
"2025-01-01",
|
|
34351
35455
|
"2026-01-01",
|
|
34352
35456
|
"2025",
|
|
34353
35457
|
{
|
|
34354
35458
|
kind: "if",
|
|
34355
35459
|
cond: isStatus11("mfj"),
|
|
34356
|
-
then:
|
|
35460
|
+
then: money34("39460000"),
|
|
34357
35461
|
// $394,600
|
|
34358
|
-
else:
|
|
35462
|
+
else: money34("19730000")
|
|
34359
35463
|
// $197,300 (incl. MFS)
|
|
34360
35464
|
},
|
|
34361
35465
|
"5000000",
|
|
@@ -34365,23 +35469,23 @@ var qbiRules = [
|
|
|
34365
35469
|
false
|
|
34366
35470
|
),
|
|
34367
35471
|
qbiRule(
|
|
34368
|
-
|
|
34369
|
-
//
|
|
35472
|
+
10,
|
|
35473
|
+
// v8 counted qualifiedDividends in the overall limit; v10 takes the schedule_d netted LT gain there
|
|
34370
35474
|
"2026-01-01",
|
|
34371
35475
|
"2027-01-01",
|
|
34372
35476
|
"2026",
|
|
34373
35477
|
{
|
|
34374
35478
|
kind: "match",
|
|
34375
|
-
on:
|
|
35479
|
+
on: fact37("filingStatus"),
|
|
34376
35480
|
cases: [
|
|
34377
|
-
{ when: "mfj", value:
|
|
35481
|
+
{ when: "mfj", value: money34("40350000") },
|
|
34378
35482
|
// $403,500
|
|
34379
|
-
{ when: "mfs", value:
|
|
35483
|
+
{ when: "mfs", value: money34("20177500") },
|
|
34380
35484
|
// $201,775
|
|
34381
|
-
{ when: "single", value:
|
|
35485
|
+
{ when: "single", value: money34("20175000") },
|
|
34382
35486
|
// $201,750
|
|
34383
|
-
{ when: "hoh", value:
|
|
34384
|
-
{ when: "qss", value:
|
|
35487
|
+
{ when: "hoh", value: money34("20175000") },
|
|
35488
|
+
{ when: "qss", value: money34("20175000") }
|
|
34385
35489
|
]
|
|
34386
35490
|
},
|
|
34387
35491
|
"7500000",
|
|
@@ -34393,12 +35497,12 @@ var qbiRules = [
|
|
|
34393
35497
|
];
|
|
34394
35498
|
|
|
34395
35499
|
// ../corpus-us-federal/dist/rules/self-employment.js
|
|
34396
|
-
var
|
|
34397
|
-
var
|
|
34398
|
-
var
|
|
35500
|
+
var fact38 = (factId) => ({ kind: "fact", factId });
|
|
35501
|
+
var money35 = (cents) => ({ kind: "money", cents });
|
|
35502
|
+
var ruleRef32 = (ruleId) => ({ kind: "rule", ruleId });
|
|
34399
35503
|
var param18 = (name) => ({ kind: "param", name });
|
|
34400
|
-
var
|
|
34401
|
-
var seNetEarnings =
|
|
35504
|
+
var zero25 = money35("0");
|
|
35505
|
+
var seNetEarnings = ruleRef32("us.federal.se_net_earnings");
|
|
34402
35506
|
function seTaxRule(version2, effectiveFrom, effectiveTo, wageBaseCents, yearLabel) {
|
|
34403
35507
|
return {
|
|
34404
35508
|
id: "us.federal.se_tax",
|
|
@@ -34422,10 +35526,10 @@ function seTaxRule(version2, effectiveFrom, effectiveTo, wageBaseCents, yearLabe
|
|
|
34422
35526
|
formula: {
|
|
34423
35527
|
kind: "if",
|
|
34424
35528
|
cond: { kind: "cmp", op: "lt", left: seNetEarnings, right: param18("seFloor") },
|
|
34425
|
-
then:
|
|
35529
|
+
then: zero25,
|
|
34426
35530
|
else: {
|
|
34427
35531
|
kind: "if",
|
|
34428
|
-
cond:
|
|
35532
|
+
cond: fact38("useFormulaMethod"),
|
|
34429
35533
|
then: { kind: "add", args: [oasdiComponent, medicareComponent] },
|
|
34430
35534
|
else: {
|
|
34431
35535
|
// printed Schedule SE: lines 10 and 11 each entered in whole dollars
|
|
@@ -34441,9 +35545,9 @@ function seTaxRule(version2, effectiveFrom, effectiveTo, wageBaseCents, yearLabe
|
|
|
34441
35545
|
}
|
|
34442
35546
|
var wagesForBase = {
|
|
34443
35547
|
kind: "if",
|
|
34444
|
-
cond: { kind: "cmp", op: "gt", left:
|
|
34445
|
-
then:
|
|
34446
|
-
else:
|
|
35548
|
+
cond: { kind: "cmp", op: "gt", left: fact38("socialSecurityWages"), right: zero25 },
|
|
35549
|
+
then: fact38("socialSecurityWages"),
|
|
35550
|
+
else: fact38("wages")
|
|
34447
35551
|
};
|
|
34448
35552
|
var oasdiComponent = {
|
|
34449
35553
|
kind: "mulRate",
|
|
@@ -34487,7 +35591,7 @@ var selfEmploymentRules = [
|
|
|
34487
35591
|
output: { type: "money" },
|
|
34488
35592
|
formula: {
|
|
34489
35593
|
kind: "mulRate",
|
|
34490
|
-
base:
|
|
35594
|
+
base: fact38("selfEmploymentNetProfit"),
|
|
34491
35595
|
rate: { num: "9235", den: "10000" },
|
|
34492
35596
|
round: "half-up"
|
|
34493
35597
|
}
|
|
@@ -34514,10 +35618,10 @@ var selfEmploymentRules = [
|
|
|
34514
35618
|
output: { type: "money" },
|
|
34515
35619
|
formula: {
|
|
34516
35620
|
kind: "if",
|
|
34517
|
-
cond:
|
|
35621
|
+
cond: fact38("useFormulaMethod"),
|
|
34518
35622
|
then: {
|
|
34519
35623
|
kind: "mulRate",
|
|
34520
|
-
base:
|
|
35624
|
+
base: ruleRef32("us.federal.se_tax"),
|
|
34521
35625
|
rate: { num: "50", den: "100" },
|
|
34522
35626
|
round: "half-up"
|
|
34523
35627
|
},
|
|
@@ -34525,7 +35629,7 @@ var selfEmploymentRules = [
|
|
|
34525
35629
|
kind: "roundToDollar",
|
|
34526
35630
|
value: {
|
|
34527
35631
|
kind: "mulRate",
|
|
34528
|
-
base:
|
|
35632
|
+
base: ruleRef32("us.federal.se_tax"),
|
|
34529
35633
|
rate: { num: "50", den: "100" },
|
|
34530
35634
|
round: "half-up"
|
|
34531
35635
|
},
|
|
@@ -34536,17 +35640,17 @@ var selfEmploymentRules = [
|
|
|
34536
35640
|
];
|
|
34537
35641
|
|
|
34538
35642
|
// ../corpus-us-federal/dist/rules/senior-deduction.js
|
|
34539
|
-
var
|
|
34540
|
-
var
|
|
34541
|
-
var
|
|
35643
|
+
var fact39 = (factId) => ({ kind: "fact", factId });
|
|
35644
|
+
var money36 = (cents) => ({ kind: "money", cents });
|
|
35645
|
+
var ruleRef33 = (ruleId) => ({ kind: "rule", ruleId });
|
|
34542
35646
|
var param19 = (name) => ({ kind: "param", name });
|
|
34543
35647
|
var isStatus12 = (status) => ({
|
|
34544
35648
|
kind: "cmp",
|
|
34545
35649
|
op: "eq",
|
|
34546
|
-
left:
|
|
35650
|
+
left: fact39("filingStatus"),
|
|
34547
35651
|
right: { kind: "enum", value: status }
|
|
34548
35652
|
});
|
|
34549
|
-
var
|
|
35653
|
+
var zero26 = money36("0");
|
|
34550
35654
|
function perSeniorNet() {
|
|
34551
35655
|
return {
|
|
34552
35656
|
kind: "max0",
|
|
@@ -34559,8 +35663,8 @@ function perSeniorNet() {
|
|
|
34559
35663
|
kind: "max0",
|
|
34560
35664
|
arg: {
|
|
34561
35665
|
kind: "sub",
|
|
34562
|
-
left:
|
|
34563
|
-
right:
|
|
35666
|
+
left: ruleRef33("us.federal.agi"),
|
|
35667
|
+
right: ruleRef33("us.federal.senior_deduction.magi_threshold")
|
|
34564
35668
|
}
|
|
34565
35669
|
},
|
|
34566
35670
|
rate: { num: "6", den: "100" },
|
|
@@ -34597,17 +35701,17 @@ var seniorDeductionRules = [
|
|
|
34597
35701
|
// § 151(d)(5)(C)(v): married taxpayers must file jointly — MFS gets $0.
|
|
34598
35702
|
kind: "if",
|
|
34599
35703
|
cond: isStatus12("mfs"),
|
|
34600
|
-
then:
|
|
35704
|
+
then: zero26,
|
|
34601
35705
|
else: {
|
|
34602
35706
|
// Only compute (and only demand the threshold) when a senior exists.
|
|
34603
35707
|
kind: "if",
|
|
34604
35708
|
cond: {
|
|
34605
35709
|
kind: "or",
|
|
34606
35710
|
args: [
|
|
34607
|
-
|
|
35711
|
+
fact39("isAge65OrOlder"),
|
|
34608
35712
|
{
|
|
34609
35713
|
kind: "and",
|
|
34610
|
-
args: [isStatus12("mfj"),
|
|
35714
|
+
args: [isStatus12("mfj"), fact39("spouseIsAge65OrOlder")]
|
|
34611
35715
|
}
|
|
34612
35716
|
]
|
|
34613
35717
|
},
|
|
@@ -34619,22 +35723,22 @@ var seniorDeductionRules = [
|
|
|
34619
35723
|
args: [
|
|
34620
35724
|
{
|
|
34621
35725
|
kind: "if",
|
|
34622
|
-
cond:
|
|
35726
|
+
cond: fact39("isAge65OrOlder"),
|
|
34623
35727
|
then: perSeniorNet(),
|
|
34624
|
-
else:
|
|
35728
|
+
else: zero26
|
|
34625
35729
|
},
|
|
34626
35730
|
{
|
|
34627
35731
|
kind: "if",
|
|
34628
35732
|
cond: {
|
|
34629
35733
|
kind: "and",
|
|
34630
|
-
args: [isStatus12("mfj"),
|
|
35734
|
+
args: [isStatus12("mfj"), fact39("spouseIsAge65OrOlder")]
|
|
34631
35735
|
},
|
|
34632
35736
|
then: perSeniorNet(),
|
|
34633
|
-
else:
|
|
35737
|
+
else: zero26
|
|
34634
35738
|
}
|
|
34635
35739
|
]
|
|
34636
35740
|
},
|
|
34637
|
-
else:
|
|
35741
|
+
else: zero26
|
|
34638
35742
|
}
|
|
34639
35743
|
}
|
|
34640
35744
|
},
|
|
@@ -34654,17 +35758,17 @@ var seniorDeductionRules = [
|
|
|
34654
35758
|
output: { type: "money" },
|
|
34655
35759
|
formula: {
|
|
34656
35760
|
kind: "match",
|
|
34657
|
-
on:
|
|
35761
|
+
on: fact39("filingStatus"),
|
|
34658
35762
|
cases: [
|
|
34659
35763
|
// $150,000 only "in the case of a joint return"; QSS is not a joint
|
|
34660
35764
|
// return. MFS is excluded from the deduction entirely (never reached).
|
|
34661
|
-
{ when: "mfj", value:
|
|
35765
|
+
{ when: "mfj", value: money36("15000000") },
|
|
34662
35766
|
// $150,000
|
|
34663
|
-
{ when: "single", value:
|
|
35767
|
+
{ when: "single", value: money36("7500000") },
|
|
34664
35768
|
// $75,000
|
|
34665
|
-
{ when: "hoh", value:
|
|
35769
|
+
{ when: "hoh", value: money36("7500000") },
|
|
34666
35770
|
// $75,000
|
|
34667
|
-
{ when: "qss", value:
|
|
35771
|
+
{ when: "qss", value: money36("7500000") }
|
|
34668
35772
|
// $75,000
|
|
34669
35773
|
]
|
|
34670
35774
|
}
|
|
@@ -34672,9 +35776,9 @@ var seniorDeductionRules = [
|
|
|
34672
35776
|
];
|
|
34673
35777
|
|
|
34674
35778
|
// ../corpus-us-federal/dist/rules/social-security.js
|
|
34675
|
-
var
|
|
34676
|
-
var
|
|
34677
|
-
var
|
|
35779
|
+
var fact40 = (factId) => ({ kind: "fact", factId });
|
|
35780
|
+
var money37 = (cents) => ({ kind: "money", cents });
|
|
35781
|
+
var ruleRef34 = (ruleId) => ({ kind: "rule", ruleId });
|
|
34678
35782
|
var param20 = (name) => ({ kind: "param", name });
|
|
34679
35783
|
var pct2 = (num, base) => ({
|
|
34680
35784
|
kind: "mulRate",
|
|
@@ -34689,51 +35793,53 @@ var magiBase = {
|
|
|
34689
35793
|
left: {
|
|
34690
35794
|
kind: "add",
|
|
34691
35795
|
args: [
|
|
34692
|
-
|
|
34693
|
-
|
|
34694
|
-
|
|
35796
|
+
fact40("wages"),
|
|
35797
|
+
fact40("taxableInterest"),
|
|
35798
|
+
fact40("taxExemptInterest"),
|
|
34695
35799
|
// § 86(b)(2)(B): "increased by the amount of interest… exempt from tax"
|
|
34696
|
-
|
|
34697
|
-
|
|
34698
|
-
|
|
34699
|
-
|
|
34700
|
-
|
|
34701
|
-
|
|
34702
|
-
|
|
34703
|
-
|
|
34704
|
-
|
|
34705
|
-
|
|
34706
|
-
|
|
35800
|
+
ruleRef34("us.federal.schedule_d.preferential_lt_gain"),
|
|
35801
|
+
fact40("qualifiedDividends"),
|
|
35802
|
+
ruleRef34("us.federal.schedule_d.ordinary_st_gain"),
|
|
35803
|
+
fact40("ordinaryDividends"),
|
|
35804
|
+
fact40("selfEmploymentNetProfit"),
|
|
35805
|
+
fact40("k1OrdinaryBusinessIncome"),
|
|
35806
|
+
fact40("taxableIraDistributions"),
|
|
35807
|
+
fact40("taxablePensionsAndAnnuities"),
|
|
35808
|
+
ruleRef34("us.federal.pension.simplified_method_taxable"),
|
|
35809
|
+
fact40("unemploymentCompensation"),
|
|
35810
|
+
fact40("otherOrdinaryIncome"),
|
|
35811
|
+
fact40("scheduleENetIncome"),
|
|
35812
|
+
ruleRef34("us.federal.rental.net_income")
|
|
34707
35813
|
]
|
|
34708
35814
|
},
|
|
34709
35815
|
right: {
|
|
34710
35816
|
kind: "add",
|
|
34711
35817
|
args: [
|
|
34712
|
-
|
|
34713
|
-
|
|
35818
|
+
ruleRef34("us.federal.capital_loss_ordinary_offset"),
|
|
35819
|
+
fact40("nonpassiveScheduleELoss")
|
|
34714
35820
|
]
|
|
34715
35821
|
}
|
|
34716
35822
|
},
|
|
34717
35823
|
right: {
|
|
34718
35824
|
kind: "add",
|
|
34719
35825
|
args: [
|
|
34720
|
-
|
|
34721
|
-
|
|
35826
|
+
ruleRef34("us.federal.above_the_line_adjustments"),
|
|
35827
|
+
ruleRef34("us.federal.ira_deduction")
|
|
34722
35828
|
]
|
|
34723
35829
|
}
|
|
34724
35830
|
};
|
|
34725
|
-
var ss =
|
|
35831
|
+
var ss = fact40("socialSecurityBenefits");
|
|
34726
35832
|
var provisional = { kind: "add", args: [magiBase, pct2("50", ss)] };
|
|
34727
35833
|
var isMfs3 = {
|
|
34728
35834
|
kind: "cmp",
|
|
34729
35835
|
op: "eq",
|
|
34730
|
-
left:
|
|
35836
|
+
left: fact40("filingStatus"),
|
|
34731
35837
|
right: { kind: "enum", value: "mfs" }
|
|
34732
35838
|
};
|
|
34733
35839
|
var isMfj = {
|
|
34734
35840
|
kind: "cmp",
|
|
34735
35841
|
op: "eq",
|
|
34736
|
-
left:
|
|
35842
|
+
left: fact40("filingStatus"),
|
|
34737
35843
|
right: { kind: "enum", value: "mfj" }
|
|
34738
35844
|
};
|
|
34739
35845
|
var byJoint = (joint, other) => ({
|
|
@@ -34745,8 +35851,8 @@ var byJoint = (joint, other) => ({
|
|
|
34745
35851
|
var socialSecurityRules = [
|
|
34746
35852
|
{
|
|
34747
35853
|
id: "us.federal.taxable_social_security",
|
|
34748
|
-
version:
|
|
34749
|
-
//
|
|
35854
|
+
version: 8,
|
|
35855
|
+
// v7 read the raw gain facts; the § 86 income measure now takes the schedule_d netted results, the simplified-method pension taxable amount, and computed net rental income
|
|
34750
35856
|
jurisdiction: "us.federal",
|
|
34751
35857
|
title: "Taxable Social Security benefits (\xA7 86: the 0/50/85% worksheet)",
|
|
34752
35858
|
citation: {
|
|
@@ -34774,8 +35880,8 @@ var socialSecurityRules = [
|
|
|
34774
35880
|
},
|
|
34775
35881
|
formula: {
|
|
34776
35882
|
kind: "if",
|
|
34777
|
-
cond: { kind: "cmp", op: "eq", left: ss, right:
|
|
34778
|
-
then:
|
|
35883
|
+
cond: { kind: "cmp", op: "eq", left: ss, right: money37("0") },
|
|
35884
|
+
then: money37("0"),
|
|
34779
35885
|
else: {
|
|
34780
35886
|
kind: "if",
|
|
34781
35887
|
cond: isMfs3,
|
|
@@ -34828,14 +35934,14 @@ var socialSecurityRules = [
|
|
|
34828
35934
|
];
|
|
34829
35935
|
|
|
34830
35936
|
// ../corpus-us-federal/dist/rules/standard-deduction.js
|
|
34831
|
-
var
|
|
34832
|
-
var
|
|
34833
|
-
var
|
|
34834
|
-
var
|
|
35937
|
+
var J27 = "us.federal";
|
|
35938
|
+
var fact41 = (factId) => ({ kind: "fact", factId });
|
|
35939
|
+
var money38 = (cents) => ({ kind: "money", cents });
|
|
35940
|
+
var ruleRef35 = (ruleId) => ({ kind: "rule", ruleId });
|
|
34835
35941
|
var isStatus13 = (status) => ({
|
|
34836
35942
|
kind: "cmp",
|
|
34837
35943
|
op: "eq",
|
|
34838
|
-
left:
|
|
35944
|
+
left: fact41("filingStatus"),
|
|
34839
35945
|
right: { kind: "enum", value: status }
|
|
34840
35946
|
});
|
|
34841
35947
|
var standardDeductionRules = [
|
|
@@ -34843,7 +35949,7 @@ var standardDeductionRules = [
|
|
|
34843
35949
|
id: "us.federal.standard_deduction.base_amount",
|
|
34844
35950
|
version: 2,
|
|
34845
35951
|
// v1 held pre-OBBBA Rev. Proc. 2024-40 amounts ($15,000/$30,000/$22,500)
|
|
34846
|
-
jurisdiction:
|
|
35952
|
+
jurisdiction: J27,
|
|
34847
35953
|
title: "Basic standard deduction amount by filing status (TY2025, as amended by OBBBA)",
|
|
34848
35954
|
citation: {
|
|
34849
35955
|
source: "26 U.S.C. \xA7 63(c), as amended by Pub. L. 119-21 (OBBBA) \xA7 70102",
|
|
@@ -34856,17 +35962,17 @@ var standardDeductionRules = [
|
|
|
34856
35962
|
output: { type: "money" },
|
|
34857
35963
|
formula: {
|
|
34858
35964
|
kind: "match",
|
|
34859
|
-
on:
|
|
35965
|
+
on: fact41("filingStatus"),
|
|
34860
35966
|
cases: [
|
|
34861
|
-
{ when: "single", value:
|
|
35967
|
+
{ when: "single", value: money38("1575000") },
|
|
34862
35968
|
// $15,750
|
|
34863
|
-
{ when: "mfs", value:
|
|
35969
|
+
{ when: "mfs", value: money38("1575000") },
|
|
34864
35970
|
// $15,750 (same as single)
|
|
34865
|
-
{ when: "mfj", value:
|
|
35971
|
+
{ when: "mfj", value: money38("3150000") },
|
|
34866
35972
|
// $31,500
|
|
34867
|
-
{ when: "qss", value:
|
|
35973
|
+
{ when: "qss", value: money38("3150000") },
|
|
34868
35974
|
// $31,500 (§ 1(j)(2)(A): "…and Surviving Spouses")
|
|
34869
|
-
{ when: "hoh", value:
|
|
35975
|
+
{ when: "hoh", value: money38("2362500") }
|
|
34870
35976
|
// $23,625
|
|
34871
35977
|
]
|
|
34872
35978
|
}
|
|
@@ -34874,7 +35980,7 @@ var standardDeductionRules = [
|
|
|
34874
35980
|
{
|
|
34875
35981
|
id: "us.federal.standard_deduction.base_amount",
|
|
34876
35982
|
version: 3,
|
|
34877
|
-
jurisdiction:
|
|
35983
|
+
jurisdiction: J27,
|
|
34878
35984
|
title: "Basic standard deduction amount by filing status (TY2026)",
|
|
34879
35985
|
citation: {
|
|
34880
35986
|
source: "26 U.S.C. \xA7 63(c); Rev. Proc. 2025-32",
|
|
@@ -34887,17 +35993,17 @@ var standardDeductionRules = [
|
|
|
34887
35993
|
output: { type: "money" },
|
|
34888
35994
|
formula: {
|
|
34889
35995
|
kind: "match",
|
|
34890
|
-
on:
|
|
35996
|
+
on: fact41("filingStatus"),
|
|
34891
35997
|
cases: [
|
|
34892
|
-
{ when: "single", value:
|
|
35998
|
+
{ when: "single", value: money38("1610000") },
|
|
34893
35999
|
// $16,100
|
|
34894
|
-
{ when: "mfs", value:
|
|
36000
|
+
{ when: "mfs", value: money38("1610000") },
|
|
34895
36001
|
// $16,100 ("single or married filing separately")
|
|
34896
|
-
{ when: "mfj", value:
|
|
36002
|
+
{ when: "mfj", value: money38("3220000") },
|
|
34897
36003
|
// $32,200
|
|
34898
|
-
{ when: "qss", value:
|
|
36004
|
+
{ when: "qss", value: money38("3220000") },
|
|
34899
36005
|
// $32,200 (surviving spouse)
|
|
34900
|
-
{ when: "hoh", value:
|
|
36006
|
+
{ when: "hoh", value: money38("2415000") }
|
|
34901
36007
|
// $24,150
|
|
34902
36008
|
]
|
|
34903
36009
|
}
|
|
@@ -34905,7 +36011,7 @@ var standardDeductionRules = [
|
|
|
34905
36011
|
{
|
|
34906
36012
|
id: "us.federal.standard_deduction.base",
|
|
34907
36013
|
version: 1,
|
|
34908
|
-
jurisdiction:
|
|
36014
|
+
jurisdiction: J27,
|
|
34909
36015
|
title: "Basic standard deduction",
|
|
34910
36016
|
citation: {
|
|
34911
36017
|
source: "26 U.S.C. \xA7 63(c)(2)",
|
|
@@ -34916,12 +36022,12 @@ var standardDeductionRules = [
|
|
|
34916
36022
|
effectiveFrom: "2025-01-01",
|
|
34917
36023
|
// structural: open-ended
|
|
34918
36024
|
output: { type: "money" },
|
|
34919
|
-
formula:
|
|
36025
|
+
formula: ruleRef35("us.federal.standard_deduction.base_amount")
|
|
34920
36026
|
},
|
|
34921
36027
|
{
|
|
34922
36028
|
id: "us.federal.standard_deduction.dependent_limit",
|
|
34923
36029
|
version: 1,
|
|
34924
|
-
jurisdiction:
|
|
36030
|
+
jurisdiction: J27,
|
|
34925
36031
|
title: "Limitation on basic standard deduction of a dependent",
|
|
34926
36032
|
citation: {
|
|
34927
36033
|
source: "26 U.S.C. \xA7 63(c)(5); Rev. Proc. 2024-40 \xA7 2.15(2); Rev. Proc. 2025-32 \xA7 4.14(2)",
|
|
@@ -34933,19 +36039,19 @@ var standardDeductionRules = [
|
|
|
34933
36039
|
effectiveTo: "2027-01-01",
|
|
34934
36040
|
// same dollar amounts apply in 2025 and 2026
|
|
34935
36041
|
output: { type: "money" },
|
|
34936
|
-
applicability:
|
|
36042
|
+
applicability: fact41("isClaimedAsDependent"),
|
|
34937
36043
|
// min(normal base amount, max($1,350, earned income + $450))
|
|
34938
36044
|
// simplified: earned income approximated as wages
|
|
34939
36045
|
formula: {
|
|
34940
36046
|
kind: "min",
|
|
34941
36047
|
args: [
|
|
34942
|
-
|
|
36048
|
+
ruleRef35("us.federal.standard_deduction.base_amount"),
|
|
34943
36049
|
{
|
|
34944
36050
|
kind: "max",
|
|
34945
36051
|
args: [
|
|
34946
|
-
|
|
36052
|
+
money38("135000"),
|
|
34947
36053
|
// $1,350
|
|
34948
|
-
{ kind: "add", args: [
|
|
36054
|
+
{ kind: "add", args: [fact41("wages"), money38("45000")] }
|
|
34949
36055
|
// + $450
|
|
34950
36056
|
]
|
|
34951
36057
|
}
|
|
@@ -34956,7 +36062,7 @@ var standardDeductionRules = [
|
|
|
34956
36062
|
{
|
|
34957
36063
|
id: "us.federal.standard_deduction.mfs_spouse_itemizes",
|
|
34958
36064
|
version: 1,
|
|
34959
|
-
jurisdiction:
|
|
36065
|
+
jurisdiction: J27,
|
|
34960
36066
|
title: "MFS standard deduction is zero when the spouse itemizes",
|
|
34961
36067
|
citation: {
|
|
34962
36068
|
source: "26 U.S.C. \xA7 63(c)(6)(A)",
|
|
@@ -34972,10 +36078,10 @@ var standardDeductionRules = [
|
|
|
34972
36078
|
applicability: {
|
|
34973
36079
|
kind: "if",
|
|
34974
36080
|
cond: isStatus13("mfs"),
|
|
34975
|
-
then:
|
|
36081
|
+
then: fact41("spouseItemizes"),
|
|
34976
36082
|
else: { kind: "bool", value: false }
|
|
34977
36083
|
},
|
|
34978
|
-
formula:
|
|
36084
|
+
formula: money38("0"),
|
|
34979
36085
|
// § 63(c)(6) zeroes the ENTIRE standard deduction (basic + additional),
|
|
34980
36086
|
// so this overrides the sum rule, not just the basic component.
|
|
34981
36087
|
overrides: { ruleId: "us.federal.standard_deduction", priority: 10 }
|
|
@@ -34995,7 +36101,7 @@ var standardDeductionRules = [
|
|
|
34995
36101
|
{
|
|
34996
36102
|
id: "us.federal.standard_deduction",
|
|
34997
36103
|
version: 1,
|
|
34998
|
-
jurisdiction:
|
|
36104
|
+
jurisdiction: J27,
|
|
34999
36105
|
title: "Standard deduction",
|
|
35000
36106
|
citation: {
|
|
35001
36107
|
source: "26 U.S.C. \xA7 63(c)(1)",
|
|
@@ -35009,8 +36115,8 @@ var standardDeductionRules = [
|
|
|
35009
36115
|
formula: {
|
|
35010
36116
|
kind: "add",
|
|
35011
36117
|
args: [
|
|
35012
|
-
|
|
35013
|
-
|
|
36118
|
+
ruleRef35("us.federal.standard_deduction.base"),
|
|
36119
|
+
ruleRef35("us.federal.standard_deduction.additional")
|
|
35014
36120
|
]
|
|
35015
36121
|
}
|
|
35016
36122
|
}
|
|
@@ -35018,7 +36124,7 @@ var standardDeductionRules = [
|
|
|
35018
36124
|
function additionalRule(version2, effectiveFrom, effectiveTo, marriedCents, unmarriedCents, label) {
|
|
35019
36125
|
const perStatusAmount = {
|
|
35020
36126
|
kind: "match",
|
|
35021
|
-
on:
|
|
36127
|
+
on: fact41("filingStatus"),
|
|
35022
36128
|
cases: [
|
|
35023
36129
|
// the higher amount is for "unmarried AND not a surviving spouse" —
|
|
35024
36130
|
// MFS and QSS get the married amount (read from both Rev. Procs.)
|
|
@@ -35033,12 +36139,12 @@ function additionalRule(version2, effectiveFrom, effectiveTo, marriedCents, unma
|
|
|
35033
36139
|
kind: "if",
|
|
35034
36140
|
cond,
|
|
35035
36141
|
then: perStatusAmount,
|
|
35036
|
-
else:
|
|
36142
|
+
else: money38("0")
|
|
35037
36143
|
});
|
|
35038
36144
|
return {
|
|
35039
36145
|
id: "us.federal.standard_deduction.additional",
|
|
35040
36146
|
version: version2,
|
|
35041
|
-
jurisdiction:
|
|
36147
|
+
jurisdiction: J27,
|
|
35042
36148
|
title: `Additional standard deduction for the aged and blind (TY${label.year})`,
|
|
35043
36149
|
citation: {
|
|
35044
36150
|
source: `26 U.S.C. \xA7 63(f); ${label.sourceRevProc}`,
|
|
@@ -35056,15 +36162,15 @@ function additionalRule(version2, effectiveFrom, effectiveTo, marriedCents, unma
|
|
|
35056
36162
|
formula: {
|
|
35057
36163
|
kind: "add",
|
|
35058
36164
|
args: [
|
|
35059
|
-
addIf(
|
|
35060
|
-
addIf(
|
|
36165
|
+
addIf(fact41("isAge65OrOlder")),
|
|
36166
|
+
addIf(fact41("isBlind")),
|
|
35061
36167
|
addIf({
|
|
35062
36168
|
kind: "and",
|
|
35063
|
-
args: [isStatus13("mfj"),
|
|
36169
|
+
args: [isStatus13("mfj"), fact41("spouseIsAge65OrOlder")]
|
|
35064
36170
|
}),
|
|
35065
36171
|
addIf({
|
|
35066
36172
|
kind: "and",
|
|
35067
|
-
args: [isStatus13("mfj"),
|
|
36173
|
+
args: [isStatus13("mfj"), fact41("spouseIsBlind")]
|
|
35068
36174
|
})
|
|
35069
36175
|
]
|
|
35070
36176
|
}
|
|
@@ -35072,12 +36178,12 @@ function additionalRule(version2, effectiveFrom, effectiveTo, marriedCents, unma
|
|
|
35072
36178
|
}
|
|
35073
36179
|
|
|
35074
36180
|
// ../corpus-us-federal/dist/rules/tips-eligibility.js
|
|
35075
|
-
var
|
|
36181
|
+
var fact42 = (factId) => ({ kind: "fact", factId });
|
|
35076
36182
|
var boolLit = (value) => ({ kind: "bool", value });
|
|
35077
36183
|
var isStatus14 = (status) => ({
|
|
35078
36184
|
kind: "cmp",
|
|
35079
36185
|
op: "eq",
|
|
35080
|
-
left:
|
|
36186
|
+
left: fact42("filingStatus"),
|
|
35081
36187
|
right: { kind: "enum", value: status }
|
|
35082
36188
|
});
|
|
35083
36189
|
var WINDOW = { effectiveFrom: "2025-01-01", effectiveTo: "2029-01-01" };
|
|
@@ -35097,7 +36203,7 @@ var tipsEligibilityRules = [
|
|
|
35097
36203
|
output: { type: "bool" },
|
|
35098
36204
|
formula: {
|
|
35099
36205
|
kind: "match",
|
|
35100
|
-
on:
|
|
36206
|
+
on: fact42("occupation"),
|
|
35101
36207
|
cases: [
|
|
35102
36208
|
...TIPPED_OCCUPATIONS.map((o) => ({ when: o.slug, value: boolLit(true) })),
|
|
35103
36209
|
{ when: "other", value: boolLit(false) }
|
|
@@ -35124,23 +36230,23 @@ var tipsEligibilityRules = [
|
|
|
35124
36230
|
args: [
|
|
35125
36231
|
{ kind: "not", arg: isStatus14("mfs") },
|
|
35126
36232
|
{ kind: "rule", ruleId: "us.federal.eligible.tips_occupation" },
|
|
35127
|
-
|
|
35128
|
-
{ kind: "not", arg:
|
|
36233
|
+
fact42("tipsWereVoluntary"),
|
|
36234
|
+
{ kind: "not", arg: fact42("employerIsSSTB") }
|
|
35129
36235
|
]
|
|
35130
36236
|
}
|
|
35131
36237
|
}
|
|
35132
36238
|
];
|
|
35133
36239
|
|
|
35134
36240
|
// ../corpus-us-federal/dist/rules/tips-overtime.js
|
|
35135
|
-
var
|
|
35136
|
-
var
|
|
35137
|
-
var
|
|
36241
|
+
var fact43 = (factId) => ({ kind: "fact", factId });
|
|
36242
|
+
var money39 = (cents) => ({ kind: "money", cents });
|
|
36243
|
+
var ruleRef36 = (ruleId) => ({ kind: "rule", ruleId });
|
|
35138
36244
|
var param21 = (name) => ({ kind: "param", name });
|
|
35139
|
-
var
|
|
36245
|
+
var zero27 = money39("0");
|
|
35140
36246
|
var isStatus15 = (status) => ({
|
|
35141
36247
|
kind: "cmp",
|
|
35142
36248
|
op: "eq",
|
|
35143
|
-
left:
|
|
36249
|
+
left: fact43("filingStatus"),
|
|
35144
36250
|
right: { kind: "enum", value: status }
|
|
35145
36251
|
});
|
|
35146
36252
|
function cappedPhasedDeduction(qualifiedFactId, cap, ineligible = isStatus15("mfs")) {
|
|
@@ -35148,22 +36254,22 @@ function cappedPhasedDeduction(qualifiedFactId, cap, ineligible = isStatus15("mf
|
|
|
35148
36254
|
kind: "if",
|
|
35149
36255
|
// LAZY FIRST: with no qualified amount, no eligibility facts are ever
|
|
35150
36256
|
// demanded — a taxpayer without tips is never asked their occupation
|
|
35151
|
-
cond: { kind: "cmp", op: "gt", left:
|
|
36257
|
+
cond: { kind: "cmp", op: "gt", left: fact43(qualifiedFactId), right: zero27 },
|
|
35152
36258
|
then: {
|
|
35153
36259
|
kind: "if",
|
|
35154
36260
|
cond: ineligible,
|
|
35155
|
-
then:
|
|
36261
|
+
then: zero27,
|
|
35156
36262
|
else: {
|
|
35157
36263
|
kind: "max0",
|
|
35158
36264
|
arg: {
|
|
35159
36265
|
kind: "sub",
|
|
35160
36266
|
left: {
|
|
35161
36267
|
kind: "min",
|
|
35162
|
-
args: [
|
|
36268
|
+
args: [fact43(qualifiedFactId), cap]
|
|
35163
36269
|
},
|
|
35164
36270
|
right: {
|
|
35165
36271
|
kind: "mulInt",
|
|
35166
|
-
base:
|
|
36272
|
+
base: money39("10000"),
|
|
35167
36273
|
// $100 per step
|
|
35168
36274
|
count: {
|
|
35169
36275
|
kind: "stepUnits",
|
|
@@ -35171,7 +36277,7 @@ function cappedPhasedDeduction(qualifiedFactId, cap, ineligible = isStatus15("mf
|
|
|
35171
36277
|
kind: "max0",
|
|
35172
36278
|
arg: {
|
|
35173
36279
|
kind: "sub",
|
|
35174
|
-
left:
|
|
36280
|
+
left: ruleRef36("us.federal.agi"),
|
|
35175
36281
|
right: {
|
|
35176
36282
|
kind: "if",
|
|
35177
36283
|
cond: isStatus15("mfj"),
|
|
@@ -35189,7 +36295,7 @@ function cappedPhasedDeduction(qualifiedFactId, cap, ineligible = isStatus15("mf
|
|
|
35189
36295
|
}
|
|
35190
36296
|
}
|
|
35191
36297
|
},
|
|
35192
|
-
else:
|
|
36298
|
+
else: zero27
|
|
35193
36299
|
};
|
|
35194
36300
|
}
|
|
35195
36301
|
var WINDOW2 = { effectiveFrom: "2025-01-01", effectiveTo: "2029-01-01" };
|
|
@@ -35256,7 +36362,7 @@ var tipsOvertimeRules = [
|
|
|
35256
36362
|
// ../corpus-us-federal/dist/index.js
|
|
35257
36363
|
var corpusInput = {
|
|
35258
36364
|
name: "@invaro/opentax-corpus-us-federal",
|
|
35259
|
-
version: "0.
|
|
36365
|
+
version: "0.37.0",
|
|
35260
36366
|
rules: [
|
|
35261
36367
|
...corporateRules,
|
|
35262
36368
|
...corporate1120Rules,
|
|
@@ -35270,6 +36376,9 @@ var corpusInput = {
|
|
|
35270
36376
|
...educationCreditRules,
|
|
35271
36377
|
...iraRules,
|
|
35272
36378
|
...ira8606Rules,
|
|
36379
|
+
...scheduleDRules,
|
|
36380
|
+
...rentalRules,
|
|
36381
|
+
...pensionRules,
|
|
35273
36382
|
...capitalLossRules,
|
|
35274
36383
|
...kiddieRules,
|
|
35275
36384
|
...incomeRules,
|
|
@@ -35339,6 +36448,39 @@ var documentsShape = external_exports.object({
|
|
|
35339
36448
|
box5: usd2.describe("net benefits (box 5) \u2014 includible amount computed by the \xA7 86 0/50/85% formula, not judged here"),
|
|
35340
36449
|
box6: usd2.optional().describe("voluntary federal income tax withheld")
|
|
35341
36450
|
}).strict()).optional().describe("one entry per SSA-1099 (or RRB-1099 tier-1 equivalent); box 5 sums into socialSecurityBenefits, box 6 into withholding"),
|
|
36451
|
+
f1099necs: external_exports.array(external_exports.object({
|
|
36452
|
+
box1: usd2.describe("nonemployee compensation (gross)"),
|
|
36453
|
+
box4: usd2.optional().describe("federal income tax withheld")
|
|
36454
|
+
}).strict()).optional().describe("one entry per 1099-NEC; box 1 sums into Schedule C gross receipts and nets against scheduleCExpensesTotal"),
|
|
36455
|
+
f1099ks: external_exports.array(external_exports.object({
|
|
36456
|
+
box1a: usd2.describe("gross amount of payment card / third-party network transactions"),
|
|
36457
|
+
box4: usd2.optional().describe("federal income tax withheld")
|
|
36458
|
+
}).strict()).optional().describe("one entry per 1099-K (business receipts); box 1a sums into Schedule C gross receipts alongside 1099-NECs"),
|
|
36459
|
+
scheduleCExpensesTotal: usd2.optional().describe("TOTAL Schedule C expenses to net against the 1099-NEC/1099-K gross receipts (one business; the taxpayer's own expense total, transcribed not judged)"),
|
|
36460
|
+
f1099ints: external_exports.array(external_exports.object({
|
|
36461
|
+
box1: usd2.optional().describe("interest income"),
|
|
36462
|
+
box3: usd2.optional().describe("U.S. Treasury/savings-bond interest \u2014 federally taxable (state-exempt; noted)"),
|
|
36463
|
+
box4: usd2.optional().describe("federal income tax withheld"),
|
|
36464
|
+
box8: usd2.optional().describe("tax-exempt interest (municipal) \u2014 feeds \xA7 86 provisional income, not taxable income")
|
|
36465
|
+
}).strict()).optional().describe("one entry per 1099-INT; boxes 1+3 sum into taxableInterest, box 8 into taxExemptInterest"),
|
|
36466
|
+
f1099divs: external_exports.array(external_exports.object({
|
|
36467
|
+
box1a: usd2.describe("total ordinary dividends (INCLUDES box 1b)"),
|
|
36468
|
+
box1b: usd2.optional().describe("qualified dividends (subset of 1a)"),
|
|
36469
|
+
box2a: usd2.optional().describe("total capital gain distributions \u2014 long-term by statute (\xA7 852(b)(3)(B)), summed into the LT bucket"),
|
|
36470
|
+
box4: usd2.optional().describe("federal income tax withheld")
|
|
36471
|
+
}).strict()).optional().describe("one entry per 1099-DIV; the compiler performs the 1a-minus-1b split the fact model expects and errors if 1b exceeds 1a"),
|
|
36472
|
+
f1099bs: external_exports.array(external_exports.object({
|
|
36473
|
+
proceeds: usd2.describe("box 1d proceeds"),
|
|
36474
|
+
basis: usd2.describe("box 1e cost or other basis"),
|
|
36475
|
+
term: external_exports.enum(["short", "long"]).describe("holding period per the form's Part I/II section"),
|
|
36476
|
+
washSaleLossDisallowed: usd2.optional().describe("box 1g \u2014 disallowed loss added back to this lot's result"),
|
|
36477
|
+
box4: usd2.optional().describe("federal income tax withheld")
|
|
36478
|
+
}).strict()).optional().describe("one entry per 1099-B lot or summary line; per-bucket ST/LT results are netted here and the engine's \xA7 1222 Schedule D netting rules do the cross-bucket math"),
|
|
36479
|
+
f1099gs: external_exports.array(external_exports.object({
|
|
36480
|
+
box1: usd2.optional().describe("unemployment compensation"),
|
|
36481
|
+
box2: usd2.optional().describe("state/local income tax refund \u2014 NOT auto-included (taxable only if it produced a benefit when itemizing the prior year, \xA7 111); surfaced as a note"),
|
|
36482
|
+
box4: usd2.optional().describe("federal income tax withheld")
|
|
36483
|
+
}).strict()).optional().describe("one entry per 1099-G; box 1 sums into unemploymentCompensation"),
|
|
35342
36484
|
dependents: external_exports.array(external_exports.object({
|
|
35343
36485
|
dateOfBirth: dob,
|
|
35344
36486
|
relationship: external_exports.enum(["child_or_descendant", "sibling_or_descendant", "parent_or_other_relative"]).optional().describe("\xA7 152(c)(2) lineage: children/grandchildren and siblings/nieces/nephews can be QUALIFYING CHILDREN; a parent or other relative can only be a qualifying RELATIVE (ODC, never CTC/EIC-child). TRANSCRIBE the document's own label: son/daughter/grandchild/foster/sibling/niece \u2192 child_or_descendant or sibling_or_descendant; an explicit 'other' relationship on a source form \u2192 parent_or_other_relative (never default it back to child lineage). The child_or_descendant default is ONLY for documents that state a child relationship or say nothing at all."),
|
|
@@ -35376,7 +36518,7 @@ function compileDocuments(docs, asOf) {
|
|
|
35376
36518
|
const ints = {};
|
|
35377
36519
|
const bools = {};
|
|
35378
36520
|
const notes = [];
|
|
35379
|
-
const
|
|
36521
|
+
const add3 = (id, c2) => {
|
|
35380
36522
|
sums[id] = (sums[id] ?? 0n) + c2;
|
|
35381
36523
|
};
|
|
35382
36524
|
const born65Cutoff = (dobStr) => ageAtYearEnd(dobStr, taxYear) >= 65;
|
|
@@ -35398,20 +36540,20 @@ function compileDocuments(docs, asOf) {
|
|
|
35398
36540
|
}
|
|
35399
36541
|
let w2Box1Cents = 0n;
|
|
35400
36542
|
for (const [i, w] of (docs.w2s ?? []).entries()) {
|
|
35401
|
-
|
|
36543
|
+
add3("wages", toCents(w.box1));
|
|
35402
36544
|
w2Box1Cents += toCents(w.box1);
|
|
35403
36545
|
if (w.box2 !== void 0)
|
|
35404
|
-
|
|
36546
|
+
add3("federalTaxWithheld", toCents(w.box2));
|
|
35405
36547
|
if (w.box3 !== void 0 && !multiW2)
|
|
35406
|
-
|
|
36548
|
+
add3("socialSecurityWages", toCents(w.box3));
|
|
35407
36549
|
if (w.box5 !== void 0) {
|
|
35408
36550
|
const b5 = toCents(w.box5);
|
|
35409
|
-
|
|
36551
|
+
add3("medicareWages", b5);
|
|
35410
36552
|
if (w.box6 !== void 0 && b5 > 20000000n) {
|
|
35411
36553
|
const regular = (b5 * 145n + 5000n) / 10000n;
|
|
35412
36554
|
const excess = toCents(w.box6) - regular;
|
|
35413
36555
|
if (excess > 0n) {
|
|
35414
|
-
|
|
36556
|
+
add3("federalTaxWithheld", excess);
|
|
35415
36557
|
notes.push(`W-2 #${i + 1}: Form 8959 Part IV \u2014 box 6 exceeds 1.45% of box 5 by $${dollars2(excess)}; added to withholding`);
|
|
35416
36558
|
}
|
|
35417
36559
|
}
|
|
@@ -35423,46 +36565,133 @@ function compileDocuments(docs, asOf) {
|
|
|
35423
36565
|
const PENALTY_EXEMPT_CODES = /* @__PURE__ */ new Set(["2", "3", "4", "7", "G", "H", "Q", "T", "C"]);
|
|
35424
36566
|
for (const [i, r] of (docs.f1099rs ?? []).entries()) {
|
|
35425
36567
|
if (r.box4 !== void 0)
|
|
35426
|
-
|
|
36568
|
+
add3("federalTaxWithheld", toCents(r.box4));
|
|
35427
36569
|
const taxable3 = toCents(r.box2a);
|
|
35428
36570
|
if (r.rolledOver || r.box7.toUpperCase().includes("G")) {
|
|
35429
36571
|
notes.push(`1099-R #${i + 1}: treated as ROLLOVER (${r.rolledOver ? "interview-confirmed" : "code G"}) \u2014 gross on 4a/5a only, $0 taxable`);
|
|
35430
36572
|
continue;
|
|
35431
36573
|
}
|
|
35432
36574
|
if (r.disabilityBeforeRetirementAge) {
|
|
35433
|
-
|
|
35434
|
-
|
|
36575
|
+
add3("wages", taxable3);
|
|
36576
|
+
add3("scheduleRDisabilityIncome", taxable3);
|
|
35435
36577
|
notes.push(`1099-R #${i + 1}: code-3 disability before minimum retirement age \u2014 $${dollars2(taxable3)} reported as WAGES (Pub. 525, Form 1040 line 1h \u2014 NOT line 1a, which is W-2 box 1 only) and counted as \xA7 22 disability income`);
|
|
35436
36578
|
continue;
|
|
35437
36579
|
}
|
|
35438
36580
|
if (r.iraSepSimple)
|
|
35439
|
-
|
|
36581
|
+
add3("taxableIraDistributions", taxable3);
|
|
35440
36582
|
else
|
|
35441
|
-
|
|
36583
|
+
add3("taxablePensionsAndAnnuities", taxable3);
|
|
35442
36584
|
const dobStr = r.recipient === "spouse" ? docs.spouseDateOfBirth : docs.taxpayerDateOfBirth;
|
|
35443
36585
|
const code = r.box7.toUpperCase();
|
|
35444
36586
|
if ([...code].some((c2) => c2 === "1")) {
|
|
35445
36587
|
if (dobStr && ageYearsExact(dobStr, taxYear) >= 59.5) {
|
|
35446
36588
|
notes.push(`1099-R #${i + 1}: payer code 1 (early) but the ${r.recipient ?? "taxpayer"} is over 59\xBD \u2014 no \xA7 72(t) penalty (age controls, not the box code)`);
|
|
35447
36589
|
} else if ([...code].every((c2) => !PENALTY_EXEMPT_CODES.has(c2))) {
|
|
35448
|
-
|
|
36590
|
+
add3("earlyDistributionSubjectToPenalty", taxable3);
|
|
35449
36591
|
notes.push(`1099-R #${i + 1}: code 1 and no age exception established \u2014 $${dollars2(taxable3)} subject to the 10% \xA7 72(t) tax`);
|
|
35450
36592
|
}
|
|
35451
36593
|
}
|
|
35452
36594
|
}
|
|
35453
36595
|
for (const s of docs.ssa1099s ?? []) {
|
|
35454
|
-
|
|
36596
|
+
add3("socialSecurityBenefits", toCents(s.box5));
|
|
35455
36597
|
if (s.box6 !== void 0)
|
|
35456
|
-
|
|
36598
|
+
add3("federalTaxWithheld", toCents(s.box6));
|
|
36599
|
+
}
|
|
36600
|
+
let seGross = 0n;
|
|
36601
|
+
for (const n of docs.f1099necs ?? []) {
|
|
36602
|
+
seGross += toCents(n.box1);
|
|
36603
|
+
if (n.box4 !== void 0)
|
|
36604
|
+
add3("federalTaxWithheld", toCents(n.box4));
|
|
36605
|
+
}
|
|
36606
|
+
for (const k of docs.f1099ks ?? []) {
|
|
36607
|
+
seGross += toCents(k.box1a);
|
|
36608
|
+
if (k.box4 !== void 0)
|
|
36609
|
+
add3("federalTaxWithheld", toCents(k.box4));
|
|
36610
|
+
}
|
|
36611
|
+
if (seGross > 0n || docs.scheduleCExpensesTotal !== void 0) {
|
|
36612
|
+
const expenses = docs.scheduleCExpensesTotal !== void 0 ? toCents(docs.scheduleCExpensesTotal) : 0n;
|
|
36613
|
+
const net = seGross - expenses;
|
|
36614
|
+
if (net >= 0n) {
|
|
36615
|
+
if (net > 0n)
|
|
36616
|
+
add3("selfEmploymentNetProfit", net);
|
|
36617
|
+
notes.push(`Schedule C: $${dollars2(seGross)} gross (1099-NEC/K) \u2212 $${dollars2(expenses)} expenses = $${dollars2(net)} net profit \u2192 SE tax + QBI machinery engage on it`);
|
|
36618
|
+
} else {
|
|
36619
|
+
add3("scheduleCNetLoss", -net);
|
|
36620
|
+
notes.push(`Schedule C: expenses exceed 1099-NEC/K gross by $${dollars2(-net)} \u2014 recorded as scheduleCNetLoss`);
|
|
36621
|
+
}
|
|
36622
|
+
}
|
|
36623
|
+
for (const [i, t] of (docs.f1099ints ?? []).entries()) {
|
|
36624
|
+
if (t.box1 !== void 0)
|
|
36625
|
+
add3("taxableInterest", toCents(t.box1));
|
|
36626
|
+
if (t.box3 !== void 0 && toCents(t.box3) > 0n) {
|
|
36627
|
+
add3("taxableInterest", toCents(t.box3));
|
|
36628
|
+
notes.push(`1099-INT #${i + 1}: box 3 Treasury interest $${dollars2(toCents(t.box3))} is federally taxable (state returns exempt it \u2014 the state composers handle that subtraction)`);
|
|
36629
|
+
}
|
|
36630
|
+
if (t.box8 !== void 0)
|
|
36631
|
+
add3("taxExemptInterest", toCents(t.box8));
|
|
36632
|
+
if (t.box4 !== void 0)
|
|
36633
|
+
add3("federalTaxWithheld", toCents(t.box4));
|
|
36634
|
+
}
|
|
36635
|
+
for (const [i, d3] of (docs.f1099divs ?? []).entries()) {
|
|
36636
|
+
const total = toCents(d3.box1a);
|
|
36637
|
+
const qualified2 = d3.box1b !== void 0 ? toCents(d3.box1b) : 0n;
|
|
36638
|
+
if (qualified2 > total) {
|
|
36639
|
+
throw new Error(`1099-DIV #${i + 1}: box 1b (qualified, $${dollars2(qualified2)}) exceeds box 1a (total, $${dollars2(total)}) \u2014 transcription error`);
|
|
36640
|
+
}
|
|
36641
|
+
if (qualified2 > 0n)
|
|
36642
|
+
add3("qualifiedDividends", qualified2);
|
|
36643
|
+
if (total - qualified2 > 0n)
|
|
36644
|
+
add3("ordinaryDividends", total - qualified2);
|
|
36645
|
+
if (d3.box2a !== void 0 && toCents(d3.box2a) > 0n) {
|
|
36646
|
+
add3("__ltProceeds", toCents(d3.box2a));
|
|
36647
|
+
notes.push(`1099-DIV #${i + 1}: box 2a capital gain distributions $${dollars2(toCents(d3.box2a))} \u2014 long-term by statute (\xA7 852(b)(3)(B)), joined to the Schedule D long-term bucket`);
|
|
36648
|
+
}
|
|
36649
|
+
if (d3.box4 !== void 0)
|
|
36650
|
+
add3("federalTaxWithheld", toCents(d3.box4));
|
|
36651
|
+
}
|
|
36652
|
+
let stNet = 0n;
|
|
36653
|
+
let ltNet = sums.__ltProceeds ?? 0n;
|
|
36654
|
+
delete sums.__ltProceeds;
|
|
36655
|
+
let sawB = (docs.f1099bs ?? []).length > 0 || ltNet !== 0n;
|
|
36656
|
+
for (const b of docs.f1099bs ?? []) {
|
|
36657
|
+
let lot = toCents(b.proceeds) - toCents(b.basis);
|
|
36658
|
+
if (b.washSaleLossDisallowed !== void 0)
|
|
36659
|
+
lot += toCents(b.washSaleLossDisallowed);
|
|
36660
|
+
if (b.term === "short")
|
|
36661
|
+
stNet += lot;
|
|
36662
|
+
else
|
|
36663
|
+
ltNet += lot;
|
|
36664
|
+
if (b.box4 !== void 0)
|
|
36665
|
+
add3("federalTaxWithheld", toCents(b.box4));
|
|
36666
|
+
}
|
|
36667
|
+
if (sawB) {
|
|
36668
|
+
if (stNet > 0n)
|
|
36669
|
+
add3("shortTermCapitalGains", stNet);
|
|
36670
|
+
else if (stNet < 0n)
|
|
36671
|
+
add3("shortTermCapitalLoss", -stNet);
|
|
36672
|
+
if (ltNet > 0n)
|
|
36673
|
+
add3("longTermCapitalGains", ltNet);
|
|
36674
|
+
else if (ltNet < 0n)
|
|
36675
|
+
add3("longTermCapitalLoss", -ltNet);
|
|
36676
|
+
notes.push(`Schedule D buckets from 1099-B/DIV: short-term net $${dollars2(stNet)}, long-term net $${dollars2(ltNet)} \u2014 the \xA7 1222 netting rules combine them (character preserved, \xA7 1211(b) caps any overall loss)`);
|
|
36677
|
+
}
|
|
36678
|
+
for (const [i, g] of (docs.f1099gs ?? []).entries()) {
|
|
36679
|
+
if (g.box1 !== void 0)
|
|
36680
|
+
add3("unemploymentCompensation", toCents(g.box1));
|
|
36681
|
+
if (g.box4 !== void 0)
|
|
36682
|
+
add3("federalTaxWithheld", toCents(g.box4));
|
|
36683
|
+
if (g.box2 !== void 0 && toCents(g.box2) > 0n) {
|
|
36684
|
+
notes.push(`1099-G #${i + 1}: box 2 state refund $${dollars2(toCents(g.box2))} NOT auto-included \u2014 taxable only to the extent the prior-year SALT deduction produced a benefit (\xA7 111); add it to otherOrdinaryIncome yourself if it did`);
|
|
36685
|
+
}
|
|
35457
36686
|
}
|
|
35458
36687
|
for (const dep of docs.dependents ?? []) {
|
|
35459
36688
|
const age = ageAtYearEnd(dep.dateOfBirth, taxYear);
|
|
35460
36689
|
const disabled = dep.isPermanentlyDisabled === true;
|
|
35461
36690
|
const student = dep.isFullTimeStudent === true;
|
|
35462
36691
|
const ssn = dep.hasRequiredSSN !== false;
|
|
35463
|
-
const
|
|
36692
|
+
const months2 = dep.monthsLivedWithTaxpayer ?? 12;
|
|
35464
36693
|
const childLineage = (dep.relationship ?? "child_or_descendant") !== "parent_or_other_relative";
|
|
35465
|
-
if (
|
|
36694
|
+
if (months2 <= 6 && !disabled) {
|
|
35466
36695
|
notes.push(`dependent (b. ${dep.dateOfBirth}): lived \u22646 months \u2014 residency test not met, excluded (divorced-parents release etc. via determine_dependent)`);
|
|
35467
36696
|
continue;
|
|
35468
36697
|
}
|
|
@@ -35497,14 +36726,14 @@ function compileDocuments(docs, asOf) {
|
|
|
35497
36726
|
|
|
35498
36727
|
// dist/schema.js
|
|
35499
36728
|
var corpus = getCorpus();
|
|
35500
|
-
var
|
|
36729
|
+
var money40 = external_exports.union([external_exports.number(), external_exports.string()]).describe('dollars, e.g. 50000 or "1234.56"');
|
|
35501
36730
|
function zodForFact(f) {
|
|
35502
36731
|
if (f.id === "occupation") {
|
|
35503
36732
|
return external_exports.string().describe('tipped occupation, fuzzy-matched to the Treasury Tipped Occupation list (Treas. Reg. \xA7 1.224-1) \u2014 e.g. "bartender", "nanny" \u2014 or "other" if genuinely unlisted. Only needed when qualifiedTips > 0.');
|
|
35504
36733
|
}
|
|
35505
36734
|
switch (f.type) {
|
|
35506
36735
|
case "money":
|
|
35507
|
-
return
|
|
36736
|
+
return money40.describe(f.description);
|
|
35508
36737
|
case "bool":
|
|
35509
36738
|
return external_exports.boolean().describe(f.description);
|
|
35510
36739
|
case "int":
|
|
@@ -35569,6 +36798,11 @@ var INDIVIDUAL_GROUPS = {
|
|
|
35569
36798
|
"shortTermCapitalGains",
|
|
35570
36799
|
"ordinaryDividends",
|
|
35571
36800
|
"netCapitalLoss",
|
|
36801
|
+
"shortTermCapitalLoss",
|
|
36802
|
+
"longTermCapitalLoss",
|
|
36803
|
+
"rentalDepreciableBasis",
|
|
36804
|
+
"rentalPlacedInServiceMonth",
|
|
36805
|
+
"rentalIncomeBeforeDepreciation",
|
|
35572
36806
|
"unemploymentCompensation",
|
|
35573
36807
|
"otherOrdinaryIncome",
|
|
35574
36808
|
"socialSecurityWages",
|
|
@@ -35590,6 +36824,14 @@ var INDIVIDUAL_GROUPS = {
|
|
|
35590
36824
|
"taxableIraDistributions",
|
|
35591
36825
|
"taxablePensionsAndAnnuities",
|
|
35592
36826
|
"earlyDistributionSubjectToPenalty",
|
|
36827
|
+
// § 72(d) Simplified Method — the engine computes the taxable portion
|
|
36828
|
+
"pensionGrossPayments",
|
|
36829
|
+
"pensionCostBasis",
|
|
36830
|
+
"pensionAgeAtStart",
|
|
36831
|
+
"pensionIsJointAndSurvivor",
|
|
36832
|
+
"pensionCombinedAgesAtStart",
|
|
36833
|
+
"pensionMonthsThisYear",
|
|
36834
|
+
"pensionBasisPreviouslyRecovered",
|
|
35593
36835
|
// Form 8606 basis pro-rata — standalone target us.federal.ira8606.*
|
|
35594
36836
|
"ira8606Basis",
|
|
35595
36837
|
"ira8606NondeductibleContributions",
|
|
@@ -35705,7 +36947,25 @@ var INDIVIDUAL_GROUPS = {
|
|
|
35705
36947
|
"nyIt214HomeownerTaxes",
|
|
35706
36948
|
"nyIt216StateCredit",
|
|
35707
36949
|
"nyIt216Under4Expenses",
|
|
35708
|
-
"nyIt216TotalExpenses"
|
|
36950
|
+
"nyIt216TotalExpenses",
|
|
36951
|
+
"paCompensationAdjustment",
|
|
36952
|
+
"paUnreimbursedBusinessExpenses",
|
|
36953
|
+
"paInterestIncome",
|
|
36954
|
+
"paDividendIncome",
|
|
36955
|
+
"paBusinessNetIncome",
|
|
36956
|
+
"paSpouseBusinessNetIncome",
|
|
36957
|
+
"paPropertyGainNet",
|
|
36958
|
+
"paSpousePropertyGainNet",
|
|
36959
|
+
"paRentRoyaltyNet",
|
|
36960
|
+
"paSpouseRentRoyaltyNet",
|
|
36961
|
+
"paEstateTrustIncome",
|
|
36962
|
+
"paGamblingWinnings",
|
|
36963
|
+
"pa529Contributions",
|
|
36964
|
+
"paAbleContributions",
|
|
36965
|
+
"paMsaHsaContributions",
|
|
36966
|
+
"paSpDependentChildren",
|
|
36967
|
+
"paEligibilityIncomeAddbacks",
|
|
36968
|
+
"paResidentCredit"
|
|
35709
36969
|
],
|
|
35710
36970
|
household_employer: ["householdEmployeeCashWages", "householdFutaTestMet"],
|
|
35711
36971
|
payments_estimates: [
|
|
@@ -35995,7 +37255,7 @@ function createServer() {
|
|
|
35995
37255
|
}
|
|
35996
37256
|
});
|
|
35997
37257
|
server2.registerTool("compute_state_return", {
|
|
35998
|
-
description: "Compose a STATE return's printed-form line set deterministically (2025 IL-1040 / VA 760 / CA 540 / NY IT-201) \u2014 correct line NUMBERS from the printed forms and whole-dollar rounding, with the state tax computed by the oracle targets internally. Workflow: run compute_return first for the federal substrate, compute any state-specific components the citations describe (additions, subtractions, credits without targets \u2014 disclose each), then call this ONCE and report its line set VERBATIM. Never hand-assemble state line numbers: transposed lines on correct dollars are the dominant state error mode. ALWAYS pass taxableSocialSecurity and unemploymentCompensation when nonzero (VA/CA/NY subtractions are applied by the composer). ALWAYS transcribe the intake's state-specific block (e.g. ca_tax_return.ca_form540_schca: AB 5 employee-classification additions; va_sch_a fields; county/use-tax questions) \u2014 those fields drive composer inputs. For VA MFJ, pass vaYourVagi/vaSpouseVagi (the separate-VAGI worksheet) so the composer can run the Spouse Tax Adjustment worksheet itself.",
|
|
37258
|
+
description: "Compose a STATE return's printed-form line set deterministically (2025 IL-1040 / VA 760 / CA 540 / NY IT-201 / PA-40) \u2014 correct line NUMBERS from the printed forms and whole-dollar rounding, with the state tax computed by the oracle targets internally. PA is CLASS-BASED: transcribe the pa* class fields (Box 16 compensation, per-spouse loss classes) \u2014 federalAGI is NOT the PA base; the composer runs the class netting, Schedule O, and Tax Forgiveness targets itself, and reports the WPTC as a note (no printed line). Workflow: run compute_return first for the federal substrate, compute any state-specific components the citations describe (additions, subtractions, credits without targets \u2014 disclose each), then call this ONCE and report its line set VERBATIM. Never hand-assemble state line numbers: transposed lines on correct dollars are the dominant state error mode. ALWAYS pass taxableSocialSecurity and unemploymentCompensation when nonzero (VA/CA/NY subtractions are applied by the composer). ALWAYS transcribe the intake's state-specific block (e.g. ca_tax_return.ca_form540_schca: AB 5 employee-classification additions; va_sch_a fields; county/use-tax questions) \u2014 those fields drive composer inputs. For VA MFJ, pass vaYourVagi/vaSpouseVagi (the separate-VAGI worksheet) so the composer can run the Spouse Tax Adjustment worksheet itself.",
|
|
35999
37259
|
inputSchema: external_exports.object({ ...stateReturnShape, asOf: external_exports.string().describe("year-end date, e.g. 2025-12-31 \u2014 REQUIRED"), filingJoint: external_exports.boolean().optional(), filingHoh: external_exports.boolean().optional(), filingHohOrQss: external_exports.boolean().optional() }).strict()
|
|
36000
37260
|
}, async (args) => {
|
|
36001
37261
|
try {
|
|
@@ -36013,7 +37273,7 @@ function createServer() {
|
|
|
36013
37273
|
}
|
|
36014
37274
|
});
|
|
36015
37275
|
server2.registerTool("compute_return", {
|
|
36016
|
-
description: "Compute the COMPLETE
|
|
37276
|
+
description: "Compute the COMPLETE Form 1040 bottom-line set in one call \u2014 the 17 lines that determine the return (1a, 9, 10, 11, 12e, 15, 16, 17 AMT, 19, 22, 23, 24, 25d, 27a, 28, 32, 33, 34/37), each whole-dollar rounded by the engine. Takes the SAME input as calculate_tax (prefer the documents block: transcribe W-2/1099-R/SSA-1099 boxes and dependent birth dates \u2014 SSA-1099s are first-class, box 5 and box 6 are summed for you; the tool derives ages, classifications, Part IV withholding, and penalties deterministically). Never assemble return lines by hand \u2014 this tool is the return. TRANSCRIPTION CONVENTIONS: (1) a PRIOR-YEAR Form 1040 in the file supplies CONTINUING conditions the current-year interview omits \u2014 the 'Someone can claim: You as a dependent' checkbox and the blindness boxes carry forward unless the current-year data contradicts them; (2) COMMUNITY PROPERTY: do NOT split income 50/50 between MFS spouses when they lived apart all year with no transfers (\xA7 66(a) allocates earned income to the earner) or when a written separation agreement ended the community \u2014 transcribe each document to its named earner; never invent a Form 8958 split the intake does not request; (3) bonus depreciation for assets placed in service 1/1-1/19/2025 is 40% (\xA7 168(k) pre-OBBBA phase-down; 100% only for property ACQUIRED after 1/19/2025).",
|
|
36017
37277
|
inputSchema: external_exports.object(individualNestedShape).strict()
|
|
36018
37278
|
}, async (args) => {
|
|
36019
37279
|
try {
|
|
@@ -36022,7 +37282,7 @@ function createServer() {
|
|
|
36022
37282
|
const { value } = evaluate(corpus, facts2, { asOf, target });
|
|
36023
37283
|
return value.type === "money" ? value.cents : 0n;
|
|
36024
37284
|
};
|
|
36025
|
-
const
|
|
37285
|
+
const rd3 = (c2) => {
|
|
36026
37286
|
const neg = c2 < 0n;
|
|
36027
37287
|
const abs = neg ? -c2 : c2;
|
|
36028
37288
|
const r = (abs + 50n) / 100n * 100n;
|
|
@@ -36049,11 +37309,11 @@ function createServer() {
|
|
|
36049
37309
|
const extension = extFact && extFact.type === "money" ? BigInt(extFact.value) : 0n;
|
|
36050
37310
|
const estFact = facts2.federalEstimatedPayments;
|
|
36051
37311
|
const estimated = estFact && estFact.type === "money" ? BigInt(estFact.value) : 0n;
|
|
36052
|
-
const total24 =
|
|
36053
|
-
const payments =
|
|
37312
|
+
const total24 = rd3(after) + rd3(other);
|
|
37313
|
+
const payments = rd3(withheld) + rd3(refundable) + rd3(extension) + rd3(estimated);
|
|
36054
37314
|
const balance = payments - total24;
|
|
36055
37315
|
const { proof } = evaluate(corpus, facts2, { asOf, target: "us.federal.net_tax" });
|
|
36056
|
-
const d3 = (c2) => fmt2(
|
|
37316
|
+
const d3 = (c2) => fmt2(rd3(c2));
|
|
36057
37317
|
return ok({
|
|
36058
37318
|
ok: true,
|
|
36059
37319
|
asOf,
|
|
@@ -36080,7 +37340,7 @@ function createServer() {
|
|
|
36080
37340
|
"28_actc": d3(actc),
|
|
36081
37341
|
"29_aotc_refundable": d3(aotcRef),
|
|
36082
37342
|
"32_refundable_credits": d3(refundable),
|
|
36083
|
-
...extension > 0n ? { "31_other_payments_incl_extension": fmt2(
|
|
37343
|
+
...extension > 0n ? { "31_other_payments_incl_extension": fmt2(rd3(extension)) } : {},
|
|
36084
37344
|
"33_total_payments": fmt2(payments),
|
|
36085
37345
|
"34_refund_or_37_owed": balance >= 0n ? `refund ${fmt2(balance)}` : `owed ${fmt2(-balance)}`
|
|
36086
37346
|
},
|
|
@@ -36096,7 +37356,7 @@ function createServer() {
|
|
|
36096
37356
|
description: `Verify a claimed tax amount (yours, a user's, or another tool's) against the law. Returns verdict 'verified' or 'refuted' with the correct value. Use this as a self-check before presenting any tax number. Put asOf (and target, if any) INSIDE the facts object \u2014 e.g. facts: {..., "asOf": "2025-12-31"} \u2014 otherwise the claim is checked under today's law.`,
|
|
36097
37357
|
inputSchema: external_exports.object({
|
|
36098
37358
|
facts: factsObjectParam,
|
|
36099
|
-
claimedAmount:
|
|
37359
|
+
claimedAmount: money40.describe("the amount to verify (negative = refund)"),
|
|
36100
37360
|
toleranceDollars: external_exports.number().min(0).optional().describe("default 1")
|
|
36101
37361
|
}).strict()
|
|
36102
37362
|
}, async (args) => {
|
|
@@ -36238,11 +37498,37 @@ function createServer() {
|
|
|
36238
37498
|
corpusMerkleRoot: corpus.merkleRoot
|
|
36239
37499
|
});
|
|
36240
37500
|
});
|
|
37501
|
+
server2.registerTool("search_tax_rules", {
|
|
37502
|
+
description: "Full-text search over the encoded tax-law corpus ('kiddie tax', 'NIIT threshold', 'california renters credit'). Returns matching rules: id, title, statutory citation, effective window, and a verbatim excerpt of the law text. A hit means the engine computes this; zero hits means it is outside the corpus \u2014 say so rather than guessing. Follow up with explain_rule for a hit's full formula, or lookup_tax_parameter for its dollar amounts.",
|
|
37503
|
+
inputSchema: external_exports.object({
|
|
37504
|
+
query: external_exports.string().describe("plain-English search, e.g. 'kiddie tax'"),
|
|
37505
|
+
asOf: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
37506
|
+
limit: external_exports.number().int().min(1).max(20).optional()
|
|
37507
|
+
}).strict()
|
|
37508
|
+
}, async ({ query, asOf, limit }) => {
|
|
37509
|
+
const hits = searchRules(corpus, query, asOf, limit ?? 8);
|
|
37510
|
+
return ok({
|
|
37511
|
+
ok: true,
|
|
37512
|
+
query,
|
|
37513
|
+
...asOf ? { asOf } : {},
|
|
37514
|
+
covered: hits.length > 0,
|
|
37515
|
+
hits: hits.map((h) => ({
|
|
37516
|
+
ruleId: h.ruleId,
|
|
37517
|
+
title: h.title,
|
|
37518
|
+
jurisdiction: h.jurisdiction,
|
|
37519
|
+
citation: `${h.citation.source} ${h.citation.section}`,
|
|
37520
|
+
effective: `[${h.effectiveFrom}, ${h.effectiveTo ?? "open"})`,
|
|
37521
|
+
excerpt: h.snippet,
|
|
37522
|
+
hasDollarParameters: h.hasParameters
|
|
37523
|
+
})),
|
|
37524
|
+
corpusMerkleRoot: corpus.merkleRoot
|
|
37525
|
+
});
|
|
37526
|
+
});
|
|
36241
37527
|
server2.registerTool("verify_fact", {
|
|
36242
37528
|
description: "Fact-check a claimed dollar amount about tax law ('the 2026 MFJ standard deduction is $32,200', 'CTC is $2,000 per child') against the corpus. Returns verified / refuted (with the correct value and citation) / unknown. Never states a verdict it cannot ground.",
|
|
36243
37529
|
inputSchema: external_exports.object({
|
|
36244
37530
|
query: external_exports.string().describe("what the amount is, e.g. 'standard deduction'"),
|
|
36245
|
-
claimedAmount:
|
|
37531
|
+
claimedAmount: money40,
|
|
36246
37532
|
filingStatus: external_exports.enum(["single", "mfj", "mfs", "hoh", "qss"]).optional(),
|
|
36247
37533
|
asOf: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
36248
37534
|
}).strict()
|