regsim-in 0.1.0__py3-none-any.whl

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.
regsim/__init__.py ADDED
File without changes
regsim/cli.py ADDED
@@ -0,0 +1,107 @@
1
+ # regsim/cli.py
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import sys
7
+
8
+ from importlib import metadata
9
+
10
+ from regsim.commands.simulate import run_simulation
11
+ from regsim.core.simulation import load_json, load_rules, simulate
12
+ from regsim.engine import InvalidPayloadError
13
+ from regsim.parser import extract_from_file
14
+
15
+
16
+ def get_version():
17
+ return metadata.version("regsim-in")
18
+
19
+
20
+ def print_error(message: str, exit_code: int = 1) -> None:
21
+ print(json.dumps({
22
+ "status": "ERROR",
23
+ "message": message,
24
+ }))
25
+ sys.exit(exit_code)
26
+
27
+
28
+ def main():
29
+ parser = argparse.ArgumentParser(prog="regsim-in")
30
+ parser.add_argument(
31
+ "--version",
32
+ action="store_true",
33
+ help="Print RegSim-IN version",
34
+ )
35
+
36
+ subparsers = parser.add_subparsers(dest="command", required=False)
37
+
38
+ simulate_parser = subparsers.add_parser("simulate", help="Run regulatory simulation")
39
+ simulate_parser.add_argument("--rules", required=True)
40
+ simulate_parser.add_argument("--input", required=True)
41
+ simulate_parser.add_argument(
42
+ "--snapshot-date",
43
+ help="Regulatory snapshot date (YYYY-MM-DD)",
44
+ required=False,
45
+ )
46
+
47
+ args = parser.parse_args()
48
+
49
+ if args.version:
50
+ print(get_version())
51
+ sys.exit(0)
52
+
53
+ if not args.command:
54
+ parser.print_help()
55
+ sys.exit(2)
56
+
57
+ if args.command == "simulate":
58
+ try:
59
+ rules = load_rules(args.rules)
60
+
61
+ if os.path.isdir(args.input):
62
+ all_results = []
63
+ extraction_errors = []
64
+
65
+ for root, dirs, files in os.walk(args.input):
66
+ dirs.sort()
67
+ for filename in sorted(files):
68
+ if not filename.endswith(".py"):
69
+ continue
70
+ file_path = os.path.join(root, filename)
71
+ extraction = extract_from_file(file_path)
72
+ if extraction.errors:
73
+ extraction_errors.extend(extraction.errors)
74
+ continue
75
+ for payload in extraction.payloads:
76
+ all_results.append(
77
+ simulate(rules, payload, snapshot_date=args.snapshot_date)
78
+ )
79
+
80
+ if extraction_errors:
81
+ print_error("; ".join(extraction_errors), exit_code=2)
82
+
83
+ print(json.dumps(all_results, indent=2))
84
+ sys.exit(1 if any(r["status"] == "FAIL" for r in all_results) else 0)
85
+
86
+ if args.input.endswith(".py"):
87
+ extraction = extract_from_file(args.input)
88
+
89
+ if extraction.errors:
90
+ print_error("; ".join(extraction.errors), exit_code=2)
91
+
92
+ all_results = []
93
+ for payload in extraction.payloads:
94
+ all_results.append(
95
+ simulate(rules, payload, snapshot_date=args.snapshot_date)
96
+ )
97
+
98
+ print(json.dumps(all_results, indent=2))
99
+ sys.exit(1 if any(r["status"] == "FAIL" for r in all_results) else 0)
100
+
101
+ payload = load_json(args.input)
102
+ result = simulate(rules, payload, snapshot_date=args.snapshot_date)
103
+ print(json.dumps(result, indent=2))
104
+ except InvalidPayloadError as e:
105
+ print_error(str(e), exit_code=1)
106
+ except Exception as e:
107
+ print_error(str(e), exit_code=1)
File without changes
@@ -0,0 +1,5 @@
1
+ # regsim/commands/simulate.py
2
+
3
+ from regsim.core.simulation import run_simulation
4
+
5
+ __all__ = ["run_simulation"]
@@ -0,0 +1,3 @@
1
+ from regsim.core.simulation import run_simulation
2
+
3
+ __all__ = ["run_simulation"]
@@ -0,0 +1,65 @@
1
+ from regsim.core.fields import get_field
2
+ from regsim.core.validators import is_invalid_gstin
3
+
4
+
5
+ def evaluate_condition(condition, payload):
6
+ if "field" not in condition:
7
+ for raw_key, expected in condition.items():
8
+ if raw_key.endswith("_gt"):
9
+ field = raw_key[:-3]
10
+ operator = ">"
11
+ elif raw_key.endswith("_gte"):
12
+ field = raw_key[:-4]
13
+ operator = ">="
14
+ elif raw_key.endswith("_eq"):
15
+ field = raw_key[:-3]
16
+ operator = "=="
17
+ else:
18
+ field = raw_key
19
+ operator = "=="
20
+
21
+ found, field_value = get_field(payload, field)
22
+ if not found:
23
+ return False
24
+
25
+ try:
26
+ if operator == ">":
27
+ if not field_value > expected:
28
+ return False
29
+ elif operator == ">=":
30
+ if not field_value >= expected:
31
+ return False
32
+ elif operator == "==":
33
+ if not field_value == expected:
34
+ return False
35
+ else:
36
+ raise ValueError(f"Unsupported operator: {operator}")
37
+ except TypeError:
38
+ return False
39
+
40
+ return True
41
+
42
+ found, field_value = get_field(payload, condition["field"])
43
+ operator = condition["operator"]
44
+ expected = condition["value"]
45
+
46
+ if operator == "missing":
47
+ return not found
48
+
49
+ if operator == "invalid_gstin":
50
+ return not found or is_invalid_gstin(field_value)
51
+
52
+ if not found:
53
+ return False
54
+
55
+ try:
56
+ if operator == ">":
57
+ return field_value > expected
58
+ if operator == ">=":
59
+ return field_value >= expected
60
+ if operator == "==":
61
+ return field_value == expected
62
+ except TypeError:
63
+ return False
64
+
65
+ raise ValueError(f"Unsupported operator: {operator}")
regsim/core/fields.py ADDED
@@ -0,0 +1,29 @@
1
+ from typing import Any
2
+
3
+
4
+ def get_field(payload: Any, field_path: str):
5
+ if field_path is None or field_path == "":
6
+ return True, payload
7
+
8
+ current = payload
9
+ for part in field_path.split("."):
10
+ if isinstance(current, dict):
11
+ if part in current:
12
+ current = current[part]
13
+ else:
14
+ return False, None
15
+ continue
16
+
17
+ if isinstance(current, list):
18
+ if part.isdigit():
19
+ idx = int(part)
20
+ if 0 <= idx < len(current):
21
+ current = current[idx]
22
+ else:
23
+ return False, None
24
+ continue
25
+ return False, None
26
+
27
+ return False, None
28
+
29
+ return True, current
@@ -0,0 +1,110 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import jsonschema
6
+
7
+ from regsim.core.evaluator import evaluate_condition
8
+ from regsim.engine import InvalidPayloadError, validate_rules
9
+
10
+
11
+ def load_json(path: str):
12
+ raw = Path(path).read_text().strip()
13
+ if not raw:
14
+ raise ValueError(f"{path} is empty")
15
+ return json.loads(raw)
16
+
17
+
18
+ def load_rules(path: str):
19
+ if os.path.isdir(path):
20
+ rules = []
21
+ for root, dirs, files in os.walk(path):
22
+ dirs.sort()
23
+ for filename in sorted(files):
24
+ if not filename.endswith(".json"):
25
+ continue
26
+ file_path = os.path.join(root, filename)
27
+ data = load_json(file_path)
28
+ if isinstance(data, list):
29
+ rules.extend(data)
30
+ else:
31
+ rules.append(data)
32
+ return rules
33
+
34
+ data = load_json(path)
35
+ return data if isinstance(data, list) else [data]
36
+
37
+ def load_schema(schema_name: str) -> dict:
38
+ schema_path = Path(__file__).resolve().parents[1] / "schemas" / schema_name
39
+ raw = schema_path.read_text().strip()
40
+ if not raw:
41
+ raise ValueError(f"{schema_path} is empty")
42
+ return json.loads(raw)
43
+
44
+
45
+ def validate_rules_schema(rules):
46
+ if not isinstance(rules, list):
47
+ raise ValueError("Rules must be a JSON array")
48
+
49
+ schema = load_schema("rule.schema.json")
50
+ for rule in rules:
51
+ try:
52
+ jsonschema.validate(instance=rule, schema=schema)
53
+ except jsonschema.ValidationError as e:
54
+ rule_id = rule.get("rule_id") if isinstance(rule, dict) else None
55
+ raise ValueError(
56
+ f"Rule schema validation failed: {e.message} ({rule_id})"
57
+ ) from e
58
+
59
+
60
+ def simulate(rules, payload, snapshot_date=None) -> dict:
61
+ if not isinstance(payload, dict):
62
+ raise InvalidPayloadError("Input payload must be a JSON object")
63
+
64
+ validate_rules_schema(rules)
65
+ validate_rules(rules)
66
+
67
+ violations = []
68
+ applied_rules = []
69
+
70
+ for rule in rules:
71
+ applied_rules.append({
72
+ "rule_id": rule.get("rule_id"),
73
+ "rule_version": rule.get("rule_version"),
74
+ "effective_from": rule.get("effective_from"),
75
+ "source_reference": rule.get("source_reference"),
76
+ })
77
+
78
+ condition = rule["condition"]
79
+ action = rule["action"]
80
+
81
+ if evaluate_condition(condition, payload):
82
+ if action["type"] == "FAIL":
83
+ violations.append({
84
+ "rule_id": rule["rule_id"],
85
+ "rule_version": rule.get("rule_version"),
86
+ "severity": action.get("severity", "HIGH"),
87
+ "message": action["message"],
88
+ "risk": action.get("risk"),
89
+ "source_reference": rule.get("source_reference"),
90
+ })
91
+
92
+ status = "FAIL" if violations else "PASS"
93
+
94
+ return {
95
+ "status": status,
96
+ "violations": violations,
97
+ "metadata": {
98
+ "engine": "regsim-in",
99
+ "engine_version": "0.1.0",
100
+ "rule_snapshot": snapshot_date,
101
+ "applied_rules": applied_rules,
102
+ },
103
+ }
104
+
105
+
106
+ def run_simulation(rules_path: str, input_path: str) -> dict:
107
+ rules = load_rules(rules_path)
108
+ payload = load_json(input_path)
109
+
110
+ return simulate(rules, payload)
@@ -0,0 +1,9 @@
1
+ import re
2
+
3
+ GSTIN_REGEX = re.compile(r"^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$")
4
+
5
+
6
+ def is_invalid_gstin(value):
7
+ if not isinstance(value, str):
8
+ return True
9
+ return not bool(GSTIN_REGEX.match(value))
regsim/engine.py ADDED
@@ -0,0 +1,55 @@
1
+ def get_field(payload, field_path):
2
+ """
3
+ Fetch nested fields like payment.amount.
4
+ Returns a tuple: (found, value)
5
+ """
6
+ parts = field_path.split(".")
7
+ current = payload
8
+
9
+ for part in parts:
10
+ if not isinstance(current, dict):
11
+ return False, None
12
+ if part not in current:
13
+ return False, None
14
+ current = current[part]
15
+
16
+ return True, current
17
+
18
+
19
+ class InvalidPayloadError(Exception):
20
+ pass
21
+
22
+
23
+ REQUIRED_RULE_FIELDS = [
24
+ "rule_id",
25
+ "rule_version",
26
+ "effective_from",
27
+ "condition",
28
+ "action",
29
+ ]
30
+
31
+
32
+ def validate_rules(rules):
33
+ for rule in rules:
34
+ for field in REQUIRED_RULE_FIELDS:
35
+ if field not in rule:
36
+ raise ValueError(
37
+ f"Rule missing required field: {field} ({rule.get('rule_id')})"
38
+ )
39
+
40
+
41
+ def simulate(rules,payload):
42
+ if not isinstance(payload, dict):
43
+ raise InvalidPayloadError("Input payload must be a JSON object")
44
+
45
+ validate_rules(rules)
46
+
47
+ return{
48
+ # place holder logic
49
+ "status":"PASS",
50
+ "violations":[],
51
+ "metadata":{
52
+ "engine":"regsim-in",
53
+ "version":"0.1.0"
54
+ }
55
+ }
regsim/errors.py ADDED
File without changes
regsim/parser.py ADDED
@@ -0,0 +1,36 @@
1
+ import ast
2
+
3
+ from regsim.utils import extract_call_args, extract_dict, get_func_name
4
+
5
+ class ExtractionResult:
6
+ def __init__(self):
7
+ self.payloads = []
8
+ self.errors = []
9
+
10
+ def extract_from_file(file_path):
11
+ result = ExtractionResult()
12
+
13
+ try:
14
+ with open(file_path, "r") as f:
15
+ tree = ast.parse(f.read())
16
+ except Exception as e:
17
+ result.errors.append(str(e))
18
+ return result
19
+
20
+ for node in ast.walk(tree):
21
+ # Detect dict literals assigned to variables
22
+ if isinstance(node, ast.Assign):
23
+ if isinstance(node.value, ast.Dict):
24
+ payload = extract_dict(node.value)
25
+ if payload:
26
+ result.payloads.append(payload)
27
+
28
+ # Detect function calls like payout(...)
29
+ if isinstance(node, ast.Call):
30
+ func_name = get_func_name(node)
31
+ if func_name and "payout" in func_name.lower():
32
+ payload = extract_call_args(node)
33
+ if payload:
34
+ result.payloads.append(payload)
35
+
36
+ return result
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": true
4
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "RegSim-IN Evaluation Result",
4
+ "type": "object",
5
+ "required": ["status", "violations"],
6
+ "properties": {
7
+ "status": {
8
+ "type": "string",
9
+ "enum": ["PASS", "FAIL", "ERROR"],
10
+ "description": "Overall regulatory evaluation outcome"
11
+ },
12
+
13
+ "violations": {
14
+ "type": "array",
15
+ "description": "List of regulatory objections raised during evaluation",
16
+ "items": {
17
+ "type": "object",
18
+ "required": [
19
+ "rule_id",
20
+ "rule_version",
21
+ "severity",
22
+ "message"
23
+ ],
24
+ "properties": {
25
+ "rule_id": {
26
+ "type": "string",
27
+ "description": "Identifier of the violated rule"
28
+ },
29
+
30
+ "rule_version": {
31
+ "type": "string",
32
+ "description": "Version of the rule that was evaluated"
33
+ },
34
+
35
+ "authority": {
36
+ "type": "string",
37
+ "enum": ["CBDT", "GST", "RBI"],
38
+ "description": "Regulatory authority raising the objection"
39
+ },
40
+
41
+ "severity": {
42
+ "type": "string",
43
+ "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"],
44
+ "description": "Regulatory risk level of the violation"
45
+ },
46
+
47
+ "message": {
48
+ "type": "string",
49
+ "description": "Human-readable explanation of the violation"
50
+ },
51
+
52
+ "source_reference": {
53
+ "type": "string",
54
+ "description": "Legal basis for the violation"
55
+ },
56
+
57
+ "risk": {
58
+ "type": "string",
59
+ "description": "Operational or compliance risk summary"
60
+ },
61
+
62
+ "evidence": {
63
+ "type": "object",
64
+ "description": "Facts that caused this rule to trigger"
65
+ }
66
+ }
67
+ }
68
+ },
69
+
70
+ "metadata": {
71
+ "type": "object",
72
+ "description": "Execution metadata (engine info, snapshot, applied rules, etc.)",
73
+ "properties": {
74
+ "engine": { "type": "string" },
75
+ "engine_version": { "type": "string" },
76
+ "rule_snapshot": { "type": ["string", "null"] },
77
+ "applied_rules": {
78
+ "type": "array",
79
+ "items": {
80
+ "type": "object",
81
+ "properties": {
82
+ "rule_id": { "type": "string" },
83
+ "rule_version": { "type": "string" },
84
+ "effective_from": { "type": "string", "format": "date" },
85
+ "source_reference": { "type": "string" }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "RegSim-IN Rule Schema",
4
+ "type": "object",
5
+ "required": [
6
+ "rule_id",
7
+ "rule_version",
8
+ "effective_from",
9
+ "condition",
10
+ "action"
11
+ ],
12
+ "properties": {
13
+ "rule_id": {
14
+ "type": "string",
15
+ "description": "Stable identifier for the regulatory rule"
16
+ },
17
+
18
+ "rule_version": {
19
+ "type": "string",
20
+ "description": "Semantic or date-based version of the rule"
21
+ },
22
+
23
+ "authority": {
24
+ "type": "string",
25
+ "enum": ["CBDT", "GST", "RBI"],
26
+ "description": "Regulatory authority owning this rule"
27
+ },
28
+
29
+ "jurisdiction": {
30
+ "type": "string",
31
+ "description": "Geographic or legal scope (e.g. IN, IN-KA, CROSS_BORDER)"
32
+ },
33
+
34
+ "effective_from": {
35
+ "type": "string",
36
+ "format": "date",
37
+ "description": "Date from which the rule is legally applicable"
38
+ },
39
+
40
+ "effective_to": {
41
+ "type": "string",
42
+ "format": "date",
43
+ "description": "Date until which the rule is applicable"
44
+ },
45
+
46
+ "severity": {
47
+ "type": "string",
48
+ "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"],
49
+ "description": "Risk level from a regulatory perspective"
50
+ },
51
+
52
+ "description": {
53
+ "type": "string",
54
+ "description": "Human-readable explanation of the rule intent"
55
+ },
56
+
57
+ "condition": {
58
+ "type": "object",
59
+ "description": "Declarative conditions that determine rule applicability"
60
+ },
61
+
62
+ "action": {
63
+ "type": "object",
64
+ "required": ["type", "message"],
65
+ "properties": {
66
+ "type": {
67
+ "type": "string",
68
+ "enum": ["FAIL", "WARN", "PASS"],
69
+ "description": "Outcome when the rule condition is satisfied"
70
+ },
71
+
72
+ "message": {
73
+ "type": "string",
74
+ "description": "Explanation shown to developers or auditors"
75
+ },
76
+
77
+ "evidence_fields": {
78
+ "type": "array",
79
+ "items": { "type": "string" },
80
+ "description": "Fact fields that triggered the rule"
81
+ }
82
+ }
83
+ },
84
+
85
+ "source_reference": {
86
+ "type": "string",
87
+ "description": "Legal reference (Act section, circular, notification)"
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,9 @@
1
+ __all__ = [
2
+ "extract_call_args",
3
+ "extract_dict",
4
+ "get_func_name",
5
+ ]
6
+
7
+ from regsim.utils.extract_call_args import extract_call_args
8
+ from regsim.utils.extract_dic import extract_dict
9
+ from regsim.utils.get_func_name import get_func_name
@@ -0,0 +1,10 @@
1
+ import ast
2
+
3
+ def extract_call_args(call_node):
4
+ payload = {}
5
+
6
+ for kw in call_node.keywords:
7
+ if isinstance(kw.value, ast.Constant):
8
+ payload[kw.arg] = kw.value.value
9
+
10
+ return payload if payload else None
@@ -0,0 +1,11 @@
1
+ import ast
2
+
3
+ def extract_dict(node):
4
+ payload = {}
5
+
6
+ for key, value in zip(node.keys, node.values):
7
+ if isinstance(key, ast.Constant):
8
+ if isinstance(value, ast.Constant):
9
+ payload[key.value] = value.value
10
+
11
+ return payload if payload else None
@@ -0,0 +1,8 @@
1
+ import ast
2
+
3
+ def get_func_name(call_node):
4
+ if isinstance(call_node.func, ast.Name):
5
+ return call_node.func.id
6
+ if isinstance(call_node.func, ast.Attribute):
7
+ return call_node.func.attr
8
+ return None
@@ -0,0 +1,324 @@
1
+ Metadata-Version: 2.4
2
+ Name: regsim-in
3
+ Version: 0.1.0
4
+ Summary: Regulatory Simulation CLI for Indian Backend Systems
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: jsonschema>=4.0.0
8
+
9
+ # RegSim-IN
10
+
11
+ **Regulatory Simulation & Failure Memory CLI for Indian Backend Systems**
12
+
13
+ ---
14
+
15
+ ## What is RegSim-IN?
16
+
17
+ **RegSim-IN** is a **developer-first CLI** that simulates Indian regulatory rules (TDS, GST, RBI-style constraints) against backend data flows **before production**.
18
+
19
+ It helps backend teams **detect, explain, and remember regulatory failures** early — during development, testing, and CI — instead of discovering them during audits or incidents.
20
+
21
+ RegSim-IN treats regulation as **executable rules**, not PDFs.
22
+
23
+ ---
24
+
25
+ ## What Problem Does This Solve?
26
+
27
+ Backend teams frequently:
28
+
29
+ * Ship compliant-looking code that fails under real regulatory edge cases
30
+ * Discover issues late (audits, settlements, reversals)
31
+ * Repeat the *same regulatory mistakes* across services and teams
32
+
33
+ RegSim-IN exists to:
34
+
35
+ * Shift regulatory failures **left**
36
+ * Make rules **explicit and testable**
37
+ * Prevent **repeat regulatory incidents**
38
+
39
+ ---
40
+
41
+ ## What RegSim-IN v1 Does
42
+
43
+ Version 1 focuses on **deterministic rule simulation**.
44
+
45
+ RegSim-IN v1 can:
46
+
47
+ * Load regulatory rules defined in **JSON**
48
+ * Run those rules against input payloads (also JSON)
49
+ * Evaluate pass/fail conditions
50
+ * Emit **machine-readable JSON output**
51
+ * Explain *why* a rule failed
52
+
53
+ This makes RegSim-IN suitable for:
54
+
55
+ * Local development checks
56
+ * CI/CD gates
57
+ * Backend design validation
58
+ * Regulatory edge-case exploration
59
+
60
+ ---
61
+
62
+ ## Example: Detecting a Missed TDS Deduction
63
+
64
+ Consider a backend payout flow where a contractor payment is executed without deducting TDS.
65
+
66
+ **Input payload:**
67
+ ```json
68
+ {
69
+ "payment": {
70
+ "id": "pay_1029",
71
+ "amount": 45000,
72
+ "vendor_type": "contractor",
73
+ "tds_deducted": false
74
+ }
75
+ }
76
+ ```
77
+
78
+ **Simulation result:**
79
+
80
+ ```json
81
+ {
82
+ "status": "FAIL",
83
+ "violations": [
84
+ {
85
+ "rule_id": "TDS_194C_THRESHOLD",
86
+ "severity": "HIGH",
87
+ "message": "TDS must be deducted under section 194C"
88
+ }
89
+ ]
90
+ }
91
+ ```
92
+
93
+ This allows teams to catch deduction timing and threshold violations **before** payouts reach production systems.
94
+
95
+ ---
96
+
97
+ ## What RegSim-IN Explicitly Does NOT Do
98
+
99
+ To avoid misuse or false confidence, RegSim-IN v1 does **not**:
100
+
101
+ * Provide legal, tax, or regulatory advice
102
+ * File or generate GST / TDS / RBI reports
103
+ * Integrate with government, bank, or tax APIs
104
+ * Automatically update rules from circulars
105
+ * Fully simulate async systems (queues, retries, persistent state)
106
+
107
+ **This is a simulation tool, not a compliance authority.**
108
+
109
+ ---
110
+
111
+ ## Supported Languages
112
+
113
+ * **Python** (v1)
114
+
115
+ The CLI is language-agnostic, but rule evaluation currently targets Python-style backend data models.
116
+
117
+ ---
118
+
119
+ ## Rule Format
120
+
121
+ Rules are defined in **JSON**.
122
+
123
+ Design goals:
124
+
125
+ * Explicit structure
126
+ * Deterministic evaluation
127
+ * Easy diffing & review
128
+ * CI/CD friendliness
129
+
130
+ ### Example Rule
131
+
132
+ ```json
133
+ {
134
+ "rule_id": "TDS_194C_THRESHOLD",
135
+ "rule_version": "1.0",
136
+ "effective_from": "2024-04-01",
137
+ "description": "TDS applies if contractor payment exceeds threshold",
138
+ "condition": {
139
+ "field": "payment.amount",
140
+ "operator": ">",
141
+ "value": 30000
142
+ },
143
+ "action": {
144
+ "type": "FAIL",
145
+ "message": "TDS must be deducted under section 194C"
146
+ },
147
+ "source_reference": "Income Tax Act - Section 194C"
148
+ }
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Input Format
154
+
155
+ Inputs represent **backend payloads or traces**, also in JSON.
156
+
157
+ ---
158
+
159
+ ## Output Format
160
+
161
+ All outputs are **JSON only**.
162
+
163
+ Example failure output:
164
+
165
+ ```json
166
+ {
167
+ "status": "FAIL",
168
+ "violations": [
169
+ {
170
+ "rule_id": "TDS_194C_THRESHOLD",
171
+ "severity": "HIGH",
172
+ "message": "TDS must be deducted under section 194C"
173
+ }
174
+ ],
175
+ "metadata": {
176
+ "engine": "regsim-in",
177
+ "engine_version": "0.1.0",
178
+ "rule_snapshot": "2024-04-01",
179
+ "applied_rules": [
180
+ {
181
+ "rule_id": "TDS_194C_THRESHOLD",
182
+ "rule_version": "1.0",
183
+ "effective_from": "2024-04-01",
184
+ "source_reference": "Income Tax Act - Section 194C"
185
+ }
186
+ ]
187
+ }
188
+ }
189
+ ```
190
+
191
+ This makes RegSim-IN suitable for automation and tooling.
192
+
193
+ ---
194
+
195
+ ## Error Output
196
+
197
+ Errors are always returned as JSON and never include a Python traceback:
198
+
199
+ ```json
200
+ {
201
+ "status": "ERROR",
202
+ "message": "Rule validation failed: missing field 'action'"
203
+ }
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Installation (Early Prototype)
209
+
210
+ ```bash
211
+ pip install regsim-in
212
+ ```
213
+
214
+ > RegSim-IN is under active development.
215
+
216
+ ---
217
+
218
+ ## Usage (v1)
219
+
220
+ ```bash
221
+ regsim-in simulate \
222
+ --rules rules/ \
223
+ --input input.json
224
+ ```
225
+
226
+ Current v1 behavior:
227
+
228
+ * CLI initializes correctly
229
+ * Rules are parsed and validated
230
+ * Simulation runs deterministically
231
+ * JSON output is emitted
232
+
233
+ ---
234
+
235
+ ## Project Layout (Current)
236
+
237
+ ```
238
+ regsim/
239
+ cli.py
240
+ commands/
241
+ simulate.py
242
+ core/
243
+ evaluator.py
244
+ fields.py
245
+ simulation.py
246
+ validators.py
247
+ schemas/
248
+ ```
249
+
250
+ The CLI stays thin, while core rule evaluation lives under `regsim/core/`.
251
+
252
+ ---
253
+
254
+ ## Rule Versioning & Regulatory Drift
255
+
256
+ RegSim-IN v1 supports **explicit rule versioning**:
257
+
258
+ * Rules declare:
259
+
260
+ * `rule_version`
261
+ * `effective_from`
262
+ * `source_reference`
263
+ * No rule updates happen implicitly
264
+ * Simulations are always tied to a known regulatory snapshot
265
+
266
+ This ensures:
267
+
268
+ * Reproducibility
269
+ * Reviewability
270
+ * Trust
271
+
272
+ ---
273
+
274
+ ## CI/CD Usage Example
275
+
276
+ ```bash
277
+ regsim-in simulate --rules rules/ --input payload.json || exit 1
278
+ ```
279
+
280
+ A failing rule causes a non-zero exit code.
281
+
282
+ ---
283
+
284
+ ## Roadmap (Explicit, Not Promised)
285
+
286
+ Planned future directions include:
287
+
288
+ * Regulatory failure memory & correlation
289
+ * Safer rule authoring workflows
290
+ * Async system modeling hooks
291
+ * Community-contributed rule sets
292
+
293
+ These are **not part of v1**.
294
+
295
+ ---
296
+
297
+ ## Philosophy
298
+
299
+ * Simulation over certification
300
+ * Explicit rules over implicit assumptions
301
+ * Deterministic behavior over magic
302
+ * Memory over repetition
303
+
304
+ ---
305
+
306
+ ## Disclaimer
307
+
308
+ RegSim-IN is a **developer simulation tool**.
309
+ It does **not** guarantee legal or regulatory compliance.
310
+
311
+ Always consult qualified professionals for real-world compliance decisions
312
+
313
+ ---
314
+
315
+ ## Exact Commands
316
+
317
+ # Run against JSON payload
318
+ regsim-in simulate --rules rules/ --input payload.json
319
+
320
+ # Run against Python service
321
+ regsim-in simulate --rules rules/ --input src/
322
+
323
+ # CI usage
324
+ regsim-in simulate --rules rules/ --input src/ --snapshot-date 2024-04-01
@@ -0,0 +1,24 @@
1
+ regsim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ regsim/cli.py,sha256=g1PeXT5ZRwdm700FS4Hv5v1hYgDP6X8mrWfiVKjYizU,3481
3
+ regsim/engine.py,sha256=4ApvAIo0aPyHyvugk2hsmRfU4E-7Tja199KbxzrVYZ4,1204
4
+ regsim/errors.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ regsim/parser.py,sha256=kivTrtEn_retMjaFyibZTMIlzRV1oLJudoDRbzpUXT4,1076
6
+ regsim/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ regsim/commands/simulate.py,sha256=Z4c5A_t5lS0D-tV2EVsfTz8Obt4fITHmnhxnRLcAfZg,111
8
+ regsim/core/__init__.py,sha256=IyAjq1BAyLtSfqXjApMUC6EL5kik2dG1LunGuMObTGQ,80
9
+ regsim/core/evaluator.py,sha256=aMPuHUsxthcF-GxUi4YGwktbusSuDAgCaxX31qNcBLg,1979
10
+ regsim/core/fields.py,sha256=r8pk3ry6GdvMNa044x3p_89V0SW3EIjco1Jg5RzPya0,753
11
+ regsim/core/simulation.py,sha256=fqnyeMiNK9QRnNXfzaTmqow13Ni9H_bWApDw5BruvB8,3354
12
+ regsim/core/validators.py,sha256=h6q7wLAIKsnUK7_HOx5HnflLU8iHmFOts3KIg99U81w,230
13
+ regsim/schemas/input.schema.json,sha256=Wm2xgC1_NRUGBnj3eQY2hCDhbwAtx2DVnmLFptiqwjw,55
14
+ regsim/schemas/output.schema.json,sha256=EI1Y18TcNHKv6T1nkPSGOmMEYejdVTWtjGFfeFnpi8I,2566
15
+ regsim/schemas/rule.schema.json,sha256=tG4xSrPWcaKEjpddX1mKAPysx8PeAWPac_eFjs22djc,2201
16
+ regsim/utils/__init__.py,sha256=Y12Xi4a4hVUw_SVAXT0l2uDjBOABlmfPpsyoZtF3HMg,245
17
+ regsim/utils/extract_call_args.py,sha256=hOEFgf-q9VXON7a_UR1nhu2joerzfcKddtu7pZWIyA8,231
18
+ regsim/utils/extract_dic.py,sha256=R2ZzZXndDS77Nj1b8WGp1DXD5N7x7xWzUF5U4DB2NbA,285
19
+ regsim/utils/get_func_name.py,sha256=NF7GpDaH62r5jxCF_wA7rJP44COkRnjs8Ak7lcUp2RA,221
20
+ regsim_in-0.1.0.dist-info/METADATA,sha256=FKVa9v92-cTgpaZlkOdbQ1LLwClAgLRl9ytPtN-OKFs,6330
21
+ regsim_in-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
22
+ regsim_in-0.1.0.dist-info/entry_points.txt,sha256=9XtjTQnV-P6spTRQfs8RyvBkAJjyVgsyVCvu3XVWJdw,46
23
+ regsim_in-0.1.0.dist-info/top_level.txt,sha256=v8qlZho0FIeDZFo2-rN6-vSbtXtZJ6LIPwXei87wRzY,7
24
+ regsim_in-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ regsim-in = regsim.cli:main
@@ -0,0 +1 @@
1
+ regsim