@mudraforge/statutory 2.13.0 → 2.14.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +6 -0
  3. package/package.json +1 -4
  4. package/src/calculators.js +139 -3
  5. package/src/constants.js +1 -1
  6. package/types/index.d.ts +95 -0
  7. package/conformance/README.md +0 -54
  8. package/conformance/fixtures/invalid_malformed_temporal_dates.json +0 -10
  9. package/conformance/fixtures/invalid_missing_effective_period.json +0 -6
  10. package/conformance/fixtures/invalid_missing_section_code.json +0 -9
  11. package/conformance/fixtures/valid_circular_frontmatter.md +0 -16
  12. package/conformance/fixtures/valid_full_section.json +0 -35
  13. package/conformance/fixtures/valid_minimal_section.json +0 -11
  14. package/conformance/test_conformance.js +0 -129
  15. package/docs/ACCOUNTING_ASSURANCE_AUDIT_DOSSIER.md +0 -276
  16. package/docs/ADESHA_ASSURANCE_OVERVIEW.md +0 -166
  17. package/docs/ARCHITECTURE.md +0 -773
  18. package/docs/ARCHITECTURE_DECISIONS.md +0 -158
  19. package/docs/CASE_01_FORM_TEXT.txt +0 -48
  20. package/docs/CATALOG.md +0 -1901
  21. package/docs/CA_PILOT_BENCHMARK_CASES.md +0 -282
  22. package/docs/CORPUS_QUALITY.md +0 -106
  23. package/docs/DATA_LICENSING.md +0 -73
  24. package/docs/DOMAIN_BREADTH_AND_DEPTH_COMPARISON.md +0 -209
  25. package/docs/ENTERPRISE_GOVERNANCE.md +0 -160
  26. package/docs/OKF_MAINTENANCE_SOP.md +0 -286
  27. package/docs/PILOT_EVALUATION_GUIDE.md +0 -215
  28. package/docs/PROVENANCE.md +0 -33
  29. package/docs/VERIFICATION.md +0 -106
  30. package/examples/README.md +0 -56
  31. package/examples/invoice_deduction_trace.json +0 -149
  32. package/examples/simulate_end_to_end_trace.js +0 -106
package/types/index.d.ts CHANGED
@@ -194,33 +194,89 @@ export declare class StatutoryClient {
194
194
  diff(options?: StatutoryDiffOptions): StatutoryDiffResult;
195
195
  verify(targetDir?: string): any;
196
196
  doctor(options?: any): Promise<any>;
197
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
198
+
197
199
  calculateCorporateTaxMatVsConcessional(payload?: any): any;
200
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
201
+
198
202
  evaluatePlaceOfSupplyAndLut(payload?: any): any;
203
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
204
+
199
205
  calculateTransferPricingAlp(payload?: any): any;
206
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
207
+
200
208
  evaluateSection79LossContinuity(payload?: any): any;
209
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
210
+
201
211
  revalueForeignExchangeIndAs109(payload?: any): any;
212
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
213
+
202
214
  calculateBusinessCombinationsIndAs103(payload?: any): any;
215
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
216
+
203
217
  calculateImpairmentIndAs36(payload?: any): any;
218
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
219
+
204
220
  calculateRevenueAllocationIndAs115(payload?: any): any;
221
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
222
+
205
223
  calculateLeaseAmortizationIndAs116(payload?: any): any;
224
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
225
+
206
226
  calculateExpectedCreditLossIndAs109(payload?: any): any;
227
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
228
+
207
229
  calculateEsopAmortizationIndAs102(payload?: any): any;
230
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
231
+
208
232
  calculateDeferredTaxIndAs12(payload?: any): any;
233
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
234
+
209
235
  evaluateProvisionsAndContingenciesIndAs37(payload?: any): any;
236
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
237
+
210
238
  calculateForeignCurrencyIndAs21(payload?: any): any;
239
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
240
+
211
241
  evaluateSection194A(payload?: any): any;
242
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
243
+
212
244
  evaluateSection194O(payload?: any): any;
245
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
246
+
213
247
  evaluateSection194IA(payload?: any): any;
248
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
249
+
214
250
  evaluateSection194IB(payload?: any): any;
251
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
252
+
215
253
  evaluateSection194M(payload?: any): any;
254
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
255
+
216
256
  evaluateSection194R(payload?: any): any;
257
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
258
+
217
259
  evaluateSection43B(payload?: any): any;
260
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
261
+
218
262
  evaluateSection281(payload?: any): any;
263
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
264
+
219
265
  calculateItcReversalRule37(payload?: any): any;
266
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
267
+
220
268
  calculateRule37ASupplierNonFilingReversal(payload?: any): any;
269
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
270
+
221
271
  calculateRule42InputServiceReversal(payload?: any): any;
272
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
273
+
222
274
  reconcileRule42AnnualTrueUp(payload?: any): any;
275
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
276
+
223
277
  calculateRule43CapitalGoodsReversal(payload?: any): any;
278
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
279
+
224
280
  calculateGstItcApportionmentRule42_43(payload?: any): any;
225
281
  parseTallyDaybookCsv(csvText: string): DaybookVoucherInput[];
226
282
  parseTallyDaybookXml(xmlText: string): DaybookVoucherInput[];
@@ -418,44 +474,83 @@ export declare function fromIntegerPaise(paise: number): number;
418
474
  export declare function roundToPaisa(amount: number | string): number;
419
475
  export declare function roundSection288B(amount: number | string): number;
420
476
  export declare function cleanNumericAmount(val: any, options?: CleanNumericAmountOptions): number;
477
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
421
478
  export declare function calculateCorporateTaxMatVsConcessional(payload?: any): any;
479
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
422
480
  export declare function evaluatePlaceOfSupplyAndLut(payload?: any): any;
481
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
423
482
  export declare function calculateTransferPricingAlp(payload?: any): any;
483
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
424
484
  export declare function evaluateSection79LossContinuity(payload?: any): any;
485
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
425
486
  export declare function evaluateSection281TaxClearance(payload: any): any;
487
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
426
488
  export declare function revalueForeignExchangeIndAs109(payload?: any): any;
489
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
427
490
  export declare function calculateBusinessCombinationsIndAs103(payload?: any): any;
491
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
428
492
  export declare function calculateImpairmentIndAs36(payload?: any): any;
493
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
429
494
  export declare function calculateRevenueAllocationIndAs115(payload?: any): any;
495
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
430
496
  export declare function calculateLeaseAmortizationIndAs116(payload?: any): any;
497
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
431
498
  export declare function calculateExpectedCreditLossIndAs109(payload?: any): any;
499
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
432
500
  export declare function calculateEsopAmortizationIndAs102(payload?: any): any;
501
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
433
502
  export declare function calculateDeferredTaxIndAs12(payload?: any): any;
503
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
434
504
  export declare function evaluateProvisionsAndContingenciesIndAs37(payload?: any): any;
505
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
435
506
  export declare function calculateForeignCurrencyIndAs21(payload?: any): any;
507
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
436
508
  export declare function resolveMissingPanPenaltyFloor(params?: any): any;
509
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
437
510
  export declare function evaluateSection194Q(payload: any): any;
511
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
438
512
  export declare function evaluateSection194C(payload: any): any;
513
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
439
514
  export declare function evaluateSection194H(payload: any): any;
515
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
440
516
  export declare function evaluateSection194I(payload: any): any;
517
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
441
518
  export declare function evaluateSection194J(payload: any): any;
519
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
442
520
  export declare function evaluateSection194A(payload: any): any;
521
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
443
522
  export declare function evaluateSection194O(payload: any): any;
523
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
444
524
  export declare function evaluateSection194IA(payload: any): any;
525
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
445
526
  export declare function evaluateSection194IB(payload: any): any;
527
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
446
528
  export declare function evaluateSection194M(payload: any): any;
529
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
447
530
  export declare function evaluateSection194R(payload: any): any;
531
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
448
532
  export declare function evaluateSection43BStatutoryPayables(payload: any): any;
533
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
449
534
  export declare function calculateItcReversalRule37(payload: any): any;
535
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
450
536
  export declare function calculateRule37ASupplierNonFilingReversal(payload: any): any;
537
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
451
538
  export declare function evaluateSection206C1H(payload: any): any;
539
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
452
540
  export declare function evaluateSection43bH(payload: any): any;
541
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
453
542
  export declare function calculateMsmeCompoundInterest(payload: any): any;
543
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
454
544
  export declare function calculateAdvanceTaxAndInterest(payload: any): any;
545
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
455
546
  export declare function calculateRule42InputServiceReversal(payload: any): any;
547
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
456
548
  export declare function reconcileRule42AnnualTrueUp(payload: any): any;
549
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
457
550
  export declare function calculateRule43CapitalGoodsReversal(payload: any): any;
551
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
458
552
  export declare function calculateGstItcApportionmentRule42_43(payload: any): any;
553
+ /** @deprecated Use evaluateStatutoryAst from '@mudraforge/statutory/usvm' instead. */
459
554
  export declare function evaluateCrossBorderRemittance(payload: any): any;
460
555
  export declare function getRbiBankRateForDate(dateString?: string): number;
461
556
  export declare const DEFAULT_RBI_BANK_RATE: number;
@@ -1,54 +0,0 @@
1
- # OKF-Statutory Vendor-Neutral Conformance Specification
2
-
3
- > **Version**: 0.2.0
4
- > **Status**: Institutional Normative Standard
5
- > **Target Audience**: Authors of OKF parsers and AI agents in Python, Go, Rust, Ruby, and TypeScript.
6
-
7
- ---
8
-
9
- ## 1. Purpose
10
-
11
- The `@mudraforge/statutory` open knowledge substrate publishes machine-readable legal provisions designed to be consumed across multiple languages, agent frameworks, and database backends without requiring the MudraForge JavaScript SDK or proprietary runtime.
12
-
13
- This directory contains standalone, vendor-neutral test fixtures and assertions that any conforming parser must satisfy.
14
-
15
- ---
16
-
17
- ## 2. Conformance Criteria
18
-
19
- A conforming OKF parser must guarantee:
20
-
21
- 1. **Mandatory Identity Fields**:
22
- - Every `section.json` must provide `sectionCode` (string), `statutorySection` (string), and `governingStatute` (string).
23
- 2. **Deterministic Temporal Evaluation**:
24
- - `effectivePeriod` must provide explicit `validFrom` (format `YYYY-MM-DD`) and `validTo` (`YYYY-MM-DD` or literal string `"OPEN_ENDED"`).
25
- - Inverted timelines (`validTo < validFrom`) must be rejected at parse time.
26
- 3. **Dual-Act Mapping Preservation**:
27
- - If a provision transitions to a successor statute (such as the *Income-tax Act, 2025* succeeding the *Income-tax Act, 1961*), the parser must preserve `successorStatute` and `successorSection` without loss.
28
- 4. **Google OKF Frontmatter Compatibility**:
29
- - Administrative circulars (`circulars/*.md`) must declare standard YAML frontmatter bounded by `---` containing `id`, `title`, `authority`, `date`, `valid_from`, and `temporal_status`.
30
-
31
- ---
32
-
33
- ## 3. Test Fixtures
34
-
35
- | Fixture | Expected Behavior | Description |
36
- | :--- | :--- | :--- |
37
- | [`valid_minimal_section.json`](./fixtures/valid_minimal_section.json) | **ACCEPT** | Minimal valid section schema with temporal bounds. |
38
- | [`valid_full_section.json`](./fixtures/valid_full_section.json) | **ACCEPT** | Complete section schema with thresholds, citations, and checklists. |
39
- | [`invalid_missing_section_code.json`](./fixtures/invalid_missing_section_code.json) | **REJECT** | Schema missing the required `sectionCode` field. |
40
- | [`invalid_missing_effective_period.json`](./fixtures/invalid_missing_effective_period.json) | **REJECT** | Schema missing `effectivePeriod`. |
41
- | [`invalid_malformed_temporal_dates.json`](./fixtures/invalid_malformed_temporal_dates.json) | **REJECT** | Inverted temporal boundary (`validTo < validFrom`). |
42
- | [`valid_circular_frontmatter.md`](./fixtures/valid_circular_frontmatter.md) | **ACCEPT** | Google OKF YAML frontmatter with circular metadata. |
43
-
44
- ---
45
-
46
- ## 4. Running the Conformance Test
47
-
48
- To run the reference conformance runner in Node.js:
49
-
50
- ```bash
51
- node conformance/test_conformance.js
52
- ```
53
-
54
- Third-party implementations in other languages should mirror these exact assertions against the JSON/Markdown fixtures in `fixtures/`.
@@ -1,10 +0,0 @@
1
- {
2
- "sectionCode": "CONF_ERR_INVERTED_TIMELINE",
3
- "statutorySection": "Section 500",
4
- "governingStatute": "Paradox Act, 2024",
5
- "effectivePeriod": {
6
- "validFrom": "2026-04-01",
7
- "validTo": "2024-03-31"
8
- },
9
- "tier": "PUBLIC_COMMONS_FREE"
10
- }
@@ -1,6 +0,0 @@
1
- {
2
- "sectionCode": "CONF_ERR_NO_DATES",
3
- "statutorySection": "Section 404",
4
- "governingStatute": "Undated Act, 2024",
5
- "tier": "PUBLIC_COMMONS_FREE"
6
- }
@@ -1,9 +0,0 @@
1
- {
2
- "statutorySection": "Section 999",
3
- "governingStatute": "Incomplete Act, 2024",
4
- "effectivePeriod": {
5
- "validFrom": "2024-04-01",
6
- "validTo": "OPEN_ENDED"
7
- },
8
- "tier": "PUBLIC_COMMONS_FREE"
9
- }
@@ -1,16 +0,0 @@
1
- ---
2
- id: conf_circular_01
3
- title: Conformance Standard Test Circular
4
- authority: Conformance Verification Authority
5
- date: 2024-04-01
6
- statutory_section: Section 1
7
- valid_from: 2024-04-01
8
- valid_to: OPEN_ENDED
9
- temporal_status: ACTIVE
10
- subject: Operational guidelines for vendor-neutral OKF consumption
11
- official_url: https://example.org/circulars/2024/conf_01.pdf
12
- ---
13
-
14
- # Conformance Circular Body
15
- This circular fixture asserts that standard YAML frontmatter can be extracted
16
- by any third-party parser adhering to the Google OKF specification.
@@ -1,35 +0,0 @@
1
- {
2
- "sectionId": "CONF_SEC_02_FULL",
3
- "sectionCode": "CONF_SEC_02",
4
- "aliases": ["CONF_02", "conformance-full"],
5
- "governingStatute": "Comprehensive Compliance Act, 2024",
6
- "statutorySection": "Section 2",
7
- "successorStatute": "Future Compliance Act, 2026",
8
- "successorSection": "Section 101",
9
- "title": "Comprehensive Conformance Test Section with Temporal Bounds & Thresholds",
10
- "effectivePeriod": {
11
- "validFrom": "2024-04-01",
12
- "validTo": "2026-03-31",
13
- "annualReset": "Every April 1",
14
- "sunsetNotes": "Transitions to Section 101 on 1 April 2026."
15
- },
16
- "plainTextSummary": "This fixture asserts parsing of full metadata, thresholds, citations, and checklists.",
17
- "keyStatutoryThresholds": {
18
- "monetaryThresholdInr": 1000000,
19
- "ratePercent": 1.5,
20
- "penaltyRatePercent": 10.0
21
- },
22
- "officialCitations": [
23
- {
24
- "authority": "Standard Setting Authority",
25
- "instrument": "Notification No. 01/2024",
26
- "officialUrl": "https://example.org/gazette/2024/01.pdf"
27
- }
28
- ],
29
- "requiredDocumentChecklist": [
30
- "Taxpayer Identification Card",
31
- "Transaction Invoices"
32
- ],
33
- "tier": "PUBLIC_COMMONS_FREE",
34
- "version": "2.4.0"
35
- }
@@ -1,11 +0,0 @@
1
- {
2
- "sectionCode": "CONF_SEC_01",
3
- "statutorySection": "Section 1",
4
- "governingStatute": "Test Act, 2024",
5
- "effectivePeriod": {
6
- "validFrom": "2024-04-01",
7
- "validTo": "OPEN_ENDED"
8
- },
9
- "tier": "PUBLIC_COMMONS_FREE",
10
- "version": "2.4.0"
11
- }
@@ -1,129 +0,0 @@
1
- /**
2
- * ═══════════════════════════════════════════════════════════════════════════
3
- * @mudraforge/statutory — Vendor-Neutral OKF Conformance Runner
4
- * ═══════════════════════════════════════════════════════════════════════════
5
- * Validates that an OKF parser accurately distinguishes valid and invalid
6
- * statutory records without relying on internal framework logic.
7
- * Third-party language implementations (Python, Go, Rust) should replicate
8
- * these exact test assertions against the fixtures/ directory.
9
- */
10
-
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = path.dirname(__filename);
17
- const FIXTURES_DIR = path.join(__dirname, 'fixtures');
18
-
19
- function parseYamlFrontmatter(content) {
20
- if (!content.startsWith('---')) return null;
21
- const endIdx = content.indexOf('\n---', 3);
22
- if (endIdx === -1) return null;
23
- const yamlBlock = content.slice(3, endIdx).trim();
24
- const meta = {};
25
- for (const line of yamlBlock.split('\n')) {
26
- const colonIdx = line.indexOf(':');
27
- if (colonIdx !== -1) {
28
- const key = line.slice(0, colonIdx).trim();
29
- const val = line.slice(colonIdx + 1).trim();
30
- meta[key] = val;
31
- }
32
- }
33
- return meta;
34
- }
35
-
36
- function validateSectionRecord(record) {
37
- const errors = [];
38
- if (!record.sectionCode || typeof record.sectionCode !== 'string') {
39
- errors.push('Missing or invalid [sectionCode].');
40
- }
41
- if (!record.governingStatute || typeof record.governingStatute !== 'string') {
42
- errors.push('Missing or invalid [governingStatute].');
43
- }
44
- if (!record.effectivePeriod || typeof record.effectivePeriod !== 'object') {
45
- errors.push('Missing [effectivePeriod] object.');
46
- } else {
47
- const { validFrom, validTo } = record.effectivePeriod;
48
- if (!validFrom || !/^\d{4}-\d{2}-\d{2}$/.test(validFrom)) {
49
- errors.push('Malformed [effectivePeriod.validFrom] (expected YYYY-MM-DD).');
50
- }
51
- if (!validTo || (!/^\d{4}-\d{2}-\d{2}$/.test(validTo) && validTo !== 'OPEN_ENDED')) {
52
- errors.push('Malformed [effectivePeriod.validTo] (expected YYYY-MM-DD or OPEN_ENDED).');
53
- }
54
- if (validFrom && validTo && validTo !== 'OPEN_ENDED' && validTo < validFrom) {
55
- errors.push(`Temporal paradox: [validTo: ${validTo}] is earlier than [validFrom: ${validFrom}].`);
56
- }
57
- }
58
- return {
59
- isValid: errors.length === 0,
60
- errors
61
- };
62
- }
63
-
64
- export function runConformanceSuite() {
65
- console.log('===========================================================================');
66
- console.log('OKF VENDOR-NEUTRAL CONFORMANCE TEST SUITE');
67
- console.log('===========================================================================');
68
-
69
- let totalTests = 0;
70
- let passedTests = 0;
71
-
72
- function assert(name, condition, extraInfo = '') {
73
- totalTests++;
74
- if (condition) {
75
- passedTests++;
76
- console.log(`[PASS] ${name}`);
77
- } else {
78
- console.error(`[FAIL] ${name} — ${extraInfo}`);
79
- }
80
- }
81
-
82
- // 1. Valid Minimal Section
83
- const minRaw = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'valid_minimal_section.json'), 'utf8'));
84
- const minRes = validateSectionRecord(minRaw);
85
- assert('Fixture: valid_minimal_section.json correctly accepted', minRes.isValid, minRes.errors.join(', '));
86
-
87
- // 2. Valid Full Section
88
- const fullRaw = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'valid_full_section.json'), 'utf8'));
89
- const fullRes = validateSectionRecord(fullRaw);
90
- assert('Fixture: valid_full_section.json correctly accepted', fullRes.isValid, fullRes.errors.join(', '));
91
- assert('Fixture: valid_full_section.json retains keyStatutoryThresholds', Boolean(fullRaw.keyStatutoryThresholds?.monetaryThresholdInr === 1000000));
92
- assert('Fixture: valid_full_section.json retains dual-act successor mapping', fullRaw.successorStatute === 'Future Compliance Act, 2026');
93
-
94
- // 3. Invalid: Missing Section Code
95
- const noCodeRaw = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'invalid_missing_section_code.json'), 'utf8'));
96
- const noCodeRes = validateSectionRecord(noCodeRaw);
97
- assert('Fixture: invalid_missing_section_code.json correctly rejected', !noCodeRes.isValid);
98
- assert('Fixture: rejected with code error', noCodeRes.errors.some(e => e.includes('sectionCode')));
99
-
100
- // 4. Invalid: Missing Effective Period
101
- const noDatesRaw = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'invalid_missing_effective_period.json'), 'utf8'));
102
- const noDatesRes = validateSectionRecord(noDatesRaw);
103
- assert('Fixture: invalid_missing_effective_period.json correctly rejected', !noDatesRes.isValid);
104
- assert('Fixture: rejected with effectivePeriod error', noDatesRes.errors.some(e => e.includes('effectivePeriod')));
105
-
106
- // 5. Invalid: Malformed / Inverted Timeline
107
- const paradoxRaw = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, 'invalid_malformed_temporal_dates.json'), 'utf8'));
108
- const paradoxRes = validateSectionRecord(paradoxRaw);
109
- assert('Fixture: invalid_malformed_temporal_dates.json correctly rejected (temporal paradox)', !paradoxRes.isValid);
110
- assert('Fixture: rejected with temporal paradox explanation', paradoxRes.errors.some(e => e.includes('Temporal paradox')));
111
-
112
- // 6. Circular Markdown Frontmatter
113
- const mdContent = fs.readFileSync(path.join(FIXTURES_DIR, 'valid_circular_frontmatter.md'), 'utf8');
114
- const frontmatter = parseYamlFrontmatter(mdContent);
115
- assert('Fixture: valid_circular_frontmatter.md parsed YAML metadata', Boolean(frontmatter));
116
- assert('Fixture: circular frontmatter extracted required fields', frontmatter?.id === 'conf_circular_01' && frontmatter?.statutory_section === 'Section 1');
117
-
118
- console.log('===========================================================================');
119
- console.log(`CONFORMANCE SUMMARY: ${passedTests}/${totalTests} Tests Passed (100% Agreement)`);
120
- console.log('===========================================================================');
121
-
122
- if (passedTests !== totalTests) {
123
- process.exit(1);
124
- }
125
- }
126
-
127
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
128
- runConformanceSuite();
129
- }