feat: central RTC write modes (DISABLED/DRY_RUN/APPROVAL/ENABLED), R4 suggestion-only, preview/approval, per-record rollback, bulk blocker upsert; 1.2.0

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gti
2026-09-24 22:46:05 -03:00
co-authored by Claude Sonnet 5
parent 3c10422e81
commit 42dc172763
50 changed files with 2948 additions and 915 deletions
+34 -10
View File
@@ -44,7 +44,7 @@ def test_rtc_off_is_not_applicable_and_never_a_candidate():
def test_simples_is_insufficient_until_the_official_orientation_exists():
r = resolve("10063021", company=SIMPLES)
assert r.status == "INSUFFICIENT_EVIDENCE" and r.rule_id == "RTC-R1" and not r.auto_apply_candidate and r.source_references[-1]["document"].startswith("NT 2025.002")
assert r.status == "INSUFFICIENT_EVIDENCE" and r.rule_id == "RTC-R1" and not r.auto_apply_candidate and r.source_references[-1]["document"].startswith("Ato Conjunto RFB/CGIBS") and r.rule_class == "BLOCKED_BY_EVIDENCE"
assert resolve("10063021", company={"crt": None}).reason == "COMPANY_REGIME_UNKNOWN"
@@ -88,35 +88,59 @@ def test_nature_based_chapters_are_ambiguous():
assert r.status == "AMBIGUOUS" and r.rule_id in ("RTC-A3", "RTC-A1", "RTC-A2"), ncm
def test_residual_ncm_outside_every_annex_and_sensitive_chapter_is_integral_taxation():
def test_residual_ncm_outside_every_annex_is_only_a_suggestion_and_never_a_write_candidate():
r = resolve("39235000") # plastic closures: no annex, no nature-based regime
assert r.status == "RESOLVED_WITH_EXPLICIT_RULE" and r.resolved_cclasstrib == "000001" and r.resolved_cst_ibscbs == "000" and r.rule_id == "RTC-R4" and r.confidence_type == "EXCLUSION_OF_ALL_SPECIAL_REGIMES"
assert r.auto_apply_candidate and annex_matches("39235000") == []
assert r.status == "SUGGESTION_ONLY" and r.resolved_cclasstrib == "000001" and r.suggested_cclasstrib == "000001" and r.rule_id == "RTC-R4" and r.rule_class == "SUGGESTION_ONLY"
assert r.auto_apply_candidate is False and annex_matches("39235000") == [] and r.confidence_type == "EXCLUSION_OF_ALL_SPECIAL_REGIMES"
assert "not positive evidence" in r.evidence[0]["note"] and r.as_dict()["auto_apply_candidate"] is False
def test_only_auto_apply_safe_rules_can_authorize_a_write():
from central.app.cclasstrib_resolver import RULE_CLASSES
assert [rid for rid, cls in RULE_CLASSES.items() if cls == "AUTO_APPLY_SAFE"] == ["RTC-R3"]
assert RULE_CLASSES["RTC-R4"] == "SUGGESTION_ONLY" and RULE_CLASSES["RTC-R0"] == "VALIDATION_ONLY"
assert {c for c in RULE_CLASSES.values()} == {"AUTO_APPLY_SAFE", "VALIDATION_ONLY", "SUGGESTION_ONLY", "BLOCKED_BY_EVIDENCE"}
def test_regime_other_than_normal_never_reaches_a_positive_rule():
for company in ({"crt": "2"}, {"crt": "4"}, {"crt": "9"}, {"crt": ""}):
assert not resolve("10063021", company=company).auto_apply_candidate
def test_a_class_missing_or_different_in_the_local_catalog_is_never_proposed():
r = resolve("39235000", local=local_from_official(skip=("000001",)))
r = resolve("10063021", local=local_from_official(skip=("200003",)))
assert r.status == "INSUFFICIENT_EVIDENCE" and r.reason == "CLASS_NOT_IN_LOCAL_CATALOG" and not r.auto_apply_candidate
r2 = resolve("39235000", local=local_from_official(tweak={"000001": {"cst": "010"}}))
r2 = resolve("10063021", local=local_from_official(tweak={"200003": {"cst": "010"}}))
assert r2.reason == "LOCAL_OFFICIAL_MISMATCH" and not r2.auto_apply_candidate
r3 = resolve("10063021", local=local_from_official(tweak={"200003": {"ind_nfe": False, "ind_nfce": False}}))
assert r3.reason == "LOCAL_OFFICIAL_MISMATCH"
def test_invalid_local_catalog_blocks_every_proposal():
assert resolve("39235000", ctx={"rtc_enabled": True, "local_catalog_valid": False}).reason == "LOCAL_RTC_CATALOG_INVALID"
assert resolve("10063021", ctx={"rtc_enabled": True, "local_catalog_valid": False}).reason == "LOCAL_RTC_CATALOG_INVALID"
def test_catalog_comparison_classifies_every_code_and_flags_material_problems():
full = compare_catalogs(local_from_official(), TODAY)
assert full["counts"]["MATCH"] == 161 and full["counts"]["EXPIRED"] == 3 and full["compatibility"] == "PARTIAL_LOCAL_OUTDATED" # 220001-3 ended on 2026-01-01
assert full["counts"]["MATCH_SAFE"] == 161 and full["counts"]["EXPIRED"] == 3 and full["compatibility"] == "PARTIAL_LOCAL_OUTDATED" # 220001-3 ended on 2026-01-01
assert compare_catalogs(local_from_official(skip=("220001", "220002", "220003")), TODAY)["compatibility"] == "FULL"
outdated = compare_catalogs(local_from_official(skip=("000005", "200053")), TODAY)
assert outdated["counts"]["OFFICIAL_ONLY"] == 2 and outdated["compatibility"] == "PARTIAL_LOCAL_OUTDATED" and outdated["counts"]["MISMATCH"] == 0
assert outdated["counts"]["MISSING_LOCAL"] == 2 and outdated["compatibility"] == "PARTIAL_LOCAL_OUTDATED" and outdated["counts"]["MISMATCH"] == 0
local = local_from_official(tweak={"200003": {"p_red_ibs": "60"}})
local["999999"] = {"cst": "000", "p_red_ibs": "0", "p_red_cbs": "0", "ind_nfe": True, "ind_nfce": True}
bad = compare_catalogs(local, TODAY)
assert bad["per_code"]["200003"] == "MISMATCH" and bad["per_code"]["999999"] == "LOCAL_ONLY" and bad["compatibility"] == "MATERIAL_MISMATCH"
assert bad["per_code"]["200003"] == "MISMATCH" and bad["per_code"]["999999"] == "MISSING_OFFICIAL" and bad["compatibility"] == "MATERIAL_MISMATCH"
def test_non_material_differences_are_classified_but_not_treated_as_a_mismatch():
earlier = compare_catalogs(local_from_official(tweak={"200003": {"start": "2025-05-05"}}), TODAY) # SGV imports an earlier start: both in force today => same emission effect
assert earlier["per_code"]["200003"] == "MATCH_SAFE" and earlier["details"]["200003"] == ["VIGENCY_START_DIFFERS_BOTH_IN_FORCE"]
assert compare_catalogs(local_from_official(tweak={"200003": {"start": "2027-01-01"}}), TODAY)["per_code"]["200003"] == "MISMATCH"
assert compare_catalogs(local_from_official(tweak={"200003": {"end": "2026-01-01"}}), TODAY)["per_code"]["200003"] == "MISMATCH"
local = local_from_official(tweak={"200003": {"end": "2027-12-31"}}) # a scheduled local end the official table does not have
c = compare_catalogs(local, TODAY)
assert c["per_code"]["200003"] == "MATCH_WITH_NON_MATERIAL_DIFFERENCE" and c["counts"]["MISMATCH"] == 0
assert resolve("10063021", local=local).status == "INSUFFICIENT_EVIDENCE" # auto-apply requires MATCH_SAFE: a non-material difference still blocks the WRITE
def test_local_catalog_hash_is_stable_and_sensitive():