sud-cli 0.1.1__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.
Files changed (74) hide show
  1. sud_cli/__init__.py +3 -0
  2. sud_cli/__main__.py +4 -0
  3. sud_cli/_wm.py +118 -0
  4. sud_cli/agenda.py +90 -0
  5. sud_cli/autofill.py +192 -0
  6. sud_cli/autofill_markers.json +56 -0
  7. sud_cli/backup.py +256 -0
  8. sud_cli/calendar_ics.py +191 -0
  9. sud_cli/case_diff.py +165 -0
  10. sud_cli/case_search_bridge.py +1322 -0
  11. sud_cli/chain.py +226 -0
  12. sud_cli/cli.py +3721 -0
  13. sud_cli/completeness.py +105 -0
  14. sud_cli/config.py +20 -0
  15. sud_cli/conflict.py +210 -0
  16. sud_cli/conflicts.py +207 -0
  17. sud_cli/court_alerts.py +156 -0
  18. sud_cli/court_source.py +108 -0
  19. sud_cli/data/completeness_rules.json +18 -0
  20. sud_cli/data/key_rates.json +59 -0
  21. sud_cli/data/npa_historical_editions.json +19 -0
  22. sud_cli/db.py +2072 -0
  23. sud_cli/deadline_matrix.py +99 -0
  24. sud_cli/deadline_state.py +25 -0
  25. sud_cli/deadlines.py +8 -0
  26. sud_cli/deadlines_state.py +1 -0
  27. sud_cli/deferral.py +5 -0
  28. sud_cli/efrsb_manual.py +85 -0
  29. sud_cli/efrsb_public.py +210 -0
  30. sud_cli/egrul_nalog.py +150 -0
  31. sud_cli/explain.py +190 -0
  32. sud_cli/fees_extra.py +104 -0
  33. sud_cli/forms.py +204 -0
  34. sud_cli/fssp_manual.py +91 -0
  35. sud_cli/fssp_semi.py +285 -0
  36. sud_cli/fssp_stats.py +105 -0
  37. sud_cli/government_transfers.json +8 -0
  38. sud_cli/holidays_rf.json +62 -0
  39. sud_cli/idempotency.py +78 -0
  40. sud_cli/importing.py +575 -0
  41. sud_cli/interest.py +144 -0
  42. sud_cli/jurisdiction.py +177 -0
  43. sud_cli/jurisdiction_rules.json +265 -0
  44. sud_cli/legal_deadlines.py +278 -0
  45. sud_cli/legal_engine.py +558 -0
  46. sud_cli/legal_rules.json +1058 -0
  47. sud_cli/limitation.py +70 -0
  48. sud_cli/mcp_server.py +1594 -0
  49. sud_cli/metrics.py +142 -0
  50. sud_cli/npa_as_of.py +21 -0
  51. sud_cli/output.py +89 -0
  52. sud_cli/payment.py +24 -0
  53. sud_cli/pdn.py +259 -0
  54. sud_cli/plenum_corpus.py +134 -0
  55. sud_cli/plenums_seed.json +45 -0
  56. sud_cli/practice_status.py +136 -0
  57. sud_cli/pravo_publication.py +361 -0
  58. sud_cli/pretrial.py +151 -0
  59. sud_cli/production_calendar.py +233 -0
  60. sud_cli/providers.py +95 -0
  61. sud_cli/readiness.py +234 -0
  62. sud_cli/regional_holidays.py +37 -0
  63. sud_cli/reminders.py +92 -0
  64. sud_cli/reports.py +349 -0
  65. sud_cli/sud_netguard.py +943 -0
  66. sud_cli/timeline_audit.py +80 -0
  67. sud_cli/verify.py +201 -0
  68. sud_cli-0.1.1.dist-info/METADATA +119 -0
  69. sud_cli-0.1.1.dist-info/RECORD +74 -0
  70. sud_cli-0.1.1.dist-info/WHEEL +4 -0
  71. sud_cli-0.1.1.dist-info/entry_points.txt +2 -0
  72. sud_cli-0.1.1.dist-info/licenses/AUTHORS +5 -0
  73. sud_cli-0.1.1.dist-info/licenses/LICENSE.md +661 -0
  74. sud_cli-0.1.1.dist-info/licenses/NOTICE +7 -0
sud_cli/interest.py ADDED
@@ -0,0 +1,144 @@
1
+ """Interest calculator for articles 395 and 317.1 of the Civil Code."""
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from datetime import date, timedelta
6
+ from decimal import Decimal, ROUND_HALF_UP
7
+
8
+ #: v2.3.2 (sa-1 F-11): embedded-копия остаётся fail-safe фолбэком;
9
+ #: боевой источник — data/key_rates.json (обновляется без правки кода).
10
+ _RATE_PERIODS_EMBEDDED = (
11
+ (date(2026, 6, 15), Decimal("18")),
12
+ (date(2026, 2, 16), Decimal("15")),
13
+ (date(2025, 12, 22), Decimal("16")),
14
+ (date(2025, 9, 15), Decimal("17")),
15
+ (date(2025, 6, 9), Decimal("20")),
16
+ (date(2024, 12, 16), Decimal("21")),
17
+ (date(2024, 10, 28), Decimal("21")),
18
+ (date(2024, 9, 16), Decimal("19")),
19
+ (date(2024, 7, 29), Decimal("18")),
20
+ (date(2024, 6, 10), Decimal("16")),
21
+ (date(2024, 4, 29), Decimal("16")),
22
+ (date(2023, 9, 18), Decimal("13")),
23
+ (date(2023, 7, 24), Decimal("8.5")),
24
+ )
25
+
26
+ _RATES_PATH = __import__("pathlib").Path(__file__).with_name("data") / "key_rates.json"
27
+
28
+
29
+ def _load_rate_periods():
30
+ """JSON-first загрузка с embedded-фолбэком (нет файла — не падаем)."""
31
+ try:
32
+ data = json.loads(_RATES_PATH.read_text(encoding="utf-8"))
33
+ periods = tuple((date.fromisoformat(d), Decimal(str(r))) for d, r in data["periods"])
34
+ if periods:
35
+ return periods, data
36
+ except (OSError, ValueError, KeyError, TypeError):
37
+ pass
38
+ return _RATE_PERIODS_EMBEDDED, {"provenance": "embedded fallback (key_rates.json missing/unreadable)",
39
+ "coverage_latest_change": _RATE_PERIODS_EMBEDDED[0][0].isoformat(),
40
+ "coverage_first": _RATE_PERIODS_EMBEDDED[-1][0].isoformat()}
41
+
42
+
43
+ RATE_PERIODS, RATES_META = _load_rate_periods()
44
+
45
+
46
+ def rate_on(day):
47
+ for starts, rate in RATE_PERIODS:
48
+ if day >= starts:
49
+ return rate
50
+ return Decimal("7.5")
51
+
52
+
53
+ @dataclass
54
+ class InterestResult:
55
+ status: str
56
+ amount_rub: Decimal = Decimal("0")
57
+ days: int = 0
58
+ periods: list = None
59
+ article: str = "395"
60
+ remaining_principal: Decimal = Decimal("0")
61
+ rates_warnings: list = None
62
+ rates_meta: dict = None
63
+
64
+ def to_dict(self):
65
+ out = {"status": self.status, "article": self.article, "amount_rub": float(self.amount_rub), "days": self.days, "remaining_principal_rub": float(self.remaining_principal), "periods": self.periods or []}
66
+ if self.rates_warnings:
67
+ out["rates_warnings"] = self.rates_warnings
68
+ if self.rates_meta:
69
+ out["rates_meta"] = self.rates_meta
70
+ return out
71
+
72
+
73
+ def parse_payments(value):
74
+ """Parse ``date:amount,...`` or a JSON list of payment objects."""
75
+ if not value:
76
+ return []
77
+ if isinstance(value, str):
78
+ try:
79
+ value = json.loads(value) if value.lstrip().startswith("[") else [
80
+ dict(zip(("date", "amount"), item.split(":", 1)))
81
+ for item in value.split(",")
82
+ ]
83
+ except (ValueError, TypeError, json.JSONDecodeError):
84
+ return None
85
+ if not isinstance(value, list):
86
+ return None
87
+ payments = []
88
+ try:
89
+ for item in value:
90
+ if isinstance(item, (list, tuple)):
91
+ item = {"date": item[0], "amount": item[1]}
92
+ day = date.fromisoformat(item.get("date"))
93
+ paid = Decimal(str(item.get("amount")))
94
+ if paid <= 0:
95
+ return None
96
+ payments.append((day, paid))
97
+ except (AttributeError, IndexError, TypeError, ValueError, ArithmeticError):
98
+ return None
99
+ return sorted(payments)
100
+
101
+
102
+ def calculate_interest(amount, start, end, article="395", payments=None):
103
+ if amount <= 0 or end <= start:
104
+ return InterestResult("MISSING_REQUIRED_PARAM", article=article)
105
+ parsed_payments = parse_payments(payments)
106
+ if parsed_payments is None or any(day < start or day > end for day, _ in parsed_payments):
107
+ return InterestResult("MISSING_REQUIRED_PARAM", article=article)
108
+ principal = Decimal(str(amount))
109
+ payments_by_day = {}
110
+ for day, paid in parsed_payments:
111
+ payments_by_day[day] = payments_by_day.get(day, Decimal("0")) + paid
112
+ if any(sum((paid for day, paid in parsed_payments if day <= point), Decimal("0")) > principal for point in payments_by_day):
113
+ return InterestResult("MISSING_REQUIRED_PARAM", article=article)
114
+ cursor, total, periods = start, Decimal("0"), []
115
+ while cursor < end:
116
+ principal -= payments_by_day.get(cursor, Decimal("0"))
117
+ rate = rate_on(cursor)
118
+ boundaries = [d for d, _ in RATE_PERIODS if cursor < d < end]
119
+ boundaries.extend(d for d in payments_by_day if cursor < d < end)
120
+ # v2.3.3 (review R5): период через Новый год считал все дни по
121
+ # year_days года старта — 366 для невисокосных отрезков занижало проценты.
122
+ yb = date(cursor.year + 1, 1, 1)
123
+ if cursor < yb < end:
124
+ boundaries.append(yb)
125
+ stop = min(boundaries, default=end)
126
+ days = (stop - cursor).days
127
+ year_days = 366 if (cursor.year % 4 == 0 and (cursor.year % 100 != 0 or cursor.year % 400 == 0)) else 365
128
+ value = principal * days / year_days * rate / 100
129
+ total += value
130
+ periods.append({"from": cursor.isoformat(), "to": stop.isoformat(), "days": days, "rate": float(rate), "principal_rub": float(principal), "amount_rub": float(value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))})
131
+ cursor = stop
132
+ remaining = principal - payments_by_day.get(end, Decimal("0"))
133
+ result = InterestResult("ok", total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP), (end - start).days, periods, article, remaining)
134
+ # v2.3.2 (sa-1 F-11): тихая экстраполяция за границу покрытия — теперь с предупреждением.
135
+ warnings = []
136
+ latest = RATES_META.get("coverage_latest_change")
137
+ if latest and end.isoformat() > latest:
138
+ warnings.append(f"rates after {latest} extrapolated from last known key rate")
139
+ if not RATES_META.get("periods_source", True) or "embedded fallback" in str(RATES_META.get("provenance", "")):
140
+ warnings.append("key rates loaded from embedded fallback, not key_rates.json")
141
+ result.rates_warnings = warnings
142
+ result.rates_meta = {"provenance": RATES_META.get("provenance"), "coverage_latest_change": latest,
143
+ "coverage_first": RATES_META.get("coverage_first")}
144
+ return result
@@ -0,0 +1,177 @@
1
+ """Jurisdiction venue helper for sud-cli (v2.6.0).
2
+
3
+ Pure read of sud_cli/jurisdiction_rules.json. No norm texts are invented:
4
+ reference_only entries carry fetch_hint="npa_as_of" instead of legal_text.
5
+ """
6
+ import json
7
+ from pathlib import Path
8
+
9
+ RULES_PATH = Path(__file__).with_name("jurisdiction_rules.json")
10
+
11
+ CODE_TO_KEY = {"apk": "APK", "gpk": "GPK", "kas": "KAS"}
12
+
13
+
14
+ def load_rules(path=None):
15
+ return json.loads(Path(path or RULES_PATH).read_text(encoding="utf-8"))
16
+
17
+
18
+ def _candidate(rule):
19
+ out = {
20
+ "court_level": _court_level(rule),
21
+ "venue_basis": rule["norm"],
22
+ "confidence": rule["confidence"],
23
+ }
24
+ if rule.get("legal_text"):
25
+ out["legal_text"] = rule["legal_text"]
26
+ else:
27
+ out["fetch_hint"] = "npa_as_of"
28
+ return out
29
+
30
+
31
+ def _court_level(rule):
32
+ effect = rule.get("effect", "")
33
+ return effect.split(",")[0].strip() if effect else rule.get("norm", "")
34
+
35
+
36
+ def _mr_entry(rule):
37
+ return {"reason": rule.get("mr_reason") or rule.get("effect", ""), "norm": rule["norm"]}
38
+
39
+
40
+ _FLAG_KEYS = {
41
+ "APK": {
42
+ "unknown_rf_location": ["unknown_or_no_ru_loc_36_1"],
43
+ "immovable": ["exclusive_38"],
44
+ "corporate": ["corporate_38_4_1"],
45
+ "bankruptcy": ["bankruptcy"],
46
+ "ip_rights": ["ip_court"],
47
+ "contract_forum": ["agree_37_2"],
48
+ "arbitration_clause": None,
49
+ "multiple_defendants": ["multiple_defendants_36_2"],
50
+ "foreign_element": ["foreign_element"],
51
+ },
52
+ "GPK": {
53
+ "unknown_rf_location": ["plaintiff_choice_29"],
54
+ "consumer": ["plaintiff_choice_29"],
55
+ "alimony": ["plaintiff_choice_29"],
56
+ "immovable": ["exclusive_30"],
57
+ "contract_forum": ["agree_32"],
58
+ "arbitration_clause": None,
59
+ "multiple_defendants": ["multiple_31_1"],
60
+ "foreign_element": ["foreign_element"],
61
+ },
62
+ "KAS": {
63
+ "unknown_rf_location": ["choice_24"],
64
+ },
65
+ }
66
+
67
+
68
+ def _claim_has(claim, *words):
69
+ cl = str(claim or "").lower()
70
+ return any(w in cl for w in words)
71
+
72
+
73
+ def check_jurisdiction(code, claim=None, party_org=False, party_phys=False,
74
+ unknown_rf_location=False, immovable=False, corporate=False,
75
+ bankruptcy=False, ip_rights=False, contract_forum=False,
76
+ arbitration_clause=False, multiple_defendants=False,
77
+ foreign_element=False, consumer=False, alimony=False,
78
+ rules=None):
79
+ # Claim keywords can also drive branches (keeps --claim meaningful).
80
+ unknown_rf_location = unknown_rf_location or _claim_has(claim, "unknown", "неизвест", "no_ru", "no-location", "unknown_rf_location", "unknown_rf")
81
+ immovable = immovable or _claim_has(claim, "immovable", "недвиж", "имущ")
82
+ corporate = corporate or _claim_has(claim, "corporate", "корпор")
83
+ bankruptcy = bankruptcy or _claim_has(claim, "bankruptcy", "банкрот")
84
+ ip_rights = ip_rights or _claim_has(claim, "ip_rights", "ip-rights", "интеллект", "сип", "sip", "intellectual", "ip")
85
+ contract_forum = contract_forum or _claim_has(claim, "contract", "договор", "forum", "форум")
86
+ arbitration_clause = arbitration_clause or _claim_has(claim, "arbitration", "арбитраж", "третей", "оговорк")
87
+ multiple_defendants = multiple_defendants or _claim_has(claim, "multiple", "нескольк", "множ")
88
+ foreign_element = foreign_element or _claim_has(claim, "foreign", "иностр", "трансгран")
89
+ consumer = consumer or _claim_has(claim, "consumer", "потреб")
90
+ alimony = alimony or _claim_has(claim, "alimony", "алимент")
91
+
92
+ rules = rules or load_rules()
93
+ key = CODE_TO_KEY.get(str(code).lower(), str(code).upper())
94
+ jur = rules["jurisdiction"].get(key, {})
95
+ flags = {
96
+ "unknown_rf_location": unknown_rf_location,
97
+ "immovable": immovable,
98
+ "corporate": corporate,
99
+ "bankruptcy": bankruptcy,
100
+ "ip_rights": ip_rights,
101
+ "contract_forum": contract_forum,
102
+ "arbitration_clause": arbitration_clause,
103
+ "multiple_defendants": multiple_defendants,
104
+ "foreign_element": foreign_element,
105
+ "consumer": consumer,
106
+ "alimony": alimony,
107
+ }
108
+
109
+ candidates = []
110
+ manual_review = []
111
+
112
+ general_key = {
113
+ "APK": "general_35",
114
+ "GPK": "general_28",
115
+ "KAS": "general_organ_22",
116
+ }.get(key)
117
+ if general_key and general_key in jur:
118
+ candidates.append(_candidate(jur[general_key]))
119
+
120
+ mapping = _FLAG_KEYS.get(key, {})
121
+ for flag, rkeys in mapping.items():
122
+ if not flags.get(flag):
123
+ continue
124
+ if rkeys is None:
125
+ continue
126
+ for rk in rkeys:
127
+ if rk in jur:
128
+ rule = jur[rk]
129
+ if rule.get("confidence") == "manual_review":
130
+ manual_review.append(_mr_entry(rule))
131
+ else:
132
+ cand = _candidate(rule)
133
+ if cand not in candidates:
134
+ candidates.append(cand)
135
+
136
+ if arbitration_clause:
137
+ aa = rules.get("arbitration_agreement", {}).get(str(code).lower(), {})
138
+ if aa:
139
+ manual_review.append(_mr_entry(aa))
140
+
141
+ # Claim-driven specialized rules.
142
+ cl = str(claim or "").lower()
143
+ if key == "GPK" and any(w in cl for w in ("spec", "спец", "воен", "military", "spec_military", "специализ", "военный")):
144
+ if "spec_military" in jur:
145
+ manual_review.append(_mr_entry(jur["spec_military"]))
146
+ if key == "KAS" and any(w in cl for w in ("vs_first", "верхов", "ст.21", "ст21", "первая", "first", "21", "vsrf", "вс рф", "вс_рф", "верховный")):
147
+ if "vs_first_instance_21" in jur:
148
+ rule = jur["vs_first_instance_21"]
149
+ if rule.get("confidence") == "manual_review":
150
+ manual_review.append(_mr_entry(rule))
151
+ else:
152
+ cand = _candidate(rule)
153
+ if cand not in candidates:
154
+ candidates.append(cand)
155
+ # Foreign via claim already sets flag; ensure manual_review entry present.
156
+ if foreign_element and "foreign_element" in jur and jur["foreign_element"].get("confidence") == "manual_review":
157
+ pass
158
+
159
+ # Dedup manual_review by norm.
160
+ seen = set()
161
+ deduped = []
162
+ for mr in manual_review:
163
+ if mr["norm"] in seen:
164
+ continue
165
+ seen.add(mr["norm"])
166
+ deduped.append(mr)
167
+ manual_review = deduped
168
+
169
+ warnings = [rules.get("disclaimer", "")]
170
+ return {
171
+ "code": str(code).lower(),
172
+ "claim": claim,
173
+ "candidates": candidates,
174
+ "manual_review": manual_review,
175
+ "warnings": warnings,
176
+ "disclaimer": rules.get("disclaimer", ""),
177
+ }
@@ -0,0 +1,265 @@
1
+ {
2
+ "rules_version": "1.1.0",
3
+ "legal_status": "VERIFIED_BY_LEGAL_REVIEW_20261024_ADD_CHARTERS",
4
+ "verified_source": "docs/spec/2026.10.24.LEGAL-VERIFIED-SUD-JURISDICTION-RULES.md (88a9d95); charters: the compliance file (акт W2b)",
5
+ "effective_from": "2026-10-24",
6
+ "confidence_model": [
7
+ "hard",
8
+ "soft",
9
+ "manual_review"
10
+ ],
11
+ "text_policy": "legal_text = дословный текст legal; reference_only = норма-ссылка, полный текст подтягивается runtime через npa_as_of (правило «тексты только из реестра»: тексты статей по памяти НЕ хранятся)",
12
+ "jurisdiction": {
13
+ "APK": {
14
+ "general_35": {
15
+ "norm": "ч.1 ст.35 АПК РФ",
16
+ "effect": "АС субъекта РФ, если дело не подсудно ВС РФ по ч.3 ст.34 и нет исключительной/договорной/специальной подсудности",
17
+ "confidence": "hard",
18
+ "text_policy": "reference_only"
19
+ },
20
+ "unknown_or_no_ru_loc_36_1": {
21
+ "norm": "ч.1 ст.36 АПК РФ",
22
+ "effect": "иск к ответчику без места в РФ/неизвестному месту — по месту имущества ИЛИ последнему известному месту нахождения/жительства в РФ",
23
+ "confidence": "hard",
24
+ "legal_text": "иск к ответчику, место нахождения или место жительства которого неизвестно либо который не имеет места нахождения или места жительства в РФ, может быть предъявлен в АС по месту нахождения его имущества либо по его последнему известному месту нахождения или месту жительства в РФ",
25
+ "note": "ВАС РФ не существует (2-ФКЗ от 05.02.2014) — первая инстанция высшего суда: ВС РФ, ч.3 ст.34"
26
+ },
27
+ "agree_37_2": {
28
+ "norm": "ч.2 ст.37 АПК РФ",
29
+ "effect": "договорная подсудность",
30
+ "confidence": "manual_review",
31
+ "mr_reason": "проверка действительности соглашения, объема оговорки, полномочий подписантов"
32
+ },
33
+ "exclusive_38": {
34
+ "norm": "ст.38 АПК РФ",
35
+ "effect": "исключительная: недвижимость — место объекта и др. (полный текст runtime)",
36
+ "confidence": "hard",
37
+ "text_policy": "reference_only"
38
+ },
39
+ "corporate_38_4_1": {
40
+ "norm": "ч.4.1 ст.38 АПК РФ",
41
+ "effect": "корпоративные споры — АС по адресу юрлица",
42
+ "confidence": "hard",
43
+ "note": "ст.38.1 АПК НЕ существует"
44
+ },
45
+ "bankruptcy": {
46
+ "norm": "ч.4 ст.38 АПК РФ + ст.33 ФЗ-127",
47
+ "effect": "банкротство — АС по адресу должника",
48
+ "confidence": "hard"
49
+ },
50
+ "ip_court": {
51
+ "norm": "ст.34 АПК РФ + ст.43.4 ФКЗ-1",
52
+ "effect": "интеллектуальные права — Суд по интеллектуальным правам (СИП)",
53
+ "confidence": "hard"
54
+ },
55
+ "multiple_defendants_36_2": {
56
+ "norm": "ч.2 ст.36 АПК РФ",
57
+ "effect": "несколько ответчиков с разной территориальной подсудностью — выбор истца",
58
+ "confidence": "manual_review",
59
+ "mr_reason": "риск искусственного изменения подсудности"
60
+ },
61
+ "foreign_element": {
62
+ "norm": "раздел V АПК РФ",
63
+ "effect": "иностранные лица/трансграничная",
64
+ "confidence": "manual_review"
65
+ }
66
+ },
67
+ "GPK": {
68
+ "general_28": {
69
+ "norm": "ст.28 ГПК РФ",
70
+ "effect": "иск к физлицу — по месту жительства ответчика",
71
+ "confidence": "hard"
72
+ },
73
+ "plaintiff_choice_29": {
74
+ "norm": "ст.29 ГПК РФ",
75
+ "effect": "ч.1 неизвестное место жительства: по имуществу/последнему месту; ч.3 алименты: по месту жительства истца; ч.7 зоЗПП: место жительства/пребывания истца или заключения/исполнения договора; прочие части — runtime полный текст",
76
+ "confidence": "hard",
77
+ "parts_confirmed": [
78
+ "1",
79
+ "3",
80
+ "7"
81
+ ]
82
+ },
83
+ "exclusive_30": {
84
+ "norm": "ст.30 ГПК РФ",
85
+ "effect": "ч.1 недвижимость — место нахождения имущества; ч.3 перевозчики — место нахождения перевозчика",
86
+ "confidence": "hard",
87
+ "parts_confirmed": [
88
+ "1",
89
+ "3"
90
+ ]
91
+ },
92
+ "agree_32": {
93
+ "norm": "ст.32 ГПК РФ",
94
+ "effect": "договорная подсудность",
95
+ "confidence": "manual_review",
96
+ "mr_reason": "действительность соглашения"
97
+ },
98
+ "multiple_31_1": {
99
+ "norm": "ч.1 ст.31 ГПК РФ",
100
+ "effect": "несколько ответчиков — выбор истца",
101
+ "confidence": "manual_review"
102
+ },
103
+ "spec_military": {
104
+ "norm": "ст.25-27 ГПК РФ, ФКЗ от 23.06.1999 №1-ФКЗ",
105
+ "effect": "специализированные/военные суды — субъектный состав",
106
+ "confidence": "manual_review"
107
+ },
108
+ "foreign_element": {
109
+ "norm": "подраздел V ГПК РФ",
110
+ "effect": "иностранные лица/трансграничная",
111
+ "confidence": "manual_review"
112
+ }
113
+ },
114
+ "KAS": {
115
+ "general_organ_22": {
116
+ "norm": "ст.22 КАС РФ",
117
+ "effect": "административный иск к органу — по месту нахождения органа",
118
+ "confidence": "hard",
119
+ "note": "НЕ ст.24 КАС (это по выбору истца)"
120
+ },
121
+ "choice_24": {
122
+ "norm": "ст.24 КАС РФ",
123
+ "effect": "подсудность по выбору административного истца",
124
+ "confidence": "hard",
125
+ "text_policy": "reference_only"
126
+ },
127
+ "vs_first_instance_21": {
128
+ "norm": "ст.21 КАС РФ",
129
+ "effect": "ВС РФ первой инстанции — точный перечень runtime",
130
+ "confidence": "hard",
131
+ "text_policy": "reference_only"
132
+ }
133
+ }
134
+ },
135
+ "arbitration_agreement": {
136
+ "apk": {
137
+ "norm": "п.5 ч.1 ст.148 АПК РФ",
138
+ "effect": "оставление без рассмотрения при действительной арбитражной оговорке",
139
+ "confidence": "manual_review",
140
+ "mr_reason": "автономность/действительность/исполнимость соглашения"
141
+ },
142
+ "gpk": {
143
+ "norm": "абз.6 ст.222 ГПК РФ",
144
+ "effect": "аналог ГПК",
145
+ "confidence": "manual_review"
146
+ }
147
+ },
148
+ "pretrial": {
149
+ "APK_30d": {
150
+ "norm": "ч.5 ст.4 АПК РФ",
151
+ "days": 30,
152
+ "calendar": true,
153
+ "scope": "договоры, иные сделки, неосновательное обогащение",
154
+ "trigger": "со дня направления претензии",
155
+ "confidence": "hard"
156
+ },
157
+ "ZOZPP_10d": {
158
+ "norm": "ст.22 + ч.1 ст.31 Закона РФ №2300-1",
159
+ "days": 10,
160
+ "calendar": true,
161
+ "scope": "отдельные требования потребителя",
162
+ "confidence": "hard"
163
+ },
164
+ "OSAGO_30d": {
165
+ "norm": "ст.16.1 ФЗ-40 (п.1)",
166
+ "days": 30,
167
+ "calendar": true,
168
+ "exclude_nonworking_holidays": true,
169
+ "note": "НЕ ст.16",
170
+ "confidence": "hard"
171
+ },
172
+ "charters_transport": {
173
+ "railway_ujt": {
174
+ "act": "Устав железнодорожного транспорта РФ (ФЗ от 10.01.2003 № 18-ФЗ)",
175
+ "norms": "ст. 120, 122, 124 УЖТ РФ",
176
+ "days": 30,
177
+ "calendar": true,
178
+ "scope": "претензии к перевозчику при ж/д перевозках грузов (срок рассмотрения — 30 дней со дня получения)",
179
+ "confidence": "hard"
180
+ },
181
+ "road_uat": {
182
+ "act": "Устав автомобильного транспорта и городского наземного электрического транспорта (ФЗ от 08.11.2007 № 259-ФЗ)",
183
+ "norms": "ст. 39, 40 УАТ РФ",
184
+ "days": 30,
185
+ "calendar": true,
186
+ "scope": "претензии к перевозчикам/фрахтовщикам при автоперевозках (рассмотрение в течение 30 дней со дня получения)",
187
+ "confidence": "hard"
188
+ },
189
+ "maritime_ktm": {
190
+ "act": "Кодекс торгового мореплавания РФ (ФЗ от 30.04.1999 № 81-ФЗ)",
191
+ "norms": "ст. 403, 406, 407 КТМ РФ",
192
+ "days": 30,
193
+ "calendar": true,
194
+ "scope": "претензии к перевозчику, вытекающие из договора морской перевозки груза (рассмотрение в 30-дневный срок; право на иск при неполучении ответа или отклонении)",
195
+ "confidence": "hard"
196
+ },
197
+ "inland_water_kvvt": {
198
+ "act": "Кодекс внутреннего водного транспорта РФ (ФЗ от 07.03.2001 № 24-ФЗ)",
199
+ "norms": "ст. 161, 162, 163 КВВТ РФ",
200
+ "days": 30,
201
+ "calendar": true,
202
+ "scope": "претензии по перевозкам внутренним водным транспортом (срок рассмотрения — 30 дней со дня получения)",
203
+ "confidence": "hard"
204
+ },
205
+ "air_code_vk": {
206
+ "act": "Воздушный кодекс РФ (ФЗ от 19.03.1997 № 60-ФЗ)",
207
+ "norms": "ст. 124, 126, 127, 128 ВК РФ",
208
+ "days": 30,
209
+ "calendar": true,
210
+ "scope": "претензии к внутреннему воздушному перевозчику при перевозке груза/почты (срок рассмотрения — 30 дней)",
211
+ "confidence": "hard"
212
+ },
213
+ "freight_forwarding": {
214
+ "act": "ФЗ от 30.06.2003 № 87-ФЗ «О транспортно-экспедиционной деятельности»",
215
+ "norms": "ст. 12, 13 ФЗ-87",
216
+ "days": 30,
217
+ "calendar": true,
218
+ "scope": "претензии к экспедитору по договору транспортной экспедиции (срок рассмотрения — 30 дней со дня получения)",
219
+ "confidence": "hard"
220
+ },
221
+ "postal": {
222
+ "act": "ФЗ от 17.07.1999 № 176-ФЗ «О почтовой связи»",
223
+ "norms": "ст. 37, 38 ФЗ-176",
224
+ "days": 30,
225
+ "calendar": true,
226
+ "scope": "претензии к операторам почтовой связи (рассмотрение в течение 30 дней; для претензий в пределах одного населенного пункта — 5 дней)",
227
+ "confidence": "manual_review",
228
+ "mr_reason": "дифференциация срока: 5 дней для одного населенного пункта, 30 дней для межгорода"
229
+ }
230
+ }
231
+ },
232
+ "consequences_noncompliance": {
233
+ "return_at_filing": {
234
+ "APK": "п.5 ч.1 ст.129 АПК РФ",
235
+ "GPK": "п.1 ч.1 ст.135 ГПК РФ",
236
+ "KAS": "п.1 ч.1 ст.129 КАС РФ",
237
+ "confidence": "hard"
238
+ },
239
+ "left_without_consideration": {
240
+ "APK": "п.2 ч.1 ст.148 АПК РФ",
241
+ "GPK": "абз.2 ст.222 ГПК РФ",
242
+ "KAS": "п.1 ч.1 ст.196 КАС РФ",
243
+ "confidence": "hard"
244
+ },
245
+ "left_without_movement": {
246
+ "APK": {
247
+ "norm": "ч.1 ст.128 АПК РФ",
248
+ "basis": "нарушение п.8 ч.2 ст.125 и п.7 ч.1 ст.126 АПК РФ"
249
+ },
250
+ "GPK": {
251
+ "norm": "ч.1 ст.136 ГПК РФ",
252
+ "basis": "нарушение п.7 ч.2 ст.131 и п.3 ст.132 ГПК РФ"
253
+ },
254
+ "confidence": "hard"
255
+ }
256
+ },
257
+ "manual_review_registry": [
258
+ "договорная подсудность (ст.37 АПК, ст.32 ГПК)",
259
+ "третейская/арбитражная оговорка (п.5 ч.1 ст.148 АПК, абз.6 ст.222 ГПК)",
260
+ "множественность ответчиков с разной территориальной подсудностью (ч.2 ст.36 АПК, ч.1 ст.31 ГПК)",
261
+ "специализированные и военные суды (ст.25-27 ГПК, ФКЗ №1-ФКЗ)",
262
+ "иностранный элемент/трансграничная (раздел V АПК, подраздел V ГПК)"
263
+ ],
264
+ "disclaimer": "Правила — процессуальная навигация, не юридический совет (boundary IX-3). Тексты норм без legal_text подтягиваются runtime через npa_as_of с фиксацией редакции и даты; вывод вне hard-правил = manual_review с нормой-ссылкой."
265
+ }