@mudraforge/statutory 2.7.0 → 2.8.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +60 -27
  3. package/bin/cli.js +359 -16
  4. package/docs/CORPUS_QUALITY.md +19 -6
  5. package/docs/PROVENANCE.md +33 -0
  6. package/okf/corporate_ma_restructuring/slump_sale_50b/circulars/cbdt_notification_68_2021_rule_11uae_fmv.md +1 -1
  7. package/okf/corporate_ma_restructuring/slump_sale_50b/circulars/cbic_notification_12_2017_going_concern_exemption.md +1 -1
  8. package/okf/corporate_ma_restructuring/slump_sale_50b/section.json +2 -2
  9. package/okf/direct_tax/special_rates_non_filer_206ab/circulars/cbdt_circular_11_2021_compliance_check.md +1 -1
  10. package/okf/direct_tax/special_rates_non_filer_206ab/section.json +1 -1
  11. package/okf/direct_tax/tcs_goods_206c_1h/circulars/finance_act_2025_sunset_memo.md +1 -1
  12. package/okf/direct_tax/tcs_goods_206c_1h/section.json +3 -3
  13. package/okf/direct_tax/tds_commission_194h/circulars/finance_act_2024_rate_cut_memo.md +1 -1
  14. package/okf/direct_tax/tds_commission_194h/section.json +6 -6
  15. package/okf/direct_tax/tds_contractor_194c/circulars/cbdt_circular_23_2017_gst_unbundling.md +1 -1
  16. package/okf/direct_tax/tds_contractor_194c/section.json +6 -6
  17. package/okf/direct_tax/tds_goods_194q/circulars/cbdt_circular_13_2021_guidelines.md +1 -1
  18. package/okf/direct_tax/tds_goods_194q/circulars/cbdt_circular_23_2017_gst_unbundling.md +1 -1
  19. package/okf/direct_tax/tds_goods_194q/section.json +3 -3
  20. package/okf/direct_tax/tds_goods_393/section.json +3 -3
  21. package/okf/direct_tax/tds_perquisites_194r/circulars/cbdt_circular_12_2022_perquisites.md +1 -1
  22. package/okf/direct_tax/tds_perquisites_194r/section.json +4 -4
  23. package/okf/direct_tax/tds_rent_194i/circulars/cbdt_circular_23_2017_gst_unbundling.md +1 -1
  24. package/okf/direct_tax/tds_rent_194i/section.json +6 -6
  25. package/okf/direct_tax/tds_services_194j/circulars/cbdt_circular_03_2020_fts_rate_cut.md +1 -1
  26. package/okf/direct_tax/tds_services_194j/section.json +3 -3
  27. package/okf/indirect_tax_gst/blocked_itc_17_5/circulars/cbic_circular_172_2022_employer_obligations.md +1 -1
  28. package/okf/indirect_tax_gst/blocked_itc_17_5/section.json +4 -4
  29. package/okf/indirect_tax_gst/composite_mixed_supplies_sec8/circulars/cbic_circular_47_2018_composite_mixed_supplies.md +1 -1
  30. package/okf/indirect_tax_gst/composite_mixed_supplies_sec8/section.json +3 -3
  31. package/okf/indirect_tax_gst/composition_levy_sec10/circulars/cbic_notification_14_2019_composition.md +1 -1
  32. package/okf/indirect_tax_gst/composition_levy_sec10/section.json +2 -2
  33. package/okf/indirect_tax_gst/credit_debit_notes_sec34/circulars/cbic_circular_137_2020_credit_notes.md +1 -1
  34. package/okf/indirect_tax_gst/credit_debit_notes_sec34/section.json +2 -2
  35. package/okf/indirect_tax_gst/e_invoicing_rule48_4/circulars/cbic_notification_10_2023_einvoicing.md +1 -1
  36. package/okf/indirect_tax_gst/e_invoicing_rule48_4/section.json +2 -2
  37. package/okf/indirect_tax_gst/gst_tds_sec51/circulars/cbic_notification_50_2018_gst_tds.md +1 -1
  38. package/okf/indirect_tax_gst/gst_tds_sec51/section.json +6 -6
  39. package/okf/indirect_tax_gst/itc_eligibility_16_2/circulars/cbic_circular_170_2022_itc_reporting.md +1 -1
  40. package/okf/indirect_tax_gst/itc_eligibility_16_2/section.json +4 -4
  41. package/okf/indirect_tax_gst/rcm_liability_sec9_3/circulars/cbic_circular_102_2019_rcm_clarifications.md +1 -1
  42. package/okf/indirect_tax_gst/rcm_liability_sec9_3/section.json +3 -3
  43. package/okf/international_tax/cross_border_remittance_195/circulars/cbdt_circular_03_2022_form10f_electronic.md +1 -1
  44. package/okf/international_tax/cross_border_remittance_195/circulars/supreme_court_engineering_analysis_doctrine.md +1 -1
  45. package/okf/international_tax/cross_border_remittance_195/section.json +3 -3
  46. package/okf/manifest.json +51 -51
  47. package/okf/msme_commercial/delayed_payments_15_16/circulars/msme_om_2021_trader_exclusion.md +1 -1
  48. package/okf/msme_commercial/delayed_payments_15_16/forms/schedule_iii_msme_disclosure.md +1 -1
  49. package/okf/msme_commercial/delayed_payments_15_16/section.json +5 -5
  50. package/okf/msme_commercial/enterprise_classification_sec7/circulars/msme_notification_2119_e_composite_criteria.md +1 -1
  51. package/okf/msme_commercial/enterprise_classification_sec7/section.json +2 -2
  52. package/okf/msme_commercial/mca_msme_1_filing/circulars/mca_order_2019_specified_companies.md +1 -1
  53. package/okf/msme_commercial/mca_msme_1_filing/section.json +2 -2
  54. package/okf/msme_commercial/statutory_disclosures_sec22/circulars/icai_guidance_note_schedule_iii_trade_payables.md +1 -1
  55. package/okf/msme_commercial/statutory_disclosures_sec22/section.json +3 -3
  56. package/okf.publication.json +1 -1
  57. package/package.json +3 -2
  58. package/src/index.js +1162 -2
  59. package/types/index.d.ts +104 -0
package/src/index.js CHANGED
@@ -18,13 +18,14 @@ const __filename = fileURLToPath(import.meta.url);
18
18
  const __dirname = path.dirname(__filename);
19
19
  export const DEFAULT_LOCAL_OKF_DIR = path.resolve(__dirname, '..', 'okf');
20
20
  export const DEFAULT_BASE_URL = 'https://api.mudraforge.in';
21
- export const STATUTORY_CLIENT_VERSION = '2.7.0';
21
+ export const STATUTORY_CLIENT_VERSION = '2.8.1';
22
22
 
23
23
  /**
24
24
  * Pinned official release Merkle root digests.
25
25
  * Prevents unauthorized bundle substitution attacks even if an attacker controls the remote endpoint.
26
26
  */
27
27
  export const PINNED_RELEASE_ROOTS = new Set([
28
+ 'sha256-239641de2bef05917673f693f9ba4d14bd5e137d64554fba5db87e426bc13e14', // v2.8.0 Deep Semantic Citation Merkle Root (24 Sections across 5 Domains)
28
29
  'sha256-6953ce79b63bb2a5809dc928178b4631f08448aa2dcde107dde10f84890bf5cb', // v2.7.0 Edge Sync Bundle Root (24 Sections across 5 Domains)
29
30
  'sha256-ce88c2255df1c30c15bfe184c8117336ebc302c2ac5ef25e96a6957402ae11ae', // v2.6.0 Verified Citation Merkle Root (24 Sections across 5 Domains - 100% Complete AP)
30
31
  'sha256-3f25fc3941d29de5b8befc0ddb24d1bb2632067f00b200680cf784961616424c', // v2.5.0 Verified Citation Merkle Root (18 Sections across 5 Domains)
@@ -256,6 +257,364 @@ export function getLocalManifest(okfDir = DEFAULT_LOCAL_OKF_DIR) {
256
257
  }
257
258
  }
258
259
 
260
+ /**
261
+ * MudraForge Custom CLI Exit Code Conventions
262
+ */
263
+ export const STATUTORY_EXIT_CODES = {
264
+ SUCCESS: 0,
265
+ USAGE_ERROR: 1,
266
+ INTEGRITY_OR_SECURITY_FAILURE: 2,
267
+ STATUTORY_POLICY_FAILURE: 3
268
+ };
269
+
270
+ /**
271
+ * Recursively scans a local OKF directory, verifies file-level SHA-256 hashes against
272
+ * manifest.json (Integrity), validates the Merkle root hash against authorized release roots
273
+ * or cryptographic Ed25519 signatures (Authenticity), and verifies mandatory statutory baselines.
274
+ *
275
+ * @param {string} [okfDir=DEFAULT_LOCAL_OKF_DIR] - Path to local OKF root directory.
276
+ * @param {Object} [options={}]
277
+ * @param {string} [options.publicKey] - Base64 SPKI public key for signature verification.
278
+ * @param {string} [options.expectedRootHash] - Explicit root hash expected by caller.
279
+ * @param {Array<string>} [options.requiredSections] - Baseline provisions required (defaults to MANDATORY_BASELINE_SECTIONS).
280
+ * @param {boolean} [options.allowUnverifiedRoot] - Permitted only in non-production.
281
+ * @returns {{
282
+ * valid: boolean,
283
+ * exitCode: number,
284
+ * okfVersion: string,
285
+ * publishedAt: string,
286
+ * integrity: {
287
+ * valid: boolean,
288
+ * totalFilesChecked: number,
289
+ * mismatches: Array<{ file: string, expected: string, actual: string }>,
290
+ * missingFiles: string[],
291
+ * untrackedFiles: string[],
292
+ * computedRoot: string,
293
+ * recordedRoot: string
294
+ * },
295
+ * authenticity: {
296
+ * valid: boolean,
297
+ * method: string,
298
+ * rootHash: string,
299
+ * isPinned: boolean,
300
+ * isSigned: boolean,
301
+ * keyId: string | null
302
+ * },
303
+ * baselineCheck: {
304
+ * valid: boolean,
305
+ * missingBaseline: string[],
306
+ * baselineSections: string[]
307
+ * }
308
+ * }}
309
+ */
310
+ export function verifyLocalSubstrate(okfDir = DEFAULT_LOCAL_OKF_DIR, options = {}) {
311
+ const resolvedDir = path.resolve(okfDir);
312
+ if (!fs.existsSync(resolvedDir)) {
313
+ return {
314
+ valid: false,
315
+ exitCode: STATUTORY_EXIT_CODES.INTEGRITY_OR_SECURITY_FAILURE,
316
+ error: `[@mudraforge/statutory] Local OKF directory not found at: ${resolvedDir}`,
317
+ integrity: { valid: false, totalFilesChecked: 0, mismatches: [], missingFiles: [], untrackedFiles: [], computedRoot: null, recordedRoot: null },
318
+ authenticity: { valid: false, method: 'NONE', rootHash: null, isPinned: false, isSigned: false, keyId: null },
319
+ baselineCheck: { valid: false, missingBaseline: MANDATORY_BASELINE_SECTIONS, baselineSections: MANDATORY_BASELINE_SECTIONS }
320
+ };
321
+ }
322
+
323
+ const manifestPath = path.join(resolvedDir, 'manifest.json');
324
+ if (!fs.existsSync(manifestPath)) {
325
+ return {
326
+ valid: false,
327
+ exitCode: STATUTORY_EXIT_CODES.INTEGRITY_OR_SECURITY_FAILURE,
328
+ error: `[@mudraforge/statutory] manifest.json missing in OKF directory: ${resolvedDir}`,
329
+ integrity: { valid: false, totalFilesChecked: 0, mismatches: [], missingFiles: [], untrackedFiles: [], computedRoot: null, recordedRoot: null },
330
+ authenticity: { valid: false, method: 'NONE', rootHash: null, isPinned: false, isSigned: false, keyId: null },
331
+ baselineCheck: { valid: false, missingBaseline: MANDATORY_BASELINE_SECTIONS, baselineSections: MANDATORY_BASELINE_SECTIONS }
332
+ };
333
+ }
334
+
335
+ let manifest;
336
+ try {
337
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
338
+ } catch (err) {
339
+ return {
340
+ valid: false,
341
+ exitCode: STATUTORY_EXIT_CODES.INTEGRITY_OR_SECURITY_FAILURE,
342
+ error: `[@mudraforge/statutory] Corrupted manifest.json: ${err.message}`,
343
+ integrity: { valid: false, totalFilesChecked: 0, mismatches: [], missingFiles: [], untrackedFiles: [], computedRoot: null, recordedRoot: null },
344
+ authenticity: { valid: false, method: 'NONE', rootHash: null, isPinned: false, isSigned: false, keyId: null },
345
+ baselineCheck: { valid: false, missingBaseline: MANDATORY_BASELINE_SECTIONS, baselineSections: MANDATORY_BASELINE_SECTIONS }
346
+ };
347
+ }
348
+
349
+ // 1. Gather all files on disk recursively
350
+ const fileList = [];
351
+ function scanDir(dir) {
352
+ if (!fs.existsSync(dir)) return;
353
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
354
+ for (const entry of entries) {
355
+ const full = path.join(dir, entry.name);
356
+ if (entry.isDirectory()) {
357
+ if (!entry.name.startsWith('.') && !entry.name.startsWith('.staging-sync-')) {
358
+ scanDir(full);
359
+ }
360
+ } else if (entry.isFile() && entry.name !== 'manifest.json' && !entry.name.startsWith('.')) {
361
+ fileList.push(full);
362
+ }
363
+ }
364
+ }
365
+ scanDir(resolvedDir);
366
+
367
+ const onDiskFileHashes = {};
368
+ const recordedFileHashes = manifest.files || manifest.fileHashes || {};
369
+ const mismatches = [];
370
+ const seenSections = new Set();
371
+
372
+ for (const fullPath of fileList) {
373
+ const relPath = path.relative(resolvedDir, fullPath).replace(/\\/g, '/');
374
+ let content = fs.readFileSync(fullPath);
375
+ if (fullPath.endsWith('.json') || fullPath.endsWith('.md')) {
376
+ content = Buffer.from(content.toString('utf8').replace(/\r\n/g, '\n'), 'utf8');
377
+ }
378
+ const hash = crypto.createHash('sha256').update(content).digest('hex');
379
+ onDiskFileHashes[relPath] = hash;
380
+
381
+ if (path.basename(fullPath) === 'section.json') {
382
+ try {
383
+ const sData = JSON.parse(content.toString('utf8'));
384
+ if (sData.sectionCode) {
385
+ seenSections.add(sData.sectionCode.toLowerCase().trim());
386
+ }
387
+ } catch (_) {}
388
+ }
389
+
390
+ const expectedHash = recordedFileHashes[relPath];
391
+ if (expectedHash && expectedHash !== hash) {
392
+ mismatches.push({ file: relPath, expected: expectedHash, actual: hash });
393
+ }
394
+ }
395
+
396
+ const missingFiles = [];
397
+ for (const recordedRelPath of Object.keys(recordedFileHashes)) {
398
+ if (!onDiskFileHashes[recordedRelPath]) {
399
+ missingFiles.push(recordedRelPath);
400
+ }
401
+ }
402
+
403
+ const untrackedFiles = [];
404
+ for (const onDiskRelPath of Object.keys(onDiskFileHashes)) {
405
+ if (!recordedFileHashes[onDiskRelPath]) {
406
+ untrackedFiles.push(onDiskRelPath);
407
+ }
408
+ }
409
+
410
+ // Compute deterministic root hash from on-disk file hashes
411
+ const rootHasher = crypto.createHash('sha256');
412
+ for (const [relPath, hash] of Object.entries(onDiskFileHashes).sort(([a], [b]) => a.localeCompare(b))) {
413
+ rootHasher.update(`${relPath}:${hash}\n`);
414
+ }
415
+ const computedRoot = `sha256-${rootHasher.digest('hex')}`;
416
+
417
+ const integrityValid = mismatches.length === 0 &&
418
+ missingFiles.length === 0 &&
419
+ untrackedFiles.length === 0 &&
420
+ computedRoot === manifest.rootHash;
421
+
422
+ const integrity = {
423
+ valid: integrityValid,
424
+ totalFilesChecked: fileList.length,
425
+ mismatches,
426
+ missingFiles,
427
+ untrackedFiles,
428
+ computedRoot,
429
+ recordedRoot: manifest.rootHash
430
+ };
431
+
432
+ // 2. Authenticity Verification
433
+ const isPinned = PINNED_RELEASE_ROOTS.has(manifest.rootHash);
434
+ let isSigned = false;
435
+ let signingKeyId = manifest.keyId || null;
436
+
437
+ if (manifest.signature) {
438
+ const pubKey = options.publicKey || MUDRAFORGE_STATUTORY_ROOT_PUBLIC_KEY;
439
+ isSigned = verifyBundleSignature(manifest, pubKey, options);
440
+ }
441
+
442
+ const matchesExpected = options.expectedRootHash ? (options.expectedRootHash === manifest.rootHash) : false;
443
+ const isAllowedUnverified = !isProductionEnvironment() && Boolean(options.allowUnverifiedRoot);
444
+
445
+ let authenticityValid = false;
446
+ let authMethod = 'UNAUTHENTICATED';
447
+
448
+ if (isSigned) {
449
+ authenticityValid = true;
450
+ authMethod = 'ED25519_DIGITAL_SIGNATURE';
451
+ } else if (isPinned) {
452
+ authenticityValid = true;
453
+ authMethod = 'PINNED_RELEASE_ROOT';
454
+ } else if (matchesExpected) {
455
+ authenticityValid = true;
456
+ authMethod = 'EXPLICIT_EXPECTED_ROOT_PIN';
457
+ } else if (isAllowedUnverified) {
458
+ authenticityValid = true;
459
+ authMethod = 'DEVELOPMENT_UNVERIFIED_BYPASS';
460
+ }
461
+
462
+ const authenticity = {
463
+ valid: authenticityValid,
464
+ method: authMethod,
465
+ rootHash: manifest.rootHash,
466
+ isPinned,
467
+ isSigned,
468
+ keyId: signingKeyId
469
+ };
470
+
471
+ // 3. Baseline Statutory Provisions Check
472
+ const effectiveBaseline = Array.isArray(options.requiredSections)
473
+ ? options.requiredSections
474
+ : MANDATORY_BASELINE_SECTIONS;
475
+
476
+ const manifestSectionSet = new Set((manifest.sections || []).map(s => s.toLowerCase().trim()));
477
+ const missingBaseline = [];
478
+ for (const reqCode of effectiveBaseline) {
479
+ const codeNormalized = reqCode.toLowerCase().trim();
480
+ if (!manifestSectionSet.has(codeNormalized) && !seenSections.has(codeNormalized)) {
481
+ missingBaseline.push(reqCode);
482
+ }
483
+ }
484
+
485
+ const baselineCheck = {
486
+ valid: missingBaseline.length === 0,
487
+ missingBaseline,
488
+ baselineSections: effectiveBaseline
489
+ };
490
+
491
+ // Determine Exit Code based on MudraForge Custom Exit Code Contract:
492
+ // 0 = Success
493
+ // 2 = Cryptographic or file integrity failure
494
+ // 3 = Statutory policy failure
495
+ let exitCode = STATUTORY_EXIT_CODES.SUCCESS;
496
+ if (!integrity.valid || !authenticity.valid) {
497
+ exitCode = STATUTORY_EXIT_CODES.INTEGRITY_OR_SECURITY_FAILURE;
498
+ } else if (!baselineCheck.valid) {
499
+ exitCode = STATUTORY_EXIT_CODES.STATUTORY_POLICY_FAILURE;
500
+ }
501
+
502
+ return {
503
+ valid: exitCode === STATUTORY_EXIT_CODES.SUCCESS,
504
+ exitCode,
505
+ okfVersion: manifest.okfVersion,
506
+ publishedAt: manifest.publishedAt,
507
+ integrity,
508
+ authenticity,
509
+ baselineCheck
510
+ };
511
+ }
512
+
513
+ /**
514
+ * MudraForge Statutory Environment & Substrate Doctor.
515
+ * Diagnoses host runtime environment, local OKF file health, air-gap state,
516
+ * and optional Cloudflare edge connectivity without failing offline verification.
517
+ *
518
+ * @param {Object} [options={}]
519
+ * @param {string} [options.okfDir] - Path to local OKF directory.
520
+ * @param {string} [options.baseUrl] - Base URL for edge endpoint.
521
+ * @param {boolean} [options.offline] - Force offline/air-gapped check (bypasses network).
522
+ * @param {number} [options.timeoutMs=2500] - Latency timeout for edge ping.
523
+ * @returns {Promise<{
524
+ * healthy: boolean,
525
+ * exitCode: number,
526
+ * clientVersion: string,
527
+ * runtime: Object,
528
+ * substrate: Object,
529
+ * edge: Object
530
+ * }>}
531
+ */
532
+ export async function runStatutoryDoctor(options = {}) {
533
+ const okfDir = options.okfDir || DEFAULT_LOCAL_OKF_DIR;
534
+ const baseUrl = options.baseUrl || DEFAULT_BASE_URL;
535
+ const skipNetwork = Boolean(options.offline || options.skipNetwork || process.env.MUDRA_OFFLINE === '1');
536
+
537
+ // 1. Host Runtime Diagnostics
538
+ const nodeVersion = process.version;
539
+ const majorMatch = nodeVersion.match(/^v(\d+)/);
540
+ const majorVersion = majorMatch ? parseInt(majorMatch[1], 10) : 0;
541
+ const runtime = {
542
+ nodeVersion,
543
+ isSupportedNode: majorVersion >= 18,
544
+ platform: process.platform,
545
+ arch: process.arch,
546
+ pid: process.pid,
547
+ environment: process.env.NODE_ENV || 'development',
548
+ isCI: Boolean(process.env.CI === 'true'),
549
+ hasApiKey: Boolean(process.env.MUDRA_STATUTORY_API_KEY || process.env.MUDRA_API_KEY)
550
+ };
551
+
552
+ // 2. Local Substrate Diagnostics
553
+ const substrate = verifyLocalSubstrate(okfDir, options);
554
+
555
+ // 3. Optional Edge Connectivity Diagnostics
556
+ let edge = {
557
+ status: 'SKIPPED_AIR_GAPPED',
558
+ baseUrl,
559
+ latencyMs: null,
560
+ isReachable: null,
561
+ advisory: 'Edge check bypassed via offline/air-gapped mode.'
562
+ };
563
+
564
+ if (!skipNetwork) {
565
+ const start = Date.now();
566
+ try {
567
+ assertSecureEndpoint(baseUrl);
568
+ const controller = new AbortController();
569
+ const timeoutId = setTimeout(() => controller.abort(), options.timeoutMs || 2500);
570
+ const pingUrl = new URL('/api/v1/statutory/manifest.json', baseUrl);
571
+ const res = await fetch(pingUrl, {
572
+ method: 'HEAD',
573
+ signal: controller.signal,
574
+ headers: { 'User-Agent': buildUserAgent() }
575
+ });
576
+ clearTimeout(timeoutId);
577
+ const latencyMs = Date.now() - start;
578
+ edge = {
579
+ status: res.ok ? 'REACHABLE' : `HTTP_${res.status}`,
580
+ baseUrl,
581
+ latencyMs,
582
+ isReachable: res.ok,
583
+ advisory: res.ok ? 'Edge service operational.' : `Edge returned status HTTP ${res.status}.`
584
+ };
585
+ } catch (err) {
586
+ edge = {
587
+ status: 'UNREACHABLE',
588
+ baseUrl,
589
+ latencyMs: null,
590
+ isReachable: false,
591
+ advisory: `Edge endpoint unreachable (${err.message}). Local offline substrate remains 100% operational.`
592
+ };
593
+ }
594
+ }
595
+
596
+ // Determine overall health & exit code
597
+ let exitCode = STATUTORY_EXIT_CODES.SUCCESS;
598
+ let isHealthy = true;
599
+
600
+ if (!runtime.isSupportedNode) {
601
+ isHealthy = false;
602
+ exitCode = STATUTORY_EXIT_CODES.USAGE_ERROR;
603
+ } else if (!substrate.valid) {
604
+ isHealthy = false;
605
+ exitCode = substrate.exitCode;
606
+ }
607
+
608
+ return {
609
+ healthy: isHealthy,
610
+ exitCode,
611
+ clientVersion: STATUTORY_CLIENT_VERSION,
612
+ runtime,
613
+ substrate,
614
+ edge
615
+ };
616
+ }
617
+
259
618
  /**
260
619
  * Safely parse JSON responses from remote endpoints, asserting Content-Type
261
620
  * to prevent uncaught SyntaxErrors when upstream proxies return HTML or text.
@@ -515,7 +874,7 @@ export async function checkUpdates(options = {}) {
515
874
  * @returns {Promise<{ updateAvailable: boolean, currentVersion: string, latestVersion: string } | null>}
516
875
  */
517
876
  export async function checkForPackageUpdate(options = {}) {
518
- if (process.env.NO_UPDATE_NOTIFIER === '1' || process.env.CI === 'true') {
877
+ if (!options.force && (process.env.NO_UPDATE_NOTIFIER === '1' || (process.env.CI === 'true' && !options.registryUrl))) {
519
878
  return null;
520
879
  }
521
880
 
@@ -1288,6 +1647,35 @@ export class StatutoryClient {
1288
1647
  async sync() {
1289
1648
  return await syncOkf({ baseUrl: this.baseUrl, okfDir: this.okfDir });
1290
1649
  }
1650
+
1651
+ /**
1652
+ * Crosswalk a statutory provision across the 1961 Act and 2025 Act regimes
1653
+ * resolving the crystallization date against the earlier of credit or payment.
1654
+ */
1655
+ crosswalk(section, options = {}) {
1656
+ return crosswalkStatute(section, { ...options, okfDir: this.okfDir });
1657
+ }
1658
+
1659
+ /**
1660
+ * Query the statutory regulatory changelog and amendment tracker.
1661
+ */
1662
+ diff(options = {}) {
1663
+ return getStatutoryDiff({ ...options, okfDir: this.okfDir });
1664
+ }
1665
+
1666
+ /**
1667
+ * Cryptographically verify the local OKF substrate authenticity and integrity.
1668
+ */
1669
+ verify(targetDir) {
1670
+ return verifyLocalSubstrate(targetDir || this.okfDir);
1671
+ }
1672
+
1673
+ /**
1674
+ * Run statutory diagnostic doctor.
1675
+ */
1676
+ async doctor(options = {}) {
1677
+ return await runStatutoryDoctor(options);
1678
+ }
1291
1679
  }
1292
1680
 
1293
1681
  /**
@@ -1487,3 +1875,775 @@ export function decodeTaxIdentifier(rawId) {
1487
1875
  };
1488
1876
  }
1489
1877
 
1878
+ /**
1879
+ * ═══════════════════════════════════════════════════════════════════════════
1880
+ * Authoritative Dual-Act Statutory Crosswalk Catalog
1881
+ * Bridges provisions of the Income-tax Act, 1961 (Act No. 43 of 1961) with
1882
+ * the Income-tax Act, 2025 (Act No. 1 of 2025, taking effect 1 April 2026).
1883
+ * ═══════════════════════════════════════════════════════════════════════════
1884
+ */
1885
+ export const STATUTORY_CROSSWALK_CATALOG = [
1886
+ {
1887
+ id: 'CROSSWALK_194Q_393_TABLE8_II',
1888
+ domain: 'direct_tax',
1889
+ predecessor: {
1890
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
1891
+ sectionCode: '194Q',
1892
+ statutorySection: 'Section 194Q',
1893
+ title: 'Deduction of tax at source on payment of a certain sum for purchase of goods',
1894
+ threshold: 'Buyer turnover > ₹10 Cr in preceding FY; annual purchases > ₹50 Lakhs in current FY',
1895
+ standardRate: '0.1%',
1896
+ penaltyRate: '5.0% (Section 206AA inoperative / non-furnished PAN)',
1897
+ challan: 'ITNS 281',
1898
+ returnForm: 'Form 26Q (Quarterly Return of TDS)'
1899
+ },
1900
+ successor: {
1901
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
1902
+ sectionCode: '393',
1903
+ statutorySection: 'Section 393(1), Table 8, Item (ii)',
1904
+ title: 'Deduction of tax at source on payment of a certain sum for purchase of goods',
1905
+ effectiveDate: '2026-04-01',
1906
+ threshold: 'Buyer turnover > ₹10 Cr in preceding FY; annual purchases > ₹50 Lakhs in current FY',
1907
+ standardRate: '0.1%',
1908
+ penaltyRate: '5.0% (Section 397(2) Proviso)',
1909
+ challan: 'Challan under Income-tax Act, 2025',
1910
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
1911
+ },
1912
+ transitionCutoffDate: '2026-04-01',
1913
+ savingsAuthority: {
1914
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
1915
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
1916
+ legalPrinciple: 'Tax withholding liability crystallizes on the earlier of credit to payee account in books or payment disbursement. If the trigger event occurred on or before 31 March 2026, the 1961 Act governs the deduction, remittance, and compliance obligations, notwithstanding subsequent settlement.'
1917
+ },
1918
+ aliases: ['194q', 'sec194q', 'section 194q', '393', 'sec393', 'section 393', '393(1)', '393(1)(table 8)(ii)', 'goods tds', 'purchase of goods']
1919
+ },
1920
+ {
1921
+ id: 'CROSSWALK_194J_393_TABLE6_III',
1922
+ domain: 'direct_tax',
1923
+ predecessor: {
1924
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
1925
+ sectionCode: '194J',
1926
+ statutorySection: 'Section 194J',
1927
+ title: 'Deduction of tax at source on fees for professional or technical services',
1928
+ threshold: '₹30,000 aggregate in financial year per category',
1929
+ standardRate: '2.0% (Technical Services / Royalty / Call Centre) / 10.0% (Professional Fees)',
1930
+ penaltyRate: '20.0% (Section 206AA)',
1931
+ challan: 'ITNS 281',
1932
+ returnForm: 'Form 26Q'
1933
+ },
1934
+ successor: {
1935
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
1936
+ sectionCode: '393',
1937
+ statutorySection: 'Section 393(1), Table 6, Item (iii)',
1938
+ title: 'Deduction of tax at source on fees for professional or technical services',
1939
+ effectiveDate: '2026-04-01',
1940
+ threshold: '₹30,000 aggregate in financial year per category',
1941
+ standardRate: '2.0% (FTS) / 10.0% (Professional)',
1942
+ penaltyRate: '20.0% (Section 397(2))',
1943
+ challan: 'Challan under Income-tax Act, 2025',
1944
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
1945
+ },
1946
+ transitionCutoffDate: '2026-04-01',
1947
+ savingsAuthority: {
1948
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
1949
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
1950
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
1951
+ },
1952
+ aliases: ['194j', 'sec194j', 'section 194j', '393(1)(table 6)', 'technical services tds', 'professional fees tds', 'fts']
1953
+ },
1954
+ {
1955
+ id: 'CROSSWALK_194C_393_TABLE3_I',
1956
+ domain: 'direct_tax',
1957
+ predecessor: {
1958
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
1959
+ sectionCode: '194C',
1960
+ statutorySection: 'Section 194C',
1961
+ title: 'Deduction of tax at source on payments to contractors',
1962
+ threshold: 'Single contract > ₹30,000 or aggregate > ₹1,00,000 in FY',
1963
+ standardRate: '1.0% (Individual/HUF payee) / 2.0% (Company/Firm payee)',
1964
+ penaltyRate: '20.0% (Section 206AA)',
1965
+ challan: 'ITNS 281',
1966
+ returnForm: 'Form 26Q'
1967
+ },
1968
+ successor: {
1969
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
1970
+ sectionCode: '393',
1971
+ statutorySection: 'Section 393(1), Table 3, Item (i)',
1972
+ title: 'Deduction of tax at source on payments to contractors',
1973
+ effectiveDate: '2026-04-01',
1974
+ threshold: 'Single contract > ₹30,000 or aggregate > ₹1,00,000 in FY',
1975
+ standardRate: '1.0% (Individual/HUF) / 2.0% (Company/Firm)',
1976
+ penaltyRate: '20.0% (Section 397(2))',
1977
+ challan: 'Challan under Income-tax Act, 2025',
1978
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
1979
+ },
1980
+ transitionCutoffDate: '2026-04-01',
1981
+ savingsAuthority: {
1982
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
1983
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
1984
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
1985
+ },
1986
+ aliases: ['194c', 'sec194c', 'section 194c', '393(1)(table 3)', 'contractor tds', 'subcontractor tds']
1987
+ },
1988
+ {
1989
+ id: 'CROSSWALK_194I_393_TABLE5_I',
1990
+ domain: 'direct_tax',
1991
+ predecessor: {
1992
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
1993
+ sectionCode: '194I',
1994
+ statutorySection: 'Section 194I',
1995
+ title: 'Deduction of tax at source on rent',
1996
+ threshold: '₹2,40,000 aggregate in financial year',
1997
+ standardRate: '2.0% (Plant & Machinery) / 10.0% (Land & Building)',
1998
+ penaltyRate: '20.0% (Section 206AA)',
1999
+ challan: 'ITNS 281',
2000
+ returnForm: 'Form 26Q'
2001
+ },
2002
+ successor: {
2003
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2004
+ sectionCode: '393',
2005
+ statutorySection: 'Section 393(1), Table 5, Item (i)',
2006
+ title: 'Deduction of tax at source on rent',
2007
+ effectiveDate: '2026-04-01',
2008
+ threshold: '₹2,40,000 aggregate in financial year',
2009
+ standardRate: '2.0% (Plant & Machinery) / 10.0% (Land & Building)',
2010
+ penaltyRate: '20.0% (Section 397(2))',
2011
+ challan: 'Challan under Income-tax Act, 2025',
2012
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
2013
+ },
2014
+ transitionCutoffDate: '2026-04-01',
2015
+ savingsAuthority: {
2016
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2017
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2018
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
2019
+ },
2020
+ aliases: ['194i', 'sec194i', 'section 194i', '393(1)(table 5)', 'rent tds']
2021
+ },
2022
+ {
2023
+ id: 'CROSSWALK_194H_393_TABLE4_I',
2024
+ domain: 'direct_tax',
2025
+ predecessor: {
2026
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2027
+ sectionCode: '194H',
2028
+ statutorySection: 'Section 194H',
2029
+ title: 'Deduction of tax at source on commission or brokerage',
2030
+ threshold: '₹15,000 aggregate in financial year',
2031
+ standardRate: '2.0% (Amended from 5% w.e.f. 01-10-2024 by Finance Act, 2024)',
2032
+ penaltyRate: '20.0% (Section 206AA)',
2033
+ challan: 'ITNS 281',
2034
+ returnForm: 'Form 26Q'
2035
+ },
2036
+ successor: {
2037
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2038
+ sectionCode: '393',
2039
+ statutorySection: 'Section 393(1), Table 4, Item (i)',
2040
+ title: 'Deduction of tax at source on commission or brokerage',
2041
+ effectiveDate: '2026-04-01',
2042
+ threshold: '₹15,000 aggregate in financial year',
2043
+ standardRate: '2.0%',
2044
+ penaltyRate: '20.0% (Section 397(2))',
2045
+ challan: 'Challan under Income-tax Act, 2025',
2046
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
2047
+ },
2048
+ transitionCutoffDate: '2026-04-01',
2049
+ savingsAuthority: {
2050
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2051
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2052
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
2053
+ },
2054
+ aliases: ['194h', 'sec194h', 'section 194h', '393(1)(table 4)', 'commission tds', 'brokerage tds']
2055
+ },
2056
+ {
2057
+ id: 'CROSSWALK_194R_393_TABLE8_12',
2058
+ domain: 'direct_tax',
2059
+ predecessor: {
2060
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2061
+ sectionCode: '194R',
2062
+ statutorySection: 'Section 194R',
2063
+ title: 'Deduction of tax at source on benefit or perquisite in respect of business or profession',
2064
+ threshold: '₹20,000 aggregate value in financial year',
2065
+ standardRate: '10.0%',
2066
+ penaltyRate: '20.0% (Section 206AA)',
2067
+ challan: 'ITNS 281',
2068
+ returnForm: 'Form 26Q'
2069
+ },
2070
+ successor: {
2071
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2072
+ sectionCode: '393',
2073
+ statutorySection: 'Section 393(1), Table 8, Item 12',
2074
+ title: 'Deduction of tax at source on benefit or perquisite in respect of business or profession',
2075
+ effectiveDate: '2026-04-01',
2076
+ threshold: '₹20,000 aggregate value in financial year',
2077
+ standardRate: '10.0%',
2078
+ penaltyRate: '20.0% (Section 397(2))',
2079
+ challan: 'Challan under Income-tax Act, 2025',
2080
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
2081
+ },
2082
+ transitionCutoffDate: '2026-04-01',
2083
+ savingsAuthority: {
2084
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2085
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2086
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
2087
+ },
2088
+ aliases: ['194r', 'sec194r', 'section 194r', 'perquisites tds', 'business benefits tds']
2089
+ },
2090
+ {
2091
+ id: 'CROSSWALK_195_393_2',
2092
+ domain: 'international_tax',
2093
+ predecessor: {
2094
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2095
+ sectionCode: '195',
2096
+ statutorySection: 'Section 195',
2097
+ title: 'Deduction of tax at source on payment of other sums to non-residents',
2098
+ threshold: 'Any sum chargeable under the provisions of this Act (no de minimis threshold)',
2099
+ standardRate: 'Rates in force (or DTAA rate under Section 90(2), whichever is more beneficial)',
2100
+ penaltyRate: '20.0% (Section 206AA subject to DTAA TRC exceptions)',
2101
+ challan: 'ITNS 281',
2102
+ returnForm: 'Form 27Q (Quarterly Return of TDS for Non-Residents)'
2103
+ },
2104
+ successor: {
2105
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2106
+ sectionCode: '393(2)',
2107
+ statutorySection: 'Section 393(2)',
2108
+ title: 'Deduction of tax at source on payment of other sums to non-residents',
2109
+ effectiveDate: '2026-04-01',
2110
+ threshold: 'Any sum chargeable under the provisions of the 2025 Act',
2111
+ standardRate: 'Rates in force or DTAA rate under Section 158(2)',
2112
+ penaltyRate: '20.0% (Section 397(2))',
2113
+ challan: 'Challan under Income-tax Act, 2025',
2114
+ returnForm: 'Non-Resident Tax Withholding Statement (2025 Act Rules)'
2115
+ },
2116
+ transitionCutoffDate: '2026-04-01',
2117
+ savingsAuthority: {
2118
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2119
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2120
+ legalPrinciple: 'Liability crystallizes on earlier of credit or payment. Pre-April 1, 2026 events are saved and governed under the 1961 Act.'
2121
+ },
2122
+ aliases: ['195', 'sec195', 'section 195', '393(2)', 'foreign remittance tds', 'non resident tds']
2123
+ },
2124
+ {
2125
+ id: 'CROSSWALK_206AB_397_3',
2126
+ domain: 'direct_tax',
2127
+ predecessor: {
2128
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2129
+ sectionCode: '206AB',
2130
+ statutorySection: 'Section 206AB',
2131
+ title: 'Special provision for deduction of tax at source for non-filers of income-tax return',
2132
+ threshold: 'Aggregate TDS/TCS >= ₹50,000 in preceding FY and return not filed u/s 139(1)',
2133
+ standardRate: 'Higher of: 2x specified rate, 2x rates in force, or 5.0%',
2134
+ penaltyRate: 'Higher rate applies automatically',
2135
+ challan: 'ITNS 281',
2136
+ returnForm: 'Form 26Q'
2137
+ },
2138
+ successor: {
2139
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2140
+ sectionCode: '397(3)',
2141
+ statutorySection: 'Section 397(3)',
2142
+ title: 'Higher withholding rates for non-filers of income-tax returns',
2143
+ effectiveDate: '2026-04-01',
2144
+ threshold: 'Aggregate TDS/TCS >= ₹50,000 in preceding FY and return not filed within due date',
2145
+ standardRate: 'Higher of: 2x specified rate, 2x rates in force, or 5.0%',
2146
+ penaltyRate: 'Higher rate applies automatically',
2147
+ challan: 'Challan under Income-tax Act, 2025',
2148
+ returnForm: 'Quarterly Statement of Tax Withholding (2025 Act Rules)'
2149
+ },
2150
+ transitionCutoffDate: '2026-04-01',
2151
+ savingsAuthority: {
2152
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2153
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2154
+ legalPrinciple: 'Pre-April 1, 2026 non-filer verifications governed by Section 206AB compliance portal.'
2155
+ },
2156
+ aliases: ['206ab', 'sec206ab', 'section 206ab', '397(3)', 'non filer higher tds']
2157
+ },
2158
+ {
2159
+ id: 'CROSSWALK_206C_1H_SUNSET',
2160
+ domain: 'direct_tax',
2161
+ isOmittedSunset: true,
2162
+ predecessor: {
2163
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2164
+ sectionCode: '206C(1H)',
2165
+ statutorySection: 'Section 206C(1H)',
2166
+ title: 'Collection of tax at source by seller on receipt of consideration for sale of goods',
2167
+ threshold: 'Seller turnover > ₹10 Cr in preceding FY; receipts from buyer > ₹50 Lakhs in FY',
2168
+ standardRate: '0.1%',
2169
+ penaltyRate: '1.0% (Section 206CC non-furnishing of PAN)',
2170
+ challan: 'ITNS 281 (TCS Code 0021/0020)',
2171
+ returnForm: 'Form 27EQ (Quarterly Statement of TCS)'
2172
+ },
2173
+ successor: {
2174
+ act: 'Finance Act, 2025 (Statutory Sunset & Omission)',
2175
+ sectionCode: '206C(1H) [OMITTED]',
2176
+ statutorySection: 'Section 206C(1H) Omitted',
2177
+ title: 'Formally omitted by Finance Act, 2025 effective 1 April 2025 (No successor in 2025 Act)',
2178
+ effectiveDate: '2025-04-01',
2179
+ threshold: 'N/A (Provision repealed; zero seller collection obligation)',
2180
+ standardRate: '0.0%',
2181
+ penaltyRate: '0.0%',
2182
+ challan: 'N/A',
2183
+ returnForm: 'N/A'
2184
+ },
2185
+ transitionCutoffDate: '2025-04-01',
2186
+ savingsAuthority: {
2187
+ statute: 'Finance Act, 2025 Section 206C(1H) Sunset Clause',
2188
+ subsidiary: 'General Clauses Act, 1897 Section 6',
2189
+ legalPrinciple: 'Receipts on or before 31 March 2025 remain assessable under 1961 Act. Receipts on or after 1 April 2025 attract ZERO seller TCS; tax withholding is governed strictly by buyer TDS under Section 194Q / Section 393.'
2190
+ },
2191
+ aliases: ['206c(1h)', 'sec206c(1h)', 'section 206c(1h)', '206c1h', 'seller tcs', 'tcs goods sunset']
2192
+ },
2193
+ {
2194
+ id: 'CROSSWALK_50B_78',
2195
+ domain: 'corporate_ma_restructuring',
2196
+ predecessor: {
2197
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
2198
+ sectionCode: '50B',
2199
+ statutorySection: 'Section 50B',
2200
+ title: 'Special provision for computation of capital gains in case of slump sale',
2201
+ threshold: 'Transfer of one or more undertakings as a result of the sale for a lump sum consideration without values being assigned to individual assets',
2202
+ standardRate: '20.0% (LTCG) / Normal corporate rate (STCG if undertaking held <= 36 months)',
2203
+ penaltyRate: 'N/A',
2204
+ challan: 'Advance Tax / Self-Assessment Tax',
2205
+ returnForm: 'Form 3CEA (Accountant Net Worth & Rule 11UAE Report)'
2206
+ },
2207
+ successor: {
2208
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2209
+ sectionCode: '78',
2210
+ statutorySection: 'Section 78',
2211
+ title: 'Capital gains on slump sale of undertakings',
2212
+ effectiveDate: '2026-04-01',
2213
+ threshold: 'Transfer of one or more undertakings for a lump sum consideration without individual asset values',
2214
+ standardRate: 'Statutory capital gains rates under 2025 Act',
2215
+ penaltyRate: 'N/A',
2216
+ challan: 'Advance Tax / Self-Assessment Tax (2025 Act)',
2217
+ returnForm: 'Accountant Report under 2025 Act Rules'
2218
+ },
2219
+ transitionCutoffDate: '2026-04-01',
2220
+ savingsAuthority: {
2221
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2222
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2223
+ legalPrinciple: 'Slump sales executed prior to 1 April 2026 governed by Section 50B and Rule 11UAE.'
2224
+ },
2225
+ aliases: ['50b', 'sec50b', 'section 50b', '78', 'sec78', 'section 78', 'slump sale']
2226
+ },
2227
+ {
2228
+ id: 'CROSSWALK_MSMED_43BH_37_2_G',
2229
+ domain: 'msme_commercial',
2230
+ predecessor: {
2231
+ act: 'Income-tax Act, 1961 (Act No. 43 of 1961) read with MSMED Act, 2006',
2232
+ sectionCode: 'MSMED-15-16',
2233
+ statutorySection: 'Section 43B(h) read with Sections 15 & 16 of MSMED Act, 2006',
2234
+ title: 'Timely payment to Micro and Small Enterprises and deduction disallowance for delayed payments',
2235
+ threshold: '15 days without written agreement; maximum 45 days under written agreement',
2236
+ standardRate: 'Disallowance of 100% overdue sum; compound interest at 3x RBI Bank Rate',
2237
+ penaltyRate: 'Section 23 MSMED permanent income-tax addback on penal interest',
2238
+ challan: 'N/A (Business Income Adjustment)',
2239
+ returnForm: 'ITR-6 Schedule BP & Tax Audit Form 3CD Clause 22'
2240
+ },
2241
+ successor: {
2242
+ act: 'Income-tax Act, 2025 (Act No. 1 of 2025) read with MSMED Act, 2006',
2243
+ sectionCode: '37(2)(g)',
2244
+ statutorySection: 'Section 37(2)(g) of Income-tax Act, 2025 read with MSMED Act, 2006',
2245
+ title: 'Disallowance of expenditure for delayed payments to Micro and Small Enterprises',
2246
+ effectiveDate: '2026-04-01',
2247
+ threshold: '15 days default / maximum 45 days under written agreement',
2248
+ standardRate: '100% expenditure disallowance in year of incurrence; deductible in year of actual payment',
2249
+ penaltyRate: 'Penal interest disallowed under Section 37(2)(g)',
2250
+ challan: 'N/A',
2251
+ returnForm: 'Corporate Tax Return & Tax Audit Report under 2025 Act Rules'
2252
+ },
2253
+ transitionCutoffDate: '2026-04-01',
2254
+ savingsAuthority: {
2255
+ statute: 'Income-tax Act, 2025 Section 536 (Repeals and Savings)',
2256
+ subsidiary: 'General Clauses Act, 1897 Section 6 (Effect of Repeal)',
2257
+ legalPrinciple: 'Deductions disallowed in FY 2023-24 / 2024-25 / 2025-26 under Section 43B(h) remain eligible for deduction upon actual payment under savings provisions.'
2258
+ },
2259
+ aliases: ['msmed', 'msmed-15-16', '43b(h)', '43bh', 'sec43b(h)', '37(2)(g)', 'msme delayed payments']
2260
+ }
2261
+ ];
2262
+
2263
+ /**
2264
+ * Crosswalk a statutory section between the Income-tax Act, 1961 and Income-tax Act, 2025,
2265
+ * rigorously resolving the applicable governing law based on the earlier of credit or payment.
2266
+ *
2267
+ * @param {string} sectionCode Section code or alias (e.g. '194Q', '393', '194J', '206C(1H)')
2268
+ * @param {Object} [options]
2269
+ * @param {string} [options.creditDate] ISO date string (YYYY-MM-DD) of credit in books
2270
+ * @param {string} [options.paymentDate] ISO date string (YYYY-MM-DD) of payment disbursement
2271
+ * @param {string} [options.asOfDate] ISO date string (YYYY-MM-DD) fallback audit reference date
2272
+ * @param {string} [options.date] Alias for asOfDate
2273
+ * @param {string} [options.okfDir] Local OKF directory path
2274
+ * @returns {Object} Deterministic crosswalk report
2275
+ */
2276
+ export function crosswalkStatute(sectionCode, options = {}) {
2277
+ if (!sectionCode || typeof sectionCode !== 'string' || !sectionCode.trim()) {
2278
+ throw new Error('[@mudraforge/statutory] Section code query must be a non-empty string.');
2279
+ }
2280
+
2281
+ const clean = sectionCode.normalize('NFKC').replace(/[\u200B-\u200D\uFEFF]/g, '').trim().toLowerCase();
2282
+
2283
+ // Match crosswalk entry
2284
+ const entry = STATUTORY_CROSSWALK_CATALOG.find(c => {
2285
+ if (c.predecessor.sectionCode.toLowerCase() === clean) return true;
2286
+ if (c.successor.sectionCode.toLowerCase() === clean) return true;
2287
+ if (c.aliases.includes(clean)) return true;
2288
+ if (c.predecessor.statutorySection.toLowerCase() === clean) return true;
2289
+ if (c.successor.statutorySection.toLowerCase() === clean) return true;
2290
+ return false;
2291
+ });
2292
+
2293
+ if (!entry) {
2294
+ throw new Error(
2295
+ `[@mudraforge/statutory] Section "${sectionCode}" could not be resolved to a known statutory crosswalk provision. ` +
2296
+ `Supported provisions include: 194Q, 194J, 194C, 194I, 194H, 194R, 195, 206AB, 206C(1H), 50B, MSMED-15-16, 393.`
2297
+ );
2298
+ }
2299
+
2300
+ // Validate dates if provided
2301
+ const datePattern = /^\d{4}-\d{2}-\d{2}$/;
2302
+ if (options.creditDate) {
2303
+ const cd = options.creditDate.trim();
2304
+ if (!datePattern.test(cd) || isNaN(new Date(cd).getTime())) {
2305
+ throw new Error(`[@mudraforge/statutory] Invalid creditDate format "${options.creditDate}". Expected ISO 8601 YYYY-MM-DD.`);
2306
+ }
2307
+ }
2308
+ if (options.paymentDate) {
2309
+ const pd = options.paymentDate.trim();
2310
+ if (!datePattern.test(pd) || isNaN(new Date(pd).getTime())) {
2311
+ throw new Error(`[@mudraforge/statutory] Invalid paymentDate format "${options.paymentDate}". Expected ISO 8601 YYYY-MM-DD.`);
2312
+ }
2313
+ }
2314
+ const asOf = options.asOfDate || options.date;
2315
+ if (asOf && asOf !== 'CURRENT_ACTIVE') {
2316
+ const ad = asOf.trim();
2317
+ if (!datePattern.test(ad) || isNaN(new Date(ad).getTime())) {
2318
+ throw new Error(`[@mudraforge/statutory] Invalid date format "${asOf}". Expected ISO 8601 YYYY-MM-DD.`);
2319
+ }
2320
+ }
2321
+
2322
+ const creditDate = options.creditDate ? options.creditDate.trim() : null;
2323
+ const paymentDate = options.paymentDate ? options.paymentDate.trim() : null;
2324
+
2325
+ let triggerDate = null;
2326
+ let triggerReason = '';
2327
+ let triggerDescription = '';
2328
+
2329
+ if (creditDate && paymentDate) {
2330
+ if (creditDate < paymentDate) {
2331
+ triggerDate = creditDate;
2332
+ triggerReason = 'EARLIER_EVENT_CREDIT_IN_BOOKS';
2333
+ triggerDescription = `Credit to payee account in books occurred on ${creditDate}, prior to payment disbursement on ${paymentDate}.`;
2334
+ } else if (paymentDate < creditDate) {
2335
+ triggerDate = paymentDate;
2336
+ triggerReason = 'EARLIER_EVENT_PAYMENT_DISBURSEMENT';
2337
+ triggerDescription = `Payment disbursement occurred on ${paymentDate}, prior to credit in books on ${creditDate}.`;
2338
+ } else {
2339
+ triggerDate = creditDate;
2340
+ triggerReason = 'SIMULTANEOUS_CREDIT_AND_PAYMENT';
2341
+ triggerDescription = `Credit in books and payment disbursement occurred simultaneously on ${creditDate}.`;
2342
+ }
2343
+ } else if (creditDate) {
2344
+ triggerDate = creditDate;
2345
+ triggerReason = 'SPECIFIED_CREDIT_DATE';
2346
+ triggerDescription = `Evaluated against date of credit to payee account in books (${creditDate}).`;
2347
+ } else if (paymentDate) {
2348
+ triggerDate = paymentDate;
2349
+ triggerReason = 'SPECIFIED_PAYMENT_DATE';
2350
+ triggerDescription = `Evaluated against date of payment disbursement (${paymentDate}).`;
2351
+ } else if (asOf && asOf !== 'CURRENT_ACTIVE') {
2352
+ triggerDate = asOf.trim();
2353
+ triggerReason = 'SPECIFIED_AS_OF_DATE';
2354
+ triggerDescription = `Evaluated against explicit reference date (${triggerDate}).`;
2355
+ } else {
2356
+ triggerDate = new Date().toISOString().slice(0, 10);
2357
+ triggerReason = 'DEFAULT_CURRENT_DATE';
2358
+ triggerDescription = `Evaluated against current runtime timestamp (${triggerDate}).`;
2359
+ }
2360
+
2361
+ const cutoff = entry.transitionCutoffDate;
2362
+ let governingAct = '';
2363
+ let applicableSection = '';
2364
+ let temporalStatus = '';
2365
+ let legalAnalysis = '';
2366
+ let savingsApplied = false;
2367
+ let isOmitted = Boolean(entry.isOmittedSunset);
2368
+
2369
+ if (entry.isOmittedSunset) {
2370
+ // e.g. 206C(1H)
2371
+ if (triggerDate < cutoff) {
2372
+ governingAct = entry.predecessor.act;
2373
+ applicableSection = entry.predecessor.statutorySection;
2374
+ temporalStatus = 'GOVERNED_BY_1961_ACT';
2375
+ legalAnalysis = `Trigger event crystallized on ${triggerDate} (prior to 1 April 2025 sunset). Governed by ${entry.predecessor.statutorySection} of the ${entry.predecessor.act}. Seller was obligated to collect 0.1% TCS on receipts exceeding ₹50 Lakhs unless buyer was liable and deducted TDS u/s 194Q.`;
2376
+ } else {
2377
+ governingAct = entry.successor.act;
2378
+ applicableSection = entry.successor.statutorySection;
2379
+ temporalStatus = 'SUNSET_OMITTED';
2380
+ legalAnalysis = `Section 206C(1H) was formally omitted by the Finance Act, 2025 w.e.f. 1 April 2025 to eliminate double-compliance overlap. No seller TCS liability attaches to transactions on or after 1 April 2025. Tax withholding on sale of goods is governed exclusively by buyer TDS under Section 194Q / Section 393.`;
2381
+ }
2382
+ } else {
2383
+ // Standard transition to 2025 Act
2384
+ if (triggerDate < cutoff) {
2385
+ governingAct = entry.predecessor.act;
2386
+ applicableSection = entry.predecessor.statutorySection;
2387
+ temporalStatus = 'GOVERNED_BY_1961_ACT';
2388
+ savingsApplied = true;
2389
+ legalAnalysis = `Because the statutory withholding event crystallized on ${triggerDate} (${triggerReason}), the transaction is governed by ${entry.predecessor.act} ${entry.predecessor.statutorySection}. Under ${entry.savingsAuthority.statute} read with ${entry.savingsAuthority.subsidiary}, pre-April 1, 2026 liabilities are preserved and enforced under the 1961 Act regime (Challan ${entry.predecessor.challan}, ${entry.predecessor.returnForm}), even if remittance or payment settlement occurs on or after 1 April 2026.`;
2390
+ } else {
2391
+ governingAct = entry.successor.act;
2392
+ applicableSection = entry.successor.statutorySection;
2393
+ temporalStatus = 'GOVERNED_BY_2025_ACT';
2394
+ savingsApplied = false;
2395
+ legalAnalysis = `The trigger event crystallized on ${triggerDate} (on or after 1 April 2026). The transaction is governed by the successor provision ${entry.successor.statutorySection} of the ${entry.successor.act}. This provision directly supersedes ${entry.predecessor.statutorySection} of the 1961 Act.`;
2396
+ }
2397
+ }
2398
+
2399
+ return {
2400
+ status: 'SUCCESS',
2401
+ isValid: true,
2402
+ querySection: sectionCode,
2403
+ triggerDate,
2404
+ triggerReason,
2405
+ triggerDescription,
2406
+ creditDate,
2407
+ paymentDate,
2408
+ transitionCutoffDate: cutoff,
2409
+ governingAct,
2410
+ applicableSection,
2411
+ temporalStatus,
2412
+ savingsApplied,
2413
+ isOmittedSunset: isOmitted,
2414
+ crosswalk: {
2415
+ id: entry.id,
2416
+ domain: entry.domain,
2417
+ predecessor: entry.predecessor,
2418
+ successor: entry.successor,
2419
+ savingsAuthority: entry.savingsAuthority
2420
+ },
2421
+ legalAnalysis
2422
+ };
2423
+ }
2424
+
2425
+ /**
2426
+ * ═══════════════════════════════════════════════════════════════════════════
2427
+ * Authoritative Semantic Regulatory Amendments Changelog
2428
+ * Curated timeline of major legislative, regulatory, and procedural amendments.
2429
+ * ═══════════════════════════════════════════════════════════════════════════
2430
+ */
2431
+ export const STATUTORY_AMENDMENTS_CHANGELOG = [
2432
+ {
2433
+ id: 'DIFF-2021-05-24-50B',
2434
+ sectionCode: '50B',
2435
+ domain: 'corporate_ma_restructuring',
2436
+ title: 'Capital Gains on Slump Sale — Rule 11UAE Fair Market Value Mandate',
2437
+ effectiveDate: '2021-05-24',
2438
+ amendmentType: 'VALUATION_MANDATE',
2439
+ statute: 'Income-tax Act, 1961',
2440
+ legalAuthority: 'Finance Act, 2021 & CBDT Notification No. 68/2021 (Rule 11UAE)',
2441
+ previousValue: 'Book value of undertaking accepted as Net Worth',
2442
+ amendedValue: 'Deemed full value of consideration determined as higher of FMV1 (market value of transferred capital assets) or FMV2 (market value of consideration received)',
2443
+ changeSummary: 'CBDT notified Rule 11UAE prescribing mandatory fair market value computation for slump sale undertakings to prevent tax avoidance via undervalued asset transfers.',
2444
+ statutoryImpact: 'Mandatory Form 3CEA report by an Accountant verifying net worth and Rule 11UAE calculation.',
2445
+ aliases: ['50b', 'slump sale']
2446
+ },
2447
+ {
2448
+ id: 'DIFF-2021-07-01-194Q',
2449
+ sectionCode: '194Q',
2450
+ domain: 'direct_tax',
2451
+ title: 'Introduction of TDS on Purchase of Goods',
2452
+ effectiveDate: '2021-07-01',
2453
+ amendmentType: 'NEW_WITHHOLDING_LEVY',
2454
+ statute: 'Income-tax Act, 1961',
2455
+ legalAuthority: 'Finance Act, 2021 & CBDT Circular No. 13 of 2021',
2456
+ previousValue: 'No withholding tax by buyers on purchase of goods',
2457
+ amendedValue: '0.1% TDS on aggregate purchases exceeding ₹50 Lakhs where buyer turnover > ₹10 Cr',
2458
+ changeSummary: 'Mandated 0.1% TDS on purchase of goods exceeding ₹50 Lakhs in a financial year at earlier of credit or payment.',
2459
+ statutoryImpact: 'Buyer TDS overrides seller TCS u/s 206C(1H). Requires PAN validation; 5% penalty rate for inoperative PAN.',
2460
+ aliases: ['194q', 'goods tds']
2461
+ },
2462
+ {
2463
+ id: 'DIFF-2022-01-01-16_2',
2464
+ sectionCode: '16(2)',
2465
+ domain: 'indirect_tax_gst',
2466
+ title: 'Introduction of Section 16(2)(aa) — Mandatory Invoice Upload Matching',
2467
+ effectiveDate: '2022-01-01',
2468
+ amendmentType: 'ITC_RESTRICTION',
2469
+ statute: 'Central Goods and Services Tax Act, 2017',
2470
+ legalAuthority: 'Finance Act, 2021 (Notified w.e.f. 1 January 2022) & Notification No. 39/2021-CT',
2471
+ previousValue: 'Provisional ITC permitted under Rule 36(4) with 5% provisional cushion',
2472
+ amendedValue: 'Zero provisional ITC; invoice details must be furnished by supplier in GSTR-1 and communicated to recipient in GSTR-2B',
2473
+ changeSummary: 'Sub-clause (aa) inserted into Section 16(2), making supplier filing in GSTR-1 and reflection in GSTR-2B an absolute statutory precondition for claiming input tax credit.',
2474
+ statutoryImpact: 'Eliminates provisional credit; recipients cannot take credit unless invoice appears in auto-populated GSTR-2B.',
2475
+ aliases: ['16(2)', '16_2', 'itc eligibility', 'gstr 2b']
2476
+ },
2477
+ {
2478
+ id: 'DIFF-2022-04-01-206AB',
2479
+ sectionCode: '206AB',
2480
+ domain: 'direct_tax',
2481
+ title: 'Relaxation of Higher TDS Non-Filer Lookback Period',
2482
+ effectiveDate: '2022-04-01',
2483
+ amendmentType: 'COMPLIANCE_ADJUSTMENT',
2484
+ statute: 'Income-tax Act, 1961',
2485
+ legalAuthority: 'Finance Act, 2022',
2486
+ previousValue: '2 preceding financial years non-filing requirement',
2487
+ amendedValue: '1 preceding financial year non-filing requirement with aggregate TDS/TCS >= ₹50,000',
2488
+ changeSummary: 'Reduced the non-filing lookback threshold under Section 206AB from 2 years to 1 year preceding the relevant financial year.',
2489
+ statutoryImpact: 'Payers must verify non-filer status using the Compliance Check functionality on the Income Tax Portal.',
2490
+ aliases: ['206ab', 'non filer']
2491
+ },
2492
+ {
2493
+ id: 'DIFF-2022-07-01-194R',
2494
+ sectionCode: '194R',
2495
+ domain: 'direct_tax',
2496
+ title: 'Introduction of TDS on Business Perquisites and Benefits',
2497
+ effectiveDate: '2022-07-01',
2498
+ amendmentType: 'NEW_WITHHOLDING_LEVY',
2499
+ statute: 'Income-tax Act, 1961',
2500
+ legalAuthority: 'Finance Act, 2022 & CBDT Circular No. 12 of 2022',
2501
+ previousValue: 'No withholding tax on business gifts, dealer incentives, or perquisites',
2502
+ amendedValue: '10% TDS on value or aggregate value of benefits/perquisites exceeding ₹20,000 in FY',
2503
+ changeSummary: 'Enacted Section 194R imposing 10% withholding tax on any benefit or perquisite arising from business or exercise of profession.',
2504
+ statutoryImpact: 'Applies to non-cash benefits; deductor must ensure tax is paid prior to releasing benefit in kind.',
2505
+ aliases: ['194r', 'perquisites']
2506
+ },
2507
+ {
2508
+ id: 'DIFF-2022-07-16-195',
2509
+ sectionCode: '195',
2510
+ domain: 'international_tax',
2511
+ title: 'Mandatory Electronic Filing of Form 10F for Foreign Remittances',
2512
+ effectiveDate: '2022-07-16',
2513
+ amendmentType: 'PROCEDURAL_DIGITIZATION',
2514
+ statute: 'Income-tax Act, 1961',
2515
+ legalAuthority: 'CBDT Notification No. 03/2022',
2516
+ previousValue: 'Manual physical signed paper Form 10F accepted for Treaty relief u/s 90(2)',
2517
+ amendedValue: 'Mandatory electronic submission of Form 10F on the Income Tax e-filing portal',
2518
+ changeSummary: 'CBDT mandated electronic filing of Form 10F for non-resident payees claiming DTAA benefits where TRC lacks specific details.',
2519
+ statutoryImpact: 'Foreign payees must obtain PAN or use non-resident portal credentials to generate electronic Form 10F.',
2520
+ aliases: ['195', 'form 10f', 'foreign remittance']
2521
+ },
2522
+ {
2523
+ id: 'DIFF-2023-08-01-RULE48_4',
2524
+ sectionCode: 'RULE-48(4)',
2525
+ domain: 'indirect_tax_gst',
2526
+ title: 'E-Invoicing Turnover Threshold Lowered to ₹5 Crore',
2527
+ effectiveDate: '2023-08-01',
2528
+ amendmentType: 'THRESHOLD_LOWERING',
2529
+ statute: 'Central Goods and Services Tax Rules, 2017',
2530
+ legalAuthority: 'CBIC Notification No. 10/2023-Central Tax',
2531
+ previousValue: '₹10 Crore aggregate turnover threshold',
2532
+ amendedValue: '₹5 Crore aggregate turnover threshold in any preceding FY from 2017-18 onward',
2533
+ changeSummary: 'Lowered the mandatory e-invoicing threshold under Rule 48(4) from ₹10 Crore to ₹5 Crore.',
2534
+ statutoryImpact: 'Invoices issued without 64-character IRN and signed QR code are deemed void under Rule 48(5); buyer ITC blocked.',
2535
+ aliases: ['rule-48(4)', 'rule 48(4)', 'einvoicing', 'irn']
2536
+ },
2537
+ {
2538
+ id: 'DIFF-2024-04-01-MSMED_43B_H',
2539
+ sectionCode: 'MSMED-15-16',
2540
+ domain: 'msme_commercial',
2541
+ title: 'Enforcement of Section 43B(h) — Timely Payment to Micro & Small Enterprises',
2542
+ effectiveDate: '2024-04-01',
2543
+ amendmentType: 'DISALLOWANCE_ENFORCEMENT',
2544
+ statute: 'Income-tax Act, 1961',
2545
+ legalAuthority: 'Finance Act, 2023',
2546
+ previousValue: 'Deduction allowable on accrual basis if paid before tax return filing date u/s 139(1)',
2547
+ amendedValue: 'Proviso to Section 43B removed for MSE dues; deduction disallowed in FY unless paid within Section 15 MSMED timeline (15 or 45 days)',
2548
+ changeSummary: 'Enacted clause (h) in Section 43B disallowing deductions for sums payable to Micro and Small Enterprises if not paid within MSMED Section 15 timelines.',
2549
+ statutoryImpact: 'Overdue amounts added back to taxable income; allowable only in year of actual payment. Compound interest u/s 16 is permanently disallowed u/s 23.',
2550
+ aliases: ['msmed', 'msmed-15-16', '43b(h)', '43bh']
2551
+ },
2552
+ {
2553
+ id: 'DIFF-2024-10-01-194H',
2554
+ sectionCode: '194H',
2555
+ domain: 'direct_tax',
2556
+ title: 'Section 194H Commission & Brokerage Rate Cut from 5% to 2%',
2557
+ effectiveDate: '2024-10-01',
2558
+ amendmentType: 'RATE_REDUCTION',
2559
+ statute: 'Income-tax Act, 1961',
2560
+ legalAuthority: 'Finance (No. 2) Act, 2024',
2561
+ previousValue: '5.0% TDS',
2562
+ amendedValue: '2.0% TDS',
2563
+ changeSummary: 'Reduced statutory withholding tax rate on commission and brokerage payments from 5% to 2% to improve working capital for agents and distributors.',
2564
+ statutoryImpact: 'All credits or payments on or after 1 October 2024 attract 2% TDS. Cumulative ₹15,000 threshold remains unchanged.',
2565
+ aliases: ['194h', 'commission tds', 'brokerage tds']
2566
+ },
2567
+ {
2568
+ id: 'DIFF-2025-04-01-206C_1H',
2569
+ sectionCode: '206C(1H)',
2570
+ domain: 'direct_tax',
2571
+ title: 'Statutory Sunset & Formal Omission of Section 206C(1H) Seller TCS',
2572
+ effectiveDate: '2025-04-01',
2573
+ amendmentType: 'STATUTORY_OMISSION',
2574
+ statute: 'Income-tax Act, 1961',
2575
+ legalAuthority: 'Finance Act, 2025',
2576
+ previousValue: '0.1% TCS on sale of goods receipts exceeding ₹50 Lakhs',
2577
+ amendedValue: 'OMITTED (Section 206C(1H) repealed; 0% TCS / No collection obligation)',
2578
+ changeSummary: 'Finance Act, 2025 formally omitted Section 206C(1H) effective 1 April 2025 to eliminate regulatory overlap with buyer TDS under Section 194Q.',
2579
+ statutoryImpact: 'Sellers no longer collect TCS on sale of goods. Tax withholding attaches solely through Section 194Q buyer TDS where buyer criteria are met.',
2580
+ aliases: ['206c(1h)', 'tcs goods', 'seller tcs']
2581
+ },
2582
+ {
2583
+ id: 'DIFF-2026-04-01-ITA2025',
2584
+ sectionCode: '393',
2585
+ domain: 'direct_tax',
2586
+ title: 'Enactment of Income-tax Act, 2025 — Unified Table-Based Withholding Regime',
2587
+ effectiveDate: '2026-04-01',
2588
+ amendmentType: 'ACT_TRANSITION',
2589
+ statute: 'Income-tax Act, 2025 (Act No. 1 of 2025)',
2590
+ legalAuthority: 'Parliament of India / Income-tax Act, 2025',
2591
+ previousValue: 'Income-tax Act, 1961 (Scattered Chapter XVII-B sections: 194C, 194J, 194Q, 194I, 194H, 194R, 195)',
2592
+ amendedValue: 'Income-tax Act, 2025 Section 393 Tables (Table 3: Contractors, Table 4: Commission, Table 5: Rent, Table 6: Professional/FTS, Table 8: Goods/Perquisites)',
2593
+ changeSummary: 'The Income-tax Act, 2025 consolidates and restructures direct tax withholding into unified schedules under Section 393, repealing the 1961 Act subject to Section 536 savings.',
2594
+ statutoryImpact: 'Withholding liabilities on or after 1 April 2026 are governed by 2025 Act section references and procedural compliance.',
2595
+ aliases: ['393', 'income tax act 2025', '2025 act']
2596
+ }
2597
+ ];
2598
+
2599
+ /**
2600
+ * Retrieve statutory amendments and regulatory diffs filtered by section, date, and domain.
2601
+ *
2602
+ * @param {Object} [options]
2603
+ * @param {string} [options.section] Filter by section code or alias (e.g. '194H', '206C(1H)', '16(2)')
2604
+ * @param {string} [options.since] ISO date string (YYYY-MM-DD); returns amendments taking effect on or after this date
2605
+ * @param {string} [options.domain] Filter by statutory domain (e.g. 'direct_tax', 'indirect_tax_gst')
2606
+ * @param {string} [options.okfDir] Local OKF directory path
2607
+ * @returns {Object} Deterministic diff report
2608
+ */
2609
+ export function getStatutoryDiff(options = {}) {
2610
+ const since = options.since ? options.since.trim() : null;
2611
+ if (since) {
2612
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(since) || isNaN(new Date(since).getTime())) {
2613
+ throw new Error(`[@mudraforge/statutory] Invalid since date format "${options.since}". Expected ISO 8601 YYYY-MM-DD.`);
2614
+ }
2615
+ }
2616
+
2617
+ const sectionFilter = options.section ? options.section.trim().toLowerCase() : null;
2618
+ const domainFilter = options.domain ? options.domain.trim().toLowerCase() : null;
2619
+
2620
+ let matched = STATUTORY_AMENDMENTS_CHANGELOG.filter(item => {
2621
+ if (since && item.effectiveDate < since) {
2622
+ return false;
2623
+ }
2624
+ if (domainFilter && item.domain.toLowerCase() !== domainFilter) {
2625
+ return false;
2626
+ }
2627
+ if (sectionFilter) {
2628
+ const matchesCode = item.sectionCode.toLowerCase() === sectionFilter;
2629
+ const matchesAlias = item.aliases && item.aliases.some(a => a.toLowerCase() === sectionFilter);
2630
+ if (!matchesCode && !matchesAlias) {
2631
+ return false;
2632
+ }
2633
+ }
2634
+ return true;
2635
+ });
2636
+
2637
+ matched.sort((a, b) => a.effectiveDate.localeCompare(b.effectiveDate));
2638
+
2639
+ return {
2640
+ status: 'SUCCESS',
2641
+ filterSection: options.section || null,
2642
+ filterSince: since,
2643
+ filterDomain: options.domain || null,
2644
+ totalAmendments: matched.length,
2645
+ amendments: matched
2646
+ };
2647
+ }
2648
+
2649
+